fix(agent): include Claude Code hook fields in hooks stdin payload (#121)#122
Conversation
) Project hooks fired at pre/post-tool boundaries previously emitted only TENEX-specific fields (event, tool, args, result). Claude Code-compatible tools such as proactive-context expect session_id, cwd, transcript_path, and prompt; without them they received malformed input and produced no output. Extend ProjectHooksRunner with session_id (the conversation id) and prompt (the conversation's root user message, stable across re-engagements), and emit them — plus cwd from the runner's working_dir and a null transcript_path (TENEX stores transcripts in SQLite, not JSONL) — in both the pre-tool and post-tool stdin payloads. The original_task derivation in agent_bootstrap is hoisted above the hook runner construction so a single source of truth feeds both the hook prompt and the AgentBootstrap field. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Code ReviewOverall: Clean, well-scoped fix that correctly addresses the root cause. The minimal footprint and thorough test assertions are good. A few things worth addressing: CLAUDE.md violations — doc comment lengthCLAUDE.md mandates one short line max for comments, and only when the WHY is non-obvious. Several new doc blocks cross that line:
/// Build the `pre-tool` stdin payload. `args_json` is the tool's argument
/// string; it is embedded as parsed JSON when valid, else as a JSON string.
/// The `session_id`, `cwd`, `transcript_path`, and `prompt` fields make the
/// payload Claude Code-compatible; `transcript_path` is always `null`
/// because TENEX persists transcripts in SQLite, not a JSONL file.
fn pre_tool_stdin(...)The only non-obvious WHY here is the
/// The conversation's root user message, surfaced to hooks as the Claude
/// Code `prompt`. Stable across re-engagements within the conversation.
One line suffices. Test comment in // Claude Code-compatible fields must be present so tools like
// proactive-context can read them. `transcript_path` is always null
// because TENEX stores transcripts in SQLite, not a JSONL file.Two lines, and the
|
Summary
Closes #121. Project hooks fired at pre/post-tool boundaries previously emitted only TENEX-specific fields (
event,tool,args,result). Claude Code-compatible tools such asproactive-contextexpectsession_id,cwd,transcript_path, andprompt— without them they received malformed input and produced no output.Changes
crates/tenex-agent/src/project_hooks.rsProjectHooksRunnergainssession_id(the conversation id) andprompt(the conversation's root user message) fields;::newtakes both.session_id,cwd(from the runner'sworking_dir),transcript_path(alwaysnull— TENEX stores transcripts in SQLite, not JSONL), andprompt.resultremains post-tool only.crates/tenex-agent/src/agent_bootstrap/stages.rs—load_project_hooksthreadssession_idandpromptintoProjectHooksRunner::new.crates/tenex-agent/src/agent_bootstrap/mod.rs— theoriginal_taskderivation is hoisted above the hook runner construction so a single source of truth feeds both the hookpromptand theAgentBootstrapfield.conversation_idsuppliessession_id.Validation
cargo build -p tenex-agent— cleancargo test -p tenex-agent— 58 + 12 hook tests pass; thehook_receives_parsed_args_on_stdintest now assertssession_id,cwd,transcript_path(null), andprompt.proactive-context awareness --hook PostToolUsereturns a non-emptyhookSpecificOutputwhen fed the new payload shape.🤖 Generated with Claude Code