feat(hooks): surface PostToolUse hook stderr to LLM context#2445
Conversation
PostToolUse hooks were fire-and-forget: their stdout/stderr were captured but discarded. This made it impossible for hooks like docref to report drift back to the LLM so it could act on the information. Now PostToolUse hooks are awaited (not fire-and-forget) and any stderr they produce is appended to the tool result's .message field, which the LLM sees in the next turn's context. This enables hooks to act as reporters rather than actors — they can detect state changes and feed structured observations to the LLM, which then decides what tools to call next.
There was a problem hiding this comment.
🚩 HookEngine has a dedicated fire_and_forget_trigger helper that was not used here
The HookEngine class at src/kimi_cli/hooks/engine.py:93-115 has a fire_and_forget_trigger() method specifically designed to keep strong references to background hook tasks (preventing GC collection). The old code manually created tasks with asyncio.create_task() and added done callbacks, which was the pattern this helper replaced. The new code doesn't need this helper since it awaits the result directly, but it's worth noting the old code didn't use this helper either — it predates it. The PostToolUseFailure hook at line 402-418 still uses the old manual pattern rather than fire_and_forget_trigger().
(Refers to lines 402-418)
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
It's pre-existing technical debt, not something this PR introduced.
This change converts the PostToolUse hook from fire-and-forget to awaited, allowing hook stderr to be collected and appended to the tool result message. This gives the LLM visibility into hook output immediately after a tool call.
Changes
asyncio.create_taskfire-and-forget withawait hook_engine.trigger()ret.messagewith a[post-tool-use-hooks]headerMotivation
Currently PostToolUse hooks run in the background and their output is discarded. This makes it impossible for hooks that emit diagnostic or advisory output to feed that information back to the LLM. For example, a PostToolUse hook that validates file changes, checks for stale documentation references, or runs linting can now surface its findings directly in the tool result message that the LLM receives.
Potential use cases
In all cases, the LLM sees the hook output in the same turn and can act on it (e.g., fix docs, reformat code) without requiring an extra user round-trip.
Backwards compatibility