Skip to content

feat(config): honor a relocated Claude Code config dir (CLAUDE_CONFIG_DIR) via toolRoots - #728

Merged
jeff-r2026 merged 1 commit into
Tencent:mainfrom
Rererr:feat/tool-root-override
Sep 24, 2026
Merged

jeff-r2026 merged 1 commit into
Tencent:mainfrom
Rererr:feat/tool-root-override

Conversation

@Rererr

@Rererr Rererr commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Add toolRoots to the member-level local config so a relocated Claude Code config directory (CLAUDE_CONFIG_DIR) is honored: every Claude path teamai resolves in user scope, plus the HOME-bound hook injection that project scope also performs, moves to that directory. teamai init records CLAUDE_CONFIG_DIR into toolRoots.claude automatically, and teamai doctor reports when the variable and the recorded root disagree. Without this, a member using a second Claude Code profile gets hooks, skills and rules written into ~/.claude/, which that Claude never reads, while doctor stays green.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature causing existing behavior to change)
  • Documentation only
  • Refactor / internal cleanup

Test Plan

  • npx tsc --noEmit passes
  • npx vitest run passes (the only failures are the 3 shell-profile.test.ts Windows cases, which fail identically on a pristine main checkout on macOS; lock-atomic.test.ts has a pre-existing timing flake that reproduces on pristine main too)
  • Added/updated tests for the change

New/updated tests: tool-roots.test.ts (new, 28 cases: re-rooting every path field of the listed tool only, ~/ expansion, .config/<name> roots, refusal of outside-HOME / deeper-nested / bare .config roots with a warning, refusal of every tool id other than claude, every root a customized toolPaths.claude spreads its fields over, no phantom key for a field the team did not declare, a bare file name declared beside the root (settings: settings.json) moving inside the new one, MCP companion file moving inside the root even when the root equals the default or the resource root is customized, project-scope resource paths untouched while HOME hook paths follow the root, self single-repo hooks untouched, schema round-trip, detectClaudeConfigRoot, real hook injection into <HOME>/.claude-work/settings.json without creating <HOME>/.claude), init.test.ts (+12: records the root, records an explicit default, keeps the root across a re-init without the variable, refuses outside-HOME / nested / bare .config; a re-init that moves the root removes the hooks from the previous root — recorded or the default one — and releases its managed MCP servers and delivered model config, leaves an unmoved root alone, never creates the old settings file just to clean it, and clears the record on a set-but-blank variable; a project-scope init without the variable inherits the user-scope record), doctor.test.ts (+8: not built when the config does not sync Claude Code, pass/fail/absent cases, ~/-written value, refused value — asserting the reason is named and re-running init is not prescribed —, unrecorded value), self-mode-agents.test.ts (+1: a relocated Claude Code with no ~/.claude is detected as installed), uninstall.test.ts (+1: removes hooks from the relocated HOME root and from the legacy <project>/.claude copy), local-agent-mcp.test.ts (+1), local-agent-model-config.test.ts (+3: model sync into the relocated root; releaseClaudeModelConfig drops the delivered gateway env and profile from a given root and forgets them, and touches nothing — not even the manifest — when no model was delivered).

Mutation checks: reverting applyToolRoots in scopedToolPaths fails 4 tests; reverting the allowlist fails 5; deriving only the first root fails 1; restoring the name gate on the MCP companion fails 1; dropping the previous-root cleanup in init fails 2; dropping its exists/has-hooks guard fails 1; dropping the manifest from that removal (team hooks would survive) fails 1; dropping the user-scope fallback for project scope fails 1; dropping the refusal reason in doctor fails 1; ignoring the variable in detectHomeInstalledAgents fails 1; assigning undeclared fields in relocateToolPaths fails 1; ignoring a blank variable fails 1; skipping the MCP release fails 1; skipping the model-config release fails 1; building the doctor check for a config that excludes Claude fails 1; leaving bare file names in place fails 5; writing an empty model manifest on a no-op release fails 1; reverting the per-target hook path in uninstall fails 1; reverting the MCP path in installMcpServer fails 1; reverting detectProjectConfig in memberToolRoots fails 1.

Real-CLI end-to-end (built dist/index.js, isolated HOME, local bare repo through the generic git provider; never touching a real ~/.teamai):

  • CLAUDE_CONFIG_DIR=$HOME/.claude-work, user scope: init prints Recorded CLAUDE_CONFIG_DIR as the Claude Code root: …/.claude-work; pull lands rules, skills, hooks (settings.json) and the user-scope MCP file (.claude.json) under .claude-work/; $HOME/.claude and $HOME/.claude.json are never created; doctor passes, including the new Claude Code root matches CLAUDE_CONFIG_DIR check.
  • Re-init from a shell without the variable keeps toolRoots.claude.
  • Re-init that moves the root (unset → ~/.claude-a, then ~/.claude-a~/.claude-b): the previous root's settings.json loses its teamai hooks — built-in (6 → 0) and the team hook from hooks/hooks.yaml (1 → 0) — the new one gets them, toolRoots.claude follows; the team MCP server that pull had landed in the old MCP file (~/.claude.json, then ~/.claude-a/.claude.json) is removed (1 → 0 each time); the skills/rules left behind are named in the output. Re-init with the same root removes nothing. CLAUDE_CONFIG_DIR= teamai init (set but blank) clears the record, releases ~/.claude-b and re-arms ~/.claude (hooks 0 → 6). doctor passes under the recorded root and fails with the re-run hint when the variable points elsewhere; with a value outside HOME it names the reason instead of prescribing a re-run.
  • Project scope, from a shell without the variable, after a user-scope init recorded ~/.claude-b: the project config inherits toolRoots.claude, its hooks land in ~/.claude-b/settings.json (not ~/.claude).
  • CLAUDE_CONFIG_DIR=$HOME/.claude (explicit default): recorded; MCP goes to $HOME/.claude/.claude.json, not $HOME/.claude.json.
  • CLAUDE_CONFIG_DIR=$HOME/.config/claude-work: accepted; everything lands under it.
  • CLAUDE_CONFIG_DIR=$HOME/configs/claude and $HOME/.config: init refuses with the reason; nothing recorded.
  • doctor with the variable pointing elsewhere: ✖ … CLAUDE_CONFIG_DIR is …/.claude-elsewhere; this config syncs Claude Code to …/.claude-work. Re-run \teamai init` to record it.` With the variable unset the check is not reported.
  • Project scope: hooks go to $HOME/.claude-work/settings.json, resources stay under <project>/.claude/.
  • uninstall in project scope with a legacy <project>/.claude/settings.json present: hooks removed from both files.
  • Hand-written toolRoots.codex / .omp / .copilot: warned and ignored; no *-work directories created.
  • Regression with the variable unset and only ~/.claude present: no toolRoots recorded, all writes at the default locations, doctor green.
  • Not exercised with the real CLI: the HTTP local-agent path (unit-tested only) and teamai push (resolves through scopedToolPaths, so it reads the relocated root by construction).

Related Issues

Closes #725

Notes for Reviewers

  • Where the override applies. Inside scopedToolPaths() at user scope. Every hook writer already resolves its paths at the hook scope (scopedToolPaths(teamConfig, { ...localConfig, scope: hookScope }), feat(agents): support Qoder CN via its .qoder-cn user directory #695/fix(doctor): probe the Copilot hooks file where inject writes it #733), so hooks injected into HOME from a project-scope config pick the root up with no further change; the local agent's HOME writers call applyToolRoots on their synthetic team config. Project-scope resource paths are deliberately untouched: CLAUDE_CONFIG_DIR relocates the user config, not <project>/.claude/. Self single-repo hooks stay in the business repo.
  • Why a persisted key rather than reading the env var on every run. Hooks inherit the variable from Claude Code, but a manual pull / doctor / uninstall from another shell would not, splitting one install across two directories. init is the only command that reads CLAUDE_CONFIG_DIR; doctor only compares.
  • Moving the root on re-init. When init records a root different from the one the previous config resolved to (recorded, or the default), the active configuration in the previous root is released first: the teamai hooks in its settings.json (a manifest-aware pass, so team hooks go too), the teamai-managed MCP servers in its MCP file (reconcileMcpForConfig with removeAll, addressed through the previous config so it resolves to the old file), and the gateway env / model profile the local agent delivered there (releaseClaudeModelConfig, which also forgets them in the model manifest, so the agent re-delivers into the new root). Otherwise that Claude keeps firing the hooks, starting the servers and using the credentials — one install split across two directories. Skills, rules and the CLAUDE.md block under the old root are inert copies; they are left where they are and named in the output rather than deleted by init. No file is created just to be cleaned. Server-pushed agent hooks in the old root are removed with the rest; the local agent's hook manifest is not rewritten, so they come back with the agent's next push.
  • Ending a relocation. An unset variable cannot mean "clear the record" — it is also what every shell that does not export it looks like, and re-init from such a shell must keep the root. A set-but-blank value is the explicit signal: CLAUDE_CONFIG_DIR= teamai init … clears toolRoots.claude and releases the old root the same way. Documented in the usage guide and in the setup skill.
  • Project scope inherits the user record. The root is a fact about the machine and project hooks land in HOME, so a project-scope init with no record of its own and no variable to read starts from the user-scope toolRoots. Records stay per scope: a later user-scope move does not rewrite project records, but doctor (with the variable set) reports the drift for that project.
  • doctor builds the root check only for a config that syncs Claude Code (Claude declared in toolPaths and not excluded through enabledAgents / disabledAgents); there is nothing to compare otherwise.
  • Bare file names move inside the root. relocateToolPaths treats any path without a / (.claude.json, or a customized settings: settings.json) as a file beside the root and moves it inside the new one — the MCP companion is the usual case, not a special one.
  • Explicit default is not "unset". With CLAUDE_CONFIG_DIR=~/.claude, Claude Code reads .claude.json from inside that directory (docs), so the value is recorded and the MCP companion file moves inside the root even when the root is unchanged. doctor also fails when the variable is set but nothing is recorded, for the same reason.
  • Accepted root shapes. One directory in HOME (not .config itself) or .config/<name>: exactly what toolInstallRoot() can express, so the installed-tool gate keeps meaning "the root exists". Deeper roots would let an unrelated ~/configs count as installed. The four settings.split('/')[0] gates in hooks.ts now use toolInstallRoot() so hook and resource gates agree on .config/<name> roots.
  • claude only, by allowlist. A root is only honest for a tool whose every user-scope write goes through toolPaths. Claude qualifies (hooks, skills, rules, agents, CLAUDE.md, MCP, model sync, co-author); most others still have a fixed path (OMP's extension dir, Codex/Cursor co-author files, OpenCode's plugin dir, Copilot's $COPILOT_HOME). Other tool ids are refused with a warning. The key stays a generic record so a tool can be added once its writes are audited.
  • Moved, not changed: toolInstallRoot() from resources/base.ts to types.ts (needed there; avoids a circular import).
  • detectHomeInstalledAgents (self-mode picker, clone-time self-heal) now also counts Claude as installed when CLAUDE_CONFIG_DIR names an existing directory; it runs before any config exists, so the variable is the only signal there. Reading it outside init is limited to this detection.
  • Left as is: legacyHooksNeedReinject in pull.ts probes the fixed ~/.claude/settings.json before config is loaded. On a machine that still carries pre-dispatch-format hooks there while syncing to a relocated root, the migration re-injects into the relocated root and re-fires on each pull; a re-init on this branch clears that file, so the case is confined to installs that never re-init. memberToolRoots in the local agent resolves the governing config per call (a detectProjectConfig each time); the writers that call it are per hook / per resource, not per tick.
  • Docs: docs/usage-guide.md and docs/usage-guide.zh-CN.md (config reference sample + a "Relocated tool roots" subsection); skill-data/setup/references/join-member.md (init with CLAUDE_CONFIG_DIR, moving and ending a relocation) and skill-data/core/references/troubleshooting.md (the doctor check and what to do). No command or flag changed, so commands.md is untouched. READMEs do not enumerate local-config keys, so they are unchanged.

🤖 Generated with Claude Code

@jeff-r2026
jeff-r2026 marked this pull request as ready for review September 23, 2026 11:28
@jeff-r2026 jeff-r2026 self-assigned this Sep 23, 2026
@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/init.ts:1606 overwrites a previously recorded Claude root without cleaning the old location. Re-running teamai init after changing CLAUDE_CONFIG_DIR installs hooks/resources in the new root but leaves executable TeamAI hooks and managed files in the previous profile. The doctor’s prescribed remediation therefore creates a split installation instead of moving it.
  • [P1 blocking] src/__tests__/git-kind-learnings.test.ts:160 removes the base branch’s root-user guard around a chmod(000) test. Under root, the file remains readable, so the assertions fail. Restore it.skipIf(process.getuid?.() === 0) when rebasing this PR.
  • [P1 blocking] skills/teamai/references/setup-admin.md:25 and the related changes to skills/teamai/SKILL.md, join-member.md, and deletion of provider-tgit.md are unrelated to CLAUDE_CONFIG_DIR. They violate the repository’s surgical-change rule and would overwrite separate TGit/onboarding documentation work; remove these changes from the PR.
  • [P2 non-blocking] src/types.ts:1838 derives only one base root from the first populated Claude path. A valid customized toolPaths.claude whose fields use different roots is only partially relocated, despite the feature promising every user-scope Claude path will move.
  • [P2 non-blocking] src/types.ts:1810 moves the Claude MCP companion only when its basename starts with the selected old resource root. With customized Claude resource paths, the normal .claude.json MCP path can remain at $HOME/.claude.json even though CLAUDE_CONFIG_DIR requires it inside the relocated directory.

The PR description contains a detailed test plan and real-CLI end-to-end verification record, so no testing-description finding is needed.

@Rererr
Rererr force-pushed the feat/tool-root-override branch from aa644a5 to 2c58bd6 Compare September 23, 2026 16:24
@Rererr

Rererr commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — addressed in the rebased push.

  • P1 (init overwrites the root without cleaning the old location): a re-init that moves the root now removes the teamai hooks from the previous root's settings.json (recorded root or the default one) before saving, and names the skills/rules left behind. Covered by 4 new init.test.ts cases and a real-CLI run (unset → ~/.claude-a~/.claude-b; hook count in the old file 6 → 0 each time, nothing removed when the root does not move).
  • P1 (root-user guard in git-kind-learnings.test.ts) and P1 (skills docs): the PR never touched those files — the branch was 25 commits behind main and the diff was taken against current main. Rebased onto main; the three-dot diff no longer includes them.
  • P2 (single base root): applyToolRoots now collects the root of every path field (base and userScope), so a customized toolPaths.claude spread over several roots moves entirely.
  • P2 (MCP companion gated on the root name): a top-level user-scope MCP file now moves inside the new root unconditionally; Claude Code reads .claude.json from inside CLAUDE_CONFIG_DIR whatever the resource root is called.

A second review pass on the rebased branch turned up five more things, fixed in the same push: the old-root cleanup is manifest-aware (team hooks are stripped too, not only built-in ones); a project-scope init without the variable inherits the user-scope record instead of sending project hooks to ~/.claude; doctor names the reason for a value init refuses instead of telling you to re-run init; detectHomeInstalledAgents counts a relocated Claude Code that has no ~/.claude; and relocateToolPaths no longer adds undefined keys for fields the team did not declare.

The rebase also folds the branch onto #695/#733: the hookToolPaths() wrapper is gone, since every hook writer already resolves its paths at the hook scope through scopedToolPaths, where the override lives. Diff is smaller for it.

@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/init.ts:1680 makes a recorded Claude root impossible to clear through init: after the user removes CLAUDE_CONFIG_DIR, re-init preserves the old toolRoots.claude, while src/doctor.ts:189 omits the consistency check when the variable is unset. TeamAI therefore continues syncing to the obsolete profile and reports green while Claude reads the default profile. Provide an explicit clearing path or detect this transition.
  • [P1 blocking] src/init.ts:82 migrates only settings.json hooks when the root changes. TeamAI-managed MCP servers remain in the previous .claude.json, and HTTP-delivered model credentials/configuration can remain in the previous settings.json and teamai-models.json. These are active configuration, not inert resources, so moving profiles leaves a split installation and potentially stale credentials.
  • [P1 blocking] src/resources/agent-format.ts:442 reverts the base branch’s multiline Codex TOML rendering fix, collapsing prompts back into escaped single-line strings. The associated tests in src/__tests__/agent-format.test.ts are also deleted. Rebase and retain the base implementation.
  • [P1 blocking] src/pull.ts:1201 reverts the base branch’s delivery-reporting fix and again prints “Synced” for skills or agents when no installed tool directory received them. src/__tests__/pull-sync-truth.test.ts is deleted as part of the regression. Rebase and preserve this behavior and coverage.
  • [P1 blocking] docs/usage-guide.md:1953 documents a new initialization/configuration workflow without updating any corresponding skill-data/ guidance. The repository explicitly treats skill-data/ as user-facing documentation and requires behavior changes to update affected setup/core skills.
  • [P2 non-blocking] src/doctor.ts:187 registers the Claude-root check unconditionally whenever CLAUDE_CONFIG_DIR exists. Configurations that do not declare Claude, or explicitly exclude it through enabledAgents/disabledAgents, therefore fail doctor even though TeamAI performs no Claude writes.
  • [P2 non-blocking] src/types.ts:1917 relocates only paths containing /, apart from the special-cased MCP filename. Valid customized bare paths such as settings.json, CLAUDE.md, or agents remain under HOME despite the documented promise that every user-scope Claude path follows toolRoots.

Earlier Findings

  • The previous-root hook cleanup, root-per-field relocation, customized MCP relocation, root-user test guard, and unrelated TGit/onboarding documentation findings are resolved in the current diff.

The PR description includes a detailed test plan and real-CLI end-to-end record, so no testing-description finding is needed.

Claude Code can move its whole user config directory with
CLAUDE_CONFIG_DIR, but teamai resolved every Claude path from the
team-wide toolPaths (.claude/...), so hooks, skills and rules were
written to ~/.claude, which that Claude Code never reads, and doctor
stayed green.

Add a member-level `toolRoots` key to the local config. In user scope
`scopedToolPaths` re-roots every path of the listed tool; a new
`hookToolPaths` does the same for writes that land in HOME regardless
of scope (hook injection/removal/listing, doctor's hook checks, the
local agent). `teamai init` records CLAUDE_CONFIG_DIR into
`toolRoots.claude` and keeps it across a re-init; `teamai doctor`
reports when the variable and the effective root disagree.

An explicit CLAUDE_CONFIG_DIR=~/.claude is recorded too: Claude Code
then reads .claude.json from inside the directory, so the MCP companion
file moves inside the root even when the root is unchanged. Accepted
roots are one directory in HOME or .config/<name>, the shapes
`toolInstallRoot` can express; the hook gates in hooks.ts now use it so
hook and resource gates agree. Only `claude` is accepted for now: it is
the one tool whose every user-scope write goes through toolPaths.

Closes Tencent#725

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@Rererr
Rererr force-pushed the feat/tool-root-override branch from 2c58bd6 to 94c72ae Compare September 23, 2026 18:50
@Rererr

Rererr commented Sep 23, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — second round addressed in the rebased push.

  • P1 (no way to clear a recorded root): an unset variable cannot carry that meaning — it is also what every shell that does not export it looks like, and a re-init from such a shell must keep the root (previous round). A set-but-blank value is now the explicit signal: CLAUDE_CONFIG_DIR= teamai init … clears toolRoots.claude and releases the old root the same way. Documented in the usage guide and the setup skill; covered by an init.test.ts case and the real-CLI run (record cleared, ~/.claude-b released, ~/.claude re-armed).
  • P1 (MCP servers and delivered model config left in the old root): the release now covers all three active pieces — hooks, teamai-managed MCP servers (reconcileMcpForConfig with removeAll, addressed through the previous config so it resolves to the old file) and the gateway env / model profile the local agent delivered (new releaseClaudeModelConfig, which also forgets them in the model manifest so the agent re-delivers into the new root). Real-CLI: the team MCP server pull had landed in ~/.claude.json, then in ~/.claude-a/.claude.json, is removed on each move (1 → 0); the model path is unit-tested (3 cases, including that a no-op release writes nothing).
  • P1 (Codex TOML rendering) and P1 (delivery reporting) "reverts": the branch does not touch agent-format.ts, pull.ts, or those tests — fix(agents): render codex TOML with multi-line literal strings #754 and fix(pull): gate the generic sync report on a tool that can receive it #751 landed on main after the previous rebase, and the review diffed against the moved main rather than the merge base (same as the two findings in round one). Rebased onto current main again; the three-dot diff is clean. If the review job can diff against the merge base, this class of finding goes away.
  • P1 (skill-data/ not updated): added the CLAUDE_CONFIG_DIR flow to skill-data/setup/references/join-member.md (init from a shell that exports it, what a move releases, how to end it) and a troubleshooting item for the doctor check in skill-data/core/references/troubleshooting.md. No command or flag changed, so commands.md is untouched; old wording grepped.
  • P2 (doctor check for configs that do not sync Claude): built only when Claude is in the config's toolPaths and not excluded.
  • P2 (bare customized paths): any path without a / now moves inside the new root, not only the MCP companion — that special case is gone.

@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/init.ts:101 invokes reconcileMcpForConfig(..., { removeAll: true }) with the complete team configuration. That reconciler iterates every MCP-capable tool, so changing only Claude’s root also removes TeamAI-managed MCP servers from Codex, Cursor, Copilot, and other installed tools. init does not restore those servers afterward; restrict cleanup to the previous Claude target.
  • [P1 blocking] src/init.ts:91 decides whether the root changed solely by comparing the resolved settings path. Transitioning between an unset CLAUDE_CONFIG_DIR and an explicitly configured ~/.claude leaves that path unchanged, but changes the MCP location between ~/.claude.json and ~/.claude/.claude.json. Cleanup is skipped, leaving the old managed MCP configuration behind.
  • [P1 blocking] src/init.ts:97 only releases delivered Claude model credentials for user-scope re-init. The local agent resolves its global Claude model files through the governing project config, so changing or clearing toolRoots.claude in project scope leaves gateway credentials and teamai-models.json in the previous root.
  • [P1 blocking] src/init.ts:105 derives the old Claude root with path.dirname(oldSettings). For supported customized paths such as .claude/config/settings.json, this targets <root>/config rather than the recorded Claude root, so credentials in <root>/settings.json and <root>/teamai-models.json remain. Resolve the previous root directly from previous.toolRoots.
  • [P2 non-blocking] src/types.ts:2017 relocates only consumers using scopedToolPaths, while src/import-local.ts:248 and src/usage-tracker.ts:170 still hard-code ~/.claude/rules and ~/.claude/skills. Consequently, import --from-claude misses relocated rules and skill-use tracking rejects skills installed solely under the relocated root.

Earlier Findings

  • The earlier regressions, unrelated documentation changes, exclusion handling, bare-path relocation, and missing setup/core documentation are resolved.
  • The PR description still contains a detailed test plan and real-CLI end-to-end record, so no testing-description finding is needed.

@jeff-r2026
jeff-r2026 merged commit 55b71ef into Tencent:main Sep 24, 2026
11 checks passed
@Rererr

Rererr commented Sep 24, 2026

Copy link
Copy Markdown
Contributor Author

Thanks — this landed at the second-pass state, so the third pass is being taken as follow-ups.

  • P1 (MCP release removes every tool's servers) and P2 (import --from-claude / skill-use tracking hard-code ~/.claude): fixed in fix(config): release only Claude's MCP servers on a root move; read the recorded root in import and skill tracking #775, with a real-CLI record showing Codex's server surviving the Claude root move on the branch and dropping to 0 on main until the next pull.
  • P1 (settings path as the only change signal), P1 (model config released in user scope only), P1 (dirname(settings) for a nested customized path): confirmed; each has a narrow trigger (explicit CLAUDE_CONFIG_DIR=~/.claude, HTTP mode in project scope, a team-customized nested settings path). Not in this follow-up; happy to take them as separate changes if wanted.

jeff-r2026 pushed a commit that referenced this pull request Sep 24, 2026
…he recorded root in import and skill tracking (#775)

A re-init that moved the Claude Code root handed the full team config to
reconcileMcpForConfig({ removeAll }), which walks every MCP-capable tool,
so Codex, Cursor and the rest lost their teamai-managed servers until the
next pull. The release now narrows the team config to Claude.

import --from-claude scanned ~/.claude/rules and skill-use tracking only
knew the static ~/.claude/skills; both now resolve the recorded root. The
resolution (project config governing the directory, else user scope) moves
into resolveMemberToolRoots so the local agent, import and tracking agree;
tracking resolves it from the hook's reported directory. The helper checks
that the directory exists before probing, as resolveConfigForDir does, so a
hook from a deleted worktree still records — this also stops the local
agent from throwing on a missing workspace path.

Follow-up to #728 (third review pass, findings 1 and 5).

Co-authored-by: Claude Fable 5.1 <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.

[feat] Honor a relocated Claude Code config dir (CLAUDE_CONFIG_DIR): per-member tool root override

2 participants