← Back to PRs

#17667: feat: tool-hooks extension — run shell commands on tool calls

by FaradayHunt open 2026-02-16 02:23 View on GitHub →
stale size: M
## Summary Adds a new `tool-hooks` extension that lets users run shell commands when specific agent tools are called. Hooks fire on `after_tool_call` and/or `before_tool_call` events, receiving full context via environment variables. ## Motivation There is currently no way for users to trigger side effects when tools are called — for example: - **Knowledge graph tracking**: automatically update a memory graph when `memory_search` is used, strengthening connections through actual recall - **Audit logging**: record tool usage patterns to a file or external service - **Webhooks**: trigger external systems when specific tools fire - **Analytics**: track which tools are used most, by which agents, in which sessions The hook system (`after_tool_call` / `before_tool_call`) already exists in the plugin infrastructure, but there is no built-in way to use it without writing a full TypeScript extension. This extension bridges that gap with simple config-driven shell commands. ## Configuration ```json { "plugins": { "tool-hooks": { "hooks": [ { "tool": "memory_search", "command": "node ~/workspace/memory/brain.js track-query \"$TOOL_PARAMS\"", "background": true, "onlyOnSuccess": true } ] } } } ``` ## Features - **Glob patterns** for tool matching (`"memory_*"`, `"*"`) - **Environment variables**: `TOOL_NAME`, `TOOL_PARAMS`, `TOOL_RESULT`, `TOOL_DURATION_MS`, `TOOL_ERROR`, `AGENT_ID`, `SESSION_KEY` - **Background execution** (fire-and-forget, default) or blocking - **Timeout** protection (default 10s) - **Success-only** filtering (`onlyOnSuccess`) - Zero core changes — uses existing plugin hook system ## Files - `extensions/tool-hooks/index.ts` — plugin implementation - `extensions/tool-hooks/openclaw.plugin.json` — plugin manifest - `extensions/tool-hooks/package.json` — package metadata - `extensions/tool-hooks/README.md` — docs and examples <!-- greptile_comment --> <h3>Greptile Summary</h3> Adds a config-driven shell hook system for tool call events (`after_tool_call` / `before_tool_call`). Hook commands run via `sh -c` with tool context exposed as environment variables. Addresses previous review feedback: now correctly uses `api.pluginConfig` instead of `api.config`, and hooks are properly registered. **Key issue found:** - The `background` parameter is documented and accepted in config but doesn't actually affect execution — both background and non-background hooks fire-and-forget with `void`, making them functionally identical **Previous thread concerns acknowledged but not addressed:** - Shell execution bypasses OpenClaw's `validateHostEnv()` filtering (dangerous env vars like `LD_PRELOAD`, `NODE_OPTIONS` can be inherited from `process.env`) - No exec-approvals flow (user config is the consent mechanism) - README examples show `$TOOL_PARAMS` being interpolated in shell commands, which is safe via env vars but could be confusing <h3>Confidence Score: 3/5</h3> - Safe to merge with one critical logic bug that breaks documented functionality - The PR correctly implements the plugin hook system and fixes previous review feedback (`api.pluginConfig` is now used correctly). However, the `background` parameter advertised in docs and config schema doesn't work — both code paths execute identically as fire-and-forget. This breaks the documented blocking vs non-blocking behavior. Security concerns about env var inheritance and shell execution bypass were raised in previous threads but appear to be design trade-offs for a config-driven extension (user explicitly opts in). - Pay close attention to `extensions/tool-hooks/index.ts` — the background parameter logic needs to be implemented correctly <sub>Last reviewed commit: 478db58</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs