Skip to content

Hook commands use relative paths, break when CWD != project root #1259

@bullsheedo

Description

@bullsheedo

When @claude-flow/cli generates .claude/settings.json hook configuration, all hook commands use relative paths like:

"command": "node .claude/helpers/auto-memory-hook.mjs sync"

These paths resolve relative to process.cwd(), not relative to the .claude/settings.json file location. When Claude Code is launched from a subdirectory (e.g., apps/api in a monorepo), Node cannot find the
helper files and the hooks crash with MODULE_NOT_FOUND.

Steps to Reproduce

  1. Initialize claude-flow in a monorepo root: npx @claude-flow/cli@latest init
  2. Launch Claude Code from a subdirectory: cd apps/api && claude
  3. Use Claude Code normally, then stop the session (Ctrl+C)
  4. Observe the Stop hook error

Error

Stop hook error: Failed with non-blocking status code: node:internal/modules/cjs/loader:1423
throw err;
^
Error: Cannot find module 'C:\projects\myproject\apps\api.claude\helpers\auto-memory-hook.mjs'
at Module._resolveFilename (node:internal/modules/cjs/loader:1420:15)
...
code: 'MODULE_NOT_FOUND',
requireStack: []

Affected Hooks

All hooks in the generated settings.json are affected:

  • SessionStart — auto-memory-hook.mjs import, hook-handler.cjs session-restore
  • SessionEnd — hook-handler.cjs session-end
  • Stop — auto-memory-hook.mjs sync
  • PreToolUse — hook-handler.cjs pre-bash
  • PostToolUse — hook-handler.cjs post-edit
  • UserPromptSubmit — hook-handler.cjs route
  • PreCompact — hook-handler.cjs compact-manual, compact-auto
  • SubagentStart — hook-handler.cjs status
  • statusLine — statusline.cjs

Root Cause

The hook generator writes relative paths assuming process.cwd() will always be the project root where .claude/settings.json lives. Claude Code may set the CWD to the directory the user launched from, which can
be any subdirectory in a monorepo.

Note: hook-handler.cjs internally uses __dirname (line 19) to resolve sibling files, so it works once Node can find it. The problem is Node never finds it because the path in settings.json is relative to CWD.

Workaround

Manually prefix all hook commands with cd /path/to/project/root &&:

"command": "cd /path/to/project/root && node .claude/helpers/auto-memory-hook.mjs sync"

Suggested Fix

Option A — Use absolute paths when generating hooks:

"command": "node /absolute/path/to/.claude/helpers/auto-memory-hook.mjs sync"

Option B — Resolve project root at runtime (walk up to find .claude/settings.json or use git):

"command": "cd $(git rev-parse --show-toplevel) && node .claude/helpers/auto-memory-hook.mjs sync"

Option C — Use __dirname-relative paths inside a wrapper that settings.json invokes via absolute path.

Environment

  • @claude-flow/cli: 3.5.2
  • OS: Windows 11 (also reproducible on any OS with monorepo subdirectory CWD)
  • Node: v25.2.1
  • Claude Code: latest

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions