Repository navigation
Conversation
|
Important Review skippedWe couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting You can disable this status message by setting the Use the checkbox below for a quick retry:
No actionable comments were generated in the recent review. ℹ️ Recent review info
Walkthrough
Priority: Severity of issue fixed: Medium Merge Risk:
|
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Out of Scope Changes check | Changes since the previous review include unrelated CI cache and compiler-cache workflows, Git compatibility build changes, and contribution-documentation and root-entry updates. These do not implemen… | Remove the unrelated CI/cache, Git compatibility, contribution-documentation, and root-entry changes from this PR, or move them to a separate PR. | |
| Docstring Coverage | Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files. (1 skipped: 1 … | Write docstrings for the functions missing them to satisfy the coverage threshold. |
✅ Passed checks (3 passed)
-
Check name Status Explanation Title check ✅ Passed The title clearly summarizes the main change: managed Claude hooks now use the config directory Claude reads. Description check ✅ Passed The description covers the change, rationale, linked issue, visual-proof exception, testing, AI disclosure, compatibility considerations, and checklist. It reports targeted tests and type-checking, wh… Linked Issues check ✅ Passed #24873requires Claude hooks to target the settings file underCLAUDE_CONFIG_DIR.getConfigPath()usesdefaultClaudeConfigDir()for.claudewhen no explicitconfigDiris supplied. The helper…
Full details: Out of Scope Changes check
-
Explanation
Changes since the previous review include unrelated CI cache and compiler-cache workflows, Git compatibility build changes, and contribution-documentation and root-entry updates. These do not implement or test
#24873, which concerns Claude hook settings-path resolution.
Full details: Docstring Coverage
-
Explanation
Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 2 files. (1 skipped: 1 unsupported.)
- Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Autopilot is currently an internal CodeRabbit preview.
Comment @coderabbitai help to get the list of available commands.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
- Hook settings target now follows
CLAUDE_CONFIG_DIR:getConfigPath()routes the.claudetarget throughdefaultClaudeConfigDir()— a trimmedprocess.env.CLAUDE_CONFIG_DIRwins verbatim, whitespace-only or unset falls back to~/.claude— matchingClaudeRuntimePathResolverand the structured-session pin..openclaude/.qoder/.codebuddykeep resolving underhomedir(), soCLAUDE_CONFIG_DIRstays Claude-specific. - CLI build graph:
src/main/claude/claude-config-dir-pin.tsis added toconfig/tsconfig.cli.jsonbecausehook-settings.tsis now reachable from the CLI and imports it; the module pulls in only node builtins. - Regression tests: new
hook-settings.test.tspins the four contracts (set / unset / whitespace-only / Claude-compatible CLI). The first case fails against the pre-fix source, so it actually guards the behavior.
Verified this run: focused hook suites pass (hook-settings 4, hook-service 26, claude-config-dir-pin 5, managed-hook-command-contract 79, plus adjacent suites, all green); tsc node and cli floors clean; oxlint/oxfmt clean; check:code-quality:changed reports 0 findings.
deepseek-v4.1-flash (free via Pullfrog for OSS) | 𝕏
|
Thanks for picking this up so quickly — this is the same fix I had for #24873 (routing the I verified the behavior on macOS:
One gap this PR leaves: hooks an older Orca already wrote to Happy to open it as a separate PR once this one lands, or feel free to fold it in here if you prefer. |
|
I opened #25276 before seeing this one, and it fixes the same thing (#24873). Flagging it rather than letting the two sit in parallel, because they overlap almost completely. Shared: both make Different: this PR decides with Yours is two days older and smaller (+46/-1 against +95/-2). If a maintainer is picking one, that seems like the reasonable pick, and I am happy to close #25276 in its favour. The one thing I would suggest keeping is the descriptor-level discriminator, if you would rather not key behaviour off a directory-name string. Both are green on |
|
Done — #25276 is closed in favour of this one. Nothing to carry over unless a reviewer wants the descriptor field over the |
dfe6205 to
2032ed0
Compare
|
Rebased onto current main (2f49377). Main's |
|
Gentle ping while this is in a good state to review. Since the last update it has gained unusually strong signals on record: the overlapping #25276 was closed in its favor by @paoValle, and @yacchi (reporter of #24873) endorsed the approach there. The branch was rebased onto main on Oct 7 and composes cleanly with the new Whenever review bandwidth allows, a look would be much appreciated. Happy to answer anything about the descriptor-vs-env-var choice or provide extra test evidence. |
paoValle
left a comment
There was a problem hiding this comment.
Read the whole diff, #24873 and yacchi's verification. The mechanism is right: getConfigPath was the one place resolving Claude's home differently from every other Claude path, reusing defaultClaudeConfigDir() keeps a single definition of it, and scoping the variable to configDirName === '.claude' is the conservative call — the other five names in that union are not Claude and must keep their own homes.
I checked the resolution by hand rather than by running the suite (no node_modules here, and I would not install an Electron tree for a path change): replicating both helpers in plain Node gives set and set/with/trailing/slash → $CLAUDE_CONFIG_DIR/settings.json; whitespace-only and empty string → ~/.claude/settings.json; OPENCLAUDE_HOOK_SETTINGS with the variable set → ~/.openclaude/settings.json; explicit configDir → wins over the env value. So the four contracts your new test asserts are what the code does.
Three things, most important first.
1. The explicit-override precedence is untested. install/getStatus pass options.configDir (the profile pin from #24300/#24616) and the ?? makes it win, but that is exactly the composition this PR lands on, and neither the new file nor claude-profile-hooks.test.ts / hook-service.test.ts sets CLAUDE_CONFIG_DIR alongside a configDir. One case — configDir and CLAUDE_CONFIG_DIR both set, expect the pin — pins the precedence instead of leaving it to the ??.
2. The .claude string comparison is the part I would reconsider (you offered the descriptor discussion). The union already carries six names, and this branch silently classifies any future .claude-named client as a CLI that reads CLAUDE_CONFIG_DIR. A configDirEnvVar?: 'CLAUDE_CONFIG_DIR' field on ClaudeCompatibleHookSettings makes it declarative and greppable — what #25276 did before we closed it. Not a blocker; the string works today.
3. Non-blocking — yacchi's point, restated with the code path. remove() (hook-service.ts:312) calls getConfigPath(this.options.settings) with no pin, so it now resolves the same environment-aware default: the managed entries an older Orca wrote to ~/.claude/settings.json are unreachable from Orca while CLAUDE_CONFIG_DIR stays set — nothing for those sessions reads them, and nothing cleans them. It heals if the variable is later unset, since both paths target ~/.claude again and the managed entries dedupe/remove. So I would not hold the PR for it; a one-time legacy-path cleanup or a tracked issue are both fine.
Worth naming as correct: refuseProfileAtDefaultHome now refuses a profile pinned to the environment's directory. That follows from this change and is what you want — it is the default home now.
Everything above that is not the hand-check is read, not executed.
2032ed0 to
7cd779b
Compare
|
Rebased onto current The rebase also pushed @nwparker gentle ping whenever you get a chance — happy to adjust anything. (Also: this PR supersedes #25276, whose author kindly closed it in favor of this one — no rush, just keeping the thread current.) |
7cd779b to
c73bd89
Compare
|
Rebased onto current @nwparker gentle ping whenever you get a chance — happy to adjust anything. |
…y reads Orca's Claude agent-status hook installer resolved its target as `~/.claude/settings.json` unconditionally, while every other part of Orca's Claude code resolves the CLI's config dir through `CLAUDE_CONFIG_DIR`. When that variable is set, Claude Code reads user settings from `$CLAUDE_CONFIG_DIR/settings.json`, so the managed hooks never fired for those sessions and an unrelated `~/.claude/settings.json` got modified instead (stablyai#24873). `getConfigPath` now routes the `.claude` settings target through the existing `defaultClaudeConfigDir()` helper — the same resolution the rest of Orca's Claude code uses: a set `CLAUDE_CONFIG_DIR` wins, a whitespace-only value falls back to `~/.claude`. Claude-compatible CLIs (`.openclaude` etc.) keep their own home directories; `CLAUDE_CONFIG_DIR` is a Claude CLI variable and says nothing about theirs. Fixes stablyai#24873 Generated-by: GLM-5.3-Flash (ZCode)
|
@ggbdpq Thanks! Really looking forward into this merged! Orca ignoring |

ELI5
Orca installs little helper hooks into Claude Code's settings file so it can show agent status. But it always wrote them into
~/.claude/settings.json, even when the user (or Orca itself) told Claude Code to keep its settings somewhere else via the standardCLAUDE_CONFIG_DIRenvironment variable. In that setup Claude reads a different file, so the hooks never ran and an unrelated settings file got edited. Now the installer writes to the same file Claude Code actually reads.What Changed
getConfigPath()insrc/main/claude/hook-settings.tsresolved the settings target as~/.claude/settings.jsonunconditionally. It now routes the.claudetarget through the existingdefaultClaudeConfigDir()helper (claude-config-dir-pin.ts) — the same resolution the rest of Orca's Claude code (session spawning, supervised stop, config-dir pinning) already uses: a setCLAUDE_CONFIG_DIRwins verbatim, a whitespace-only value falls back to~/.claude. Claude-compatible CLIs (OPENCLAUDE_HOOK_SETTINGS→.openclaude, plus the droid/gemini/cursor/copilot/kimi hook services that share this helper) keep resolving to their own home directories —CLAUDE_CONFIG_DIRis a Claude CLI variable and says nothing about theirs.claude-config-dir-pin.tsis newly reachable from the CLI build graph, so it is registered inconfig/tsconfig.cli.jsonper repo convention.User-visible before/after: with
CLAUDE_CONFIG_DIRset, Orca's managed Claude hooks land in$CLAUDE_CONFIG_DIR/settings.json, fire for those sessions, and~/.claude/settings.jsonis no longer touched. Without the variable, the path is byte-for-byte what it was before.Why
The hook installer resolving the CLI's home differently from every other Claude-code path in Orca is the bug; fixing it at the shared resolver (reusing the existing helper rather than re-deriving the env fallback inline) keeps one definition of "where does the Claude CLI read settings". Scoping the env-var honor to
.claudespecifically avoids a drive-by behavior change for the Claude-compatible CLIs that sharegetConfigPathbut have no relationship toCLAUDE_CONFIG_DIR.Linked Issue
Fixes #24873
Visual Proof
N/A — no UI surface change; the observable behavior is which settings file receives Orca's hooks. The regression tests assert the resolved path in all four cases: set (follows), unset (legacy path unchanged), whitespace-only (falls back), and Claude-compatible CLI (unaffected).
Testing
New
hook-settings.test.tspins the four contracts above. Local matrix: hook-service and hook-install suites 149 passed / 5 skipped (pre-existing skips),pnpm tcclean, oxlint clean on changed files. Manual end-to-end with a realCLAUDE_CONFIG_DIR-pinned Claude Code session was not run in this environment; the path resolution is covered by the tests and the helper is the same one the session-spawning path already exercises.AI Disclosure
Implemented by GLM-5.3-Flash (ZCode agent) under direction of ggbdpq, who completed verification after the authoring run was interrupted and reviewed the diff.
Review
Agent skill upstream boundary
docs/reference/agent-skill-sharing-upstream-boundary.mdand copies or mechanically translates no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation.Notes
Cross-platform: resolution is pure path/env logic, no platform branches; Windows hook files derive from the same
getConfigPath. No persistence or protocol impact; unset-variable behavior is byte-identical to before, so this cannot regress the default setup.Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)