doctor
bunx @pivanov/agent-hooks-bridge doctor [<script>] [options]Read-only health check across .claude/settings.json, .cursor/hooks.json, and .codex/hooks.toml. Reports which entries point at hook scripts, whether the scripts resolve, and whether each entry is wired correctly. Never writes.
Usage
bash
# inspect every managed-looking entry across all three hosts
bunx @pivanov/agent-hooks-bridge doctor
# only inspect entries that point at one specific script
bunx @pivanov/agent-hooks-bridge doctor ./.hooks/format.ts
# limit to one host
bunx @pivanov/agent-hooks-bridge doctor --hosts claude
# only check hosts that look installed on this machine
bunx @pivanov/agent-hooks-bridge doctor --hosts auto
# inspect the user-level (global) config
bunx @pivanov/agent-hooks-bridge doctor --global
# inspect a custom layout
bunx @pivanov/agent-hooks-bridge doctor --cwd ~/.claude-main --config-root --hosts claudeOptions
| Flag | Default | Notes |
|---|---|---|
<script> (positional) | unset | If provided, doctor only inspects entries whose command is <script> or starts with <script> . |
--hosts <list> | claude,cursor,codex | CSV of host ids, or auto to inspect only hosts that look installed on this machine. |
--cwd <path> | process.cwd() | Resolve <cwd>/.claude/, <cwd>/.cursor/, <cwd>/.codex/. |
--global, -g | off | Inspect each host's user-level config (~/.claude/settings.json, ~/.cursor/hooks.json, $CODEX_HOME/hooks.toml). Overrides --cwd and --config-root. |
--config-root | off | Treat <cwd> as the host's config dir directly. |
--help, -h | Print usage. |
What it checks
For every managed-looking entry (a command that starts with ./, ../, /, or ~/, or matches the <script> filter):
- The script file at the resolved path exists.
- The file starts with a shebang line (
#!); without one, hosts can't spawn it directly. - The executable bit is set (
chmod +x). - The wired
commandincludes--host <hostId>for the right host. Without this,run()falls back to stdin scoring (slower; see Host Detection).
Exit codes
| Outcome | Exit |
|---|---|
| All checks pass for every host inspected | 0 |
| Any error-level issue found | 1 |
| Argv parse failure | 1 |
The --host mismatch is a warning (?) rather than an error (!) and does not affect the exit code.
Sample output
text
# agent-hooks-bridge doctor
# cwd: /Users/x/proj
# hosts: claude, cursor, codex
# claude → /Users/x/proj/.claude/settings.json
+ 5 managed entry(ies)
SessionStart: ./.hooks/format.ts --host claude
PreToolUse (matcher=*): ./.hooks/format.ts --host claude
PostToolUse (matcher=*): ./.hooks/format.ts --host claude
UserPromptSubmit: ./.hooks/format.ts --host claude
Stop: ./.hooks/format.ts --host claude
# cursor → /Users/x/proj/.cursor/hooks.json
- file not found (skip)
# codex → /Users/x/proj/.codex/hooks.toml
+ 5 managed entry(ies)
...
! /Users/x/proj/.hooks/format.ts is not marked executable (chmod +x)
1 issue(s) foundProgrammatic API
ts
import { runDoctor } from "@pivanov/agent-hooks-bridge";
const exitCode = await runDoctor({ argv: [] });For tests, you can inject a virtual filesystem and a scriptCheck mock:
ts
import { runDoctor, type IScriptChecker } from "@pivanov/agent-hooks-bridge";
const fakeChecker: IScriptChecker = {
check: () => ({ exists: true, hasShebang: true, executable: true }),
};
await runDoctor({
argv: ["--cwd", "/proj"],
fs: virtualFs,
scriptCheck: fakeChecker,
log: (line) => collectedOutput.push(line),
});Out of scope
- Doctor does not parse
.codex/hooks.tomloutside the managed block. Hand-rolled[[hooks]]entries are ignored. - Doctor does not run the script. It checks shape, not behavior.
- Doctor does not validate that
--host <id>matches a real adapter; only that it is wired in for the host whose config it appears in.