Skip to content

fix(agent-claude-code): use $CLAUDE_PROJECT_DIR for hook commands (fixes #2090)#2091

Open
vikt0r0 wants to merge 1 commit into
AgentWrapper:mainfrom
vikt0r0:fix/hook-command-claude-project-dir
Open

fix(agent-claude-code): use $CLAUDE_PROJECT_DIR for hook commands (fixes #2090)#2091
vikt0r0 wants to merge 1 commit into
AgentWrapper:mainfrom
vikt0r0:fix/hook-command-claude-project-dir

Conversation

@vikt0r0
Copy link
Copy Markdown

@vikt0r0 vikt0r0 commented Jun 3, 2026

Problem

The Claude Code agent plugin registers its activity/metadata lifecycle hooks with relative command paths (.claude/activity-updater.sh, .claude/metadata-updater.sh) for every hook event, including PreToolUse/PostToolUse/SubagentStart/SubagentStop.

When a hook fires from a sub-agent (Agent/Task tool) call, the process working directory is not the worktree root, so the shell can't resolve the relative path:

PreToolUse:Agent hook error  ⎿ Failed with non-blocking status code: /bin/sh: 1: .claude/activity-updater.sh: not found
PostToolUse:Read hook error  ⎿ Failed with non-blocking status code: /bin/sh: 1: .claude/activity-updater.sh: not found

It's non-blocking, but it (a) spams the agent transcript while sub-agents run and (b) drops the .ao/activity.jsonl signal during sub-agent work, which can cause false stuck / probe_failure lifecycle detection on otherwise-healthy sessions. At the top-level agent cwd (worktree root) the relative path resolves fine — which is why it only shows up once the agent dispatches parallel Explore/Task sub-agents.

Fix

Prefix the hook commands with $CLAUDE_PROJECT_DIR, which Claude Code exports for hook execution and which always points at the worktree root:

$CLAUDE_PROJECT_DIR/.claude/activity-updater.sh
node $CLAUDE_PROJECT_DIR/.claude/activity-updater.cjs   # windows branch

This resolves correctly regardless of the hook's cwd, while keeping settings.json content identical across worktrees (no hardcoded absolute path — preserving the existing "different worktree paths produce identical settings.json" guarantee). The dedup/upsert logic keys off the bare filenames (activity-updater.sh, …), so existing settings migrate cleanly.

Applied to both the unix (.sh) and windows (.cjs) branches.

Tests

  • Updated index.test.ts assertions to the new $CLAUDE_PROJECT_DIR contract (including the "updates an existing absolute hook path" migration test).
  • pnpm --filter @aoagents/ao-plugin-agent-claude-code typecheck
  • pnpm --filter @aoagents/ao-plugin-agent-claude-code test270 passing. The one failing test (activity-updater.integration.test.ts, a special-character escaping round-trip) also fails on pristine main in this environment (/bin/sh escaping), and is unrelated to this change — it touches neither the hook command paths nor the updater scripts.

Fixes #2090

The activity/metadata lifecycle hooks were registered with relative command
paths (.claude/activity-updater.sh, .claude/metadata-updater.sh) for every hook
event, including PreToolUse/PostToolUse/SubagentStart/SubagentStop. When a hook
fires from a sub-agent (Agent/Task) tool call, the process cwd is not the
worktree root, so the shell cannot resolve the relative path:

  /bin/sh: 1: .claude/activity-updater.sh: not found

This is non-blocking but spams the transcript and drops the .ao/activity.jsonl
signal during sub-agent runs, which can cause false stuck/probe_failure
detection on healthy sessions.

Prefix the commands with $CLAUDE_PROJECT_DIR (exported by Claude Code for hook
execution and always pointing at the worktree root). This keeps settings.json
content identical across worktrees while resolving correctly regardless of the
hook's cwd. Applied to both the unix (.sh) and windows (.cjs) branches; tests
updated to the new contract.

Fixes AgentWrapper#2090

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

Lifecycle hook commands use relative paths → .claude/activity-updater.sh: not found during sub-agent (Agent/Task) tool calls

1 participant