[FEATURE] Run PostToolUse hooks when a mod's tool.call hook supplies the result

Status Open
Reported on v2.1.299
Maintainer reply None cached
Activity 0 comments · opened Oct 14, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

Claude Code 2.1.299, mods / function hooks. A hooks module can answer tool.call with its own { result } (no next(e)), which replaces the tool's execution. For such a call Claude Code fires no PostToolUse hooks at all: settings hooks and plugin command hooks matching the tool never run. The same call routed through next(e) runs them normally.

So a mod cannot serve a tool result (e.g. a faster Grep from a code-search plugin's index) without silently breaking every other hook on that tool: audit logs, memory/telemetry collectors, linters, chat bridges, policy hooks. Hooks used as policy or telemetry should hold regardless of what produced the result, a core run or a hook. A mod also cannot work around it: there is no documented way to dispatch PostToolUse itself or to list which settings/plugin hooks match a tool ($.classic.* dispatch exists only in the test kit).

Minimal repro (isolated: claude -p --setting-sources project --settings settings.json --plugin-dir ./mod):

// mod hooks module
on('tool.call', { tool: 'Grep' }, () => ({
  result: { mode: 'content', content: 'a.go:1:x', numFiles: 1, filenames: ['a.go'], numLines: 1 },
}));
// settings.json
{ "hooks": { "PostToolUse": [ { "matcher": "Grep",
  "hooks": [ { "type": "command", "command": "node -e \"require('fs').appendFileSync('post.log','post\\n')\"" } ] } ] } }
  • Observed: model calls Grep, gets the supplied result; post.log is never written.
  • Expected: post.log gets a line, as it does when the hook returns next(e) instead.

Proposed Solution

For a tool.call answered by a hook-supplied result, run the same post-call pipeline as for a core-run call: PostToolUse (or PostToolUseFailure) with the supplied result as tool_response, from settings, plugin and module hooks, including updatedToolOutput / decision / additionalContext handling. Ideally also keep PreToolUse and the permission check ahead of tool.call hooks, so a mod's result cannot bypass them either.

Alternative Solutions

If that is not wanted by default: expose a documented API for a mod to do it itself, e.g. $.hooks.run('PostToolUse', envelope, result) (and PreToolUse), or at least a way to list the hooks that match a tool, so a mod can decide to fall through to next(e) when other hooks would be skipped. Today the only safe option is to never supply a result.

Priority

Medium - Would be very helpful

Feature Category

Developer tools/SDK

Use Case Example

  1. A code-search plugin answers Grep from its index in ~100 ms instead of running ripgrep (300-700 ms).
  2. The user also has a PostToolUse hook on * that bridges tool activity to another agent, and a memory hook that records every search.
  3. With a supplied result both hooks miss every served Grep, so the plugin has to turn serving off.
  4. With PostToolUse run for supplied results, the plugin can serve and every other hook keeps its semantics.

Additional Context

Related, not the same: #96831 and #100869 (classic.* events not dispatched to hook modules), #101073 (ordering of multiple hooks' updatedInput / updatedToolOutput).

View original on GitHub ↗