Skip to content

feat(hooks): surface PostToolUse hook stderr to LLM context#2445

Open
zwpdbh wants to merge 1 commit into
MoonshotAI:mainfrom
zwpdbh:feat/posttooluse-hook-stderr-to-llm
Open

feat(hooks): surface PostToolUse hook stderr to LLM context#2445
zwpdbh wants to merge 1 commit into
MoonshotAI:mainfrom
zwpdbh:feat/posttooluse-hook-stderr-to-llm

Conversation

@zwpdbh

@zwpdbh zwpdbh commented Jun 10, 2026

Copy link
Copy Markdown

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

  • Replace asyncio.create_task fire-and-forget with await hook_engine.trigger()
  • Collect non-empty stderr from hook results
  • Append collected stderr to ret.message with a [post-tool-use-hooks] header

Motivation

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

  • Documentation drift detection: A hook can warn when code edits have caused documentation line-number references to become stale.
  • Linting / style checks: A hook can run formatting or linting tools after file writes and report issues to the LLM.
  • Policy / compliance checks: A hook can validate that edited files meet project conventions and surface violations.

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

  • PostToolUseFailure remains fire-and-forget (no change)
  • Hook stdout and action semantics are unchanged
  • Hooks that produce no stderr behave exactly as before

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.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 2 additional findings in Devin Review.

Open in Devin Review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 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)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's pre-existing technical debt, not something this PR introduced.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant