Skip to content

fix(hooks): trust Codex hooks and share project hooks across worktrees - #958

Open
SaulMoro wants to merge 13 commits into
Tencent:mainfrom
SaulMoro:fix/955-codex-hook-trust
Open

SaulMoro wants to merge 13 commits into
Tencent:mainfrom
SaulMoro:fix/955-codex-hook-trust

Conversation

@SaulMoro

@SaulMoro SaulMoro commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

 project scope, Claude and public Codex
   HOME built-in dispatch hooks
-  HOME team hooks gated per checkout
+  main-checkout team hooks shared by worktrees
 after hooks + MCP reconcile
-  remind the member to approve Codex hooks
+  trust exact managed commands using Codex's own currentHash
+  cache only a complete trusted result

Codex silently skips untrusted or modified hooks, so a reminder alone leaves SessionStart and project rule delivery inactive. Auto-trust is on by default through codex app-server; codexTrustEnabled: false in the member's config.yaml opts out. Trust queries use the main checkout even when the current worktree has no .codex/; doctor reads the current worktree without writing trust.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Test Plan

  • npm run build passes
  • npx tsc --noEmit passes
  • npm run lint passes
  • npx vitest run on 8042fbb6: 372 files pass, 7,258 tests pass, 1 skipped
  • npm run test:e2e on b177c7a0: 80 files pass, 453 tests pass, 26 skipped; not rerun locally for the review follow-ups below
  • Added/updated tests for the change

Before: New regression tests failed because a pull in a worktree without .codex/ cached success without trusting the main hook, absent requested hooks returned trusted, and a member command containing teamai pull received trust.
After: Public pull regression, exact command selection, missing-hook retry, cross-checkout cache, interactive/silent/dry-run pull, init/bootstrap, doctor diagnostics, project realpath/untrusted tests, app-server initialization cleanup, and hooks inject fallback trust pass. A rejecting app-server previously kept hooks inject alive after the warning; the rebuilt CLI now exits cleanly.

Real CLI verification on macOS with Codex 0.159.3, isolated HOME and CODEX_HOME under /private/tmp/tai955-final-7c2k9l5h, CLAUDE_CONFIG_DIR unset, local git provider:

  1. Built CLI: init https://git.example.com/demo/team.git --provider git --scope project --agent codex,claude. hooks/list reports HOME and main team hooks trusted.

  2. Added a worktree without .codex/, introduced a new team hook, and ran pull --force there before SessionStart. Main hook keys are trusted; worktree still has no .codex/. HOME SessionStart is trusted and loaded there.

  3. Ran hook-dispatch session-start --tool codex with that worktree's payload and waited for the background dispatch. .codex/ is created; worktree hooks/list reports the main team hooks trusted.

  4. Removed the worktree and ran pull --force: all hooks remain trusted; HOME Codex/Claude files contain no old $PWD gates.

  5. Set the new hook's trusted hash to a wrong value through config/batchWrite: doctor --json reports Codex trusts the teamai hooks: ok=false. pull --force restores trusted status.

  6. Broke the local team hook YAML, removed HOME Codex hooks, and invalidated built-in hashes. hooks inject exits 1 without overall success; recreated built-ins are trusted.

Local verification artifacts: /tmp/tai955/e2e-final.py, /tmp/tai955/e2e-final.log, and /tmp/tai955/{build,tsc,lint,tests}-final.log. Extra providers and tools are left to CI; no full provider/tool matrix was run locally.

E2E follow-up evidence

  • Before: npm run build && npm run test:e2e reproduced CI's seven failures: 4 files failed, 394 tests passed, 26 skipped. Claude/Codex team-hook assertions still read HOME. The [feat] Scope hooks, MCP servers and env variables by project: a project's entries reach every member of the role #668 delivery test stopped before rebinding to billing, so the next mcp list assertion saw checkout; its provenance assertion is unchanged.
  • After: On b177c7a0, after rebasing onto origin/main at bae48e5c, npm run build, npx tsc --noEmit, npm run lint, npx vitest run (372 files, 7,228 passed, 1 skipped), and npm run test:e2e (80 files, 453 passed, 26 skipped) all pass. The four affected real-CLI files pass all 18 tests. They verify ungated Claude/Codex team hooks in the main checkout, HOME built-ins, CodeBuddy's HOME cwd gates, namespace overrides/rebinds, idempotence/removal, and one shared main-checkout file in a real linked worktree.
  • The removal assertion first failed because hooks remove deleted the other project's CodeBuddy hook. This predates [bug] Codex never runs teamai hooks: they need manual trust, and a pull invalidates it #955; removal now passes the same existing project filter as injection. User/self behavior is unchanged. Usage guides and the core troubleshooting reference document the removal boundary.

The rebases preserve #947 rule-cleanup guidance, #950 Claude other-host protection, and #955 automatic trust/main-checkout layout. #929/#931 source changes are inherited from main. Exact Claude command assertions include the other-host prefix; a real-shell control proves the main-checkout hook skips Cursor only when Cursor owns its hooks, and runs from a linked worktree without a cwd gate. Five focused E2E files pass all 25 tests. All checks above were rerun after the final rebase.

All follow-up test commands unset CLAUDE_CONFIG_DIR and CODEX_HOME; unit/E2E runs use temporary HOME directories. No real ~/.claude, ~/.claude2 or ~/.codex was used. Layout fixtures for #373/#264 opt out of Codex trust to test file ownership independently. Remote-provider tests requiring credentials remain skipped. Logs in /tmp/tai958-verification/: build-host.log, tsc-host.log, lint-host.log, unit-host-final.log, e2e-host-final.log; original reproduction: e2e-before.log; removal negative control: removal-red.log; focused verification: host-target-green.log. This follow-up did not rerun live Claude/Codex sessions; automated suites use a fake or unavailable Codex app-server. The earlier live Codex record above is preserved.

Hook ownership review follow-up

 Codex team-hook ownership
-  claim entries by command equality, including newly desired commands
+  record event, matcher-group position and complete generated definition
 Codex trust and doctor
-  select every hook with the same file + command
+  select the exact generated Codex key + file + command
  • Before: Same-command member hooks were trusted in user scope and deleted on the first project reconcile. Public-interface regression tests failed for both cases. The ownership boundary also failed for ambiguous legacy duplicates; a negative control caught duplicate HOME gates when exact positions were applied to internal Codex variants.
  • After: On 9ae91686, npm run build, npx tsc --noEmit, npm run lint, and npx vitest run pass: 372 files, 7,233 passed, 1 Windows-only test skipped. New tests cover different events, identical entries under the same event, matcher/timeout differences, repeat/update/removal, ambiguous legacy records and alternating internal Codex projects. Exact ownership applies only to public Codex; other tools retain their HOME layout/gates. The legacy replacement fixture now supplies its ownership manifest instead of claiming an unrecorded command.
  • Real CLI: Built CLI + real Codex 0.160.0 app-server, isolated HOME with CLAUDE_CONFIG_DIR and CODEX_HOME unset, in both user and project scope. hooks inject preserves member entries sharing the team command; their Codex keys never receive a trusted hash, while the generated team key does. Repeated injection, a team-command update and hooks remove retain those member entries and their options. Both fixtures pass. Artifacts: /private/tmp/tai958-owned-lsmem0u8; script and log: /tmp/tai958-review/verify-owned-hooks.py, real-cli-final.log.

The full E2E suite was not rerun locally for this focused correction. Its previous CI run on b177c7a0 passed with 453 tests and 26 skips. The current correction has the focused real-CLI record above; live agent sessions and credentialed providers were not exercised. Logs in /tmp/tai958-review/: trust-red.log, project-red.log, legacy-red.log, internal-red.log, and {build,tsc,lint,unit}-final.log. No real ~/.claude, ~/.claude2 or ~/.codex was used.

Checkout and path review follow-up

 bare repository
-  write/list/trust the bare project anchor
+  write/list/trust the current workspace, with separate ownership per worktree
 project team-hook targets
-  hard-coded default Claude/Codex paths
+  project toolPaths; Claude settings.local.json beside its configured settings file
 reordered Codex team hook
-  lose ownership when its recorded group index changes
+  recover a unique complete-definition match, preserving ambiguous entries
  • Before: Regression tests failed for bare-anchor placement/trust, configured project paths and a moved managed Codex group. Separate bare worktrees sharing one data home also lost ownership of an older command; uninstall discovered only one workspace's files.
  • After: On 79b01c80, npm run build, npx tsc --noEmit, npm run lint and npx vitest run pass: 372 files, 7,239 tests passed, 1 skipped. Public-interface tests cover actual Git bare anchors, project placement/trust, separate workspace ownership, uninstall discovery, custom project targets with default HOME built-ins, repeat/update/removal and moved Codex definitions. Trust and doctor use the configured target; when Codex does not load a custom path, trust fails with that path and doctor reports not loaded.
  • Real CLI: Built CLI + real Git bare worktrees and Codex 0.160.0 app-server, local git provider, isolated HOME with CLAUDE_CONFIG_DIR and CODEX_HOME unset. hooks inject writes Claude/Codex team hooks into the actual worktrees, grants trust to those worktrees and never writes or trusts the bare directory. Inserting an unrelated member group before a managed Codex entry and reinjecting leaves one team copy. Removing from one workspace preserves its member hook and the sibling workspace's team hook. A custom-path fixture verifies ungated/idempotent project files, default HOME built-ins and removal at the configured paths, with codexTrustEnabled: false. Both fixtures pass. Artifacts: /private/tmp/tai958-checkout-nx4uo_lq; script and log: /tmp/tai958-review-next/verify-checkout-hooks.py, real-cli.log.

The full E2E suite was not rerun locally for this focused correction, as requested. The preceding CI run on 9ae91686 passed, including fork-safe E2E with 453 tests passed and 26 skipped. This head has the representative CLI verification above; live agent sessions and credentialed providers were not exercised. Logs in /tmp/tai958-review-next/: reorder-red.log, bare-red.log, paths-red.log, bare-ownership-red.log, bare-discovery-red.log, and {build,tsc,lint,unit}.log. No real ~/.claude, ~/.claude2 or ~/.codex was used.

Legacy upgrade and removal review follow-up

 legacy Codex ownership
-  require omitted timeout/context options to be absent
+  recover a unique event/matcher/command match for a single command handler
 main-checkout hook removal
-  skip when HOME/current-worktree tool roots are absent
+  remove existing main-checkout hooks without recreating those roots
  • Before: Three legacy-option regression cases failed with duplicate entries for timeout, additionalContextLimit, or both. A linked-worktree removal test failed after the HOME roots were deleted.
  • After: On c7d0a467, npm run build, npx tsc --noEmit, npm run lint and npx vitest run pass: 372 files, 7,245 tests passed, 1 skipped. Tests cover migration/update/removal, trust and direct removal before the first upgraded reconcile, legacy collisions differing only in unrecorded options, multi-handler member groups and missing-HOME main-checkout cleanup. Modern ownership still requires the complete recorded definition; legacy recovery requires a unique match on its recorded fields. Ambiguous entries remain untouched and untrusted.
  • Real CLI: Built CLI, real Codex 0.160.0 app-server and real Git linked worktree, isolated HOME with CLAUDE_CONFIG_DIR and CODEX_HOME unset. A legacy hook carrying timeout/context options migrates to one current entry; repeat/update/removal and direct legacy removal preserve member hooks under other events/matchers. Only the generated team key receives trust. After deleting HOME Claude/Codex roots, hooks remove from a linked worktree removes main-checkout team hooks, retains member hooks and leaves the missing roots absent. Both fixtures pass. Script/log: /tmp/tai958-legacy-review/verify-legacy-hooks.py, real-cli.log; artifacts: /private/tmp/tai958-legacy-hfmcxb7c.

The full E2E suite was not rerun locally for this focused correction, as requested. The earlier CI run on 9ae91686 passed fork-safe E2E with 453 tests passed and 26 skipped. Live agent sessions and credentialed providers were not exercised. Logs: /tmp/tai958-legacy-review/{legacy-red,legacy-green,remove-red,focused-green,build,tsc,lint,unit}.log. No real ~/.claude, ~/.claude2 or ~/.codex was used.

Pre-#370 ownership review follow-up

 main-checkout Codex hooks
-  skip legacy sweep without importing its ownership
+  import <main>/.teamai/managed-hooks.json before reconciliation
+  retire legacy Codex ownership after successful reconcile
 untrusted-project warning
-  assume <project>/.codex/hooks.json
+  describe project hooks and MCP configuration generically
  • Before: Four upgrade/direct-removal regression cases retained the old gated project hook, and both warning cases named an assumed hooks file. All six tests failed.
  • After: On a9ab3f21, npm run build, npx tsc --noEmit, npm run lint and npx vitest run pass: 372 files, 7,251 tests passed, 1 skipped. Tests cover injection and direct removal from main/linked worktrees, no duplicates on repeat, removal after upgrade, consumption of old gated ownership and preservation of same-command member hooks plus other tools' legacy records. Main-only manifests consume old gates; HOME still retains other projects' gates. Warning tests cover hook and MCP-only outcomes without assuming a path.
  • Real CLI: Built CLI, real Git linked worktrees and Codex 0.160.0 app-server, isolated HOME with CLAUDE_CONFIG_DIR and CODEX_HOME unset. Pre-fix(hooks): unify hook-injection scope so project-scope init installs the SessionStart hook #370 fixtures pass upgrade/repeat/update/removal and direct removal from both main and linked worktree. The old gate is consumed, only the generated team key is trusted, member hooks remain untrusted/preserved, and unrelated legacy ownership remains. A custom-path project explicitly marked untrusted receives the generic warning and keeps its untrusted choice. Script/log: /tmp/tai958-transfer-review/verify-transfer.py, real-cli.log; artifacts: /private/tmp/tai958-transfer-us51wi64.

The full E2E suite was not rerun locally for this focused correction, as requested. The earlier CI run on 9ae91686 passed fork-safe E2E with 453 tests passed and 26 skipped. Live agent sessions and credentialed providers were not exercised. Logs: /tmp/tai958-transfer-review/{red,focused-green,build,tsc,lint,unit}.log. No real ~/.claude, ~/.claude2 or ~/.codex was used.

Main-checkout cleanup ownership follow-up

 team-only cleanup in <main>/.claude/settings.local.json and <main>/.codex/hooks.json
-  claim any command containing a teamai marker substring
+  claim exact generated built-ins and manifest-recorded team hooks only
+  Claude entries also need the [teamai:hook:<id>] marker id of the record
 legacy sweep, Codex excluded via enabledAgents / disabledAgents / filterAgents
-  skip the coincident main Codex file (main pass never ran)
+  remove legacy-owned entries and exact built-ins; keep member and current entries
 uninstall, legacy and main targets naming the same file
-  reconcile it twice, the legacy pass without team-only ownership
+  reconcile it once with current + legacy ownership (paths compared by realpath)
  • Before: From a linked worktree, member hooks such as teamai pull --silent && ./notify or teamai hook-dispatch session-start --tool codex && ./notify in the main checkout were deleted on the first reconcile (Claude and Codex cases failed). With Codex excluded, the old gated hook, built-in and legacy record survived (three selection cases failed). Uninstall deleted the member hook through the legacy pass (one case failed).
  • Same command, different definition: a member Claude hook with the generated command, event and matcher but its own timeout or description was kept on the first reconcile, then deleted on the next one, because ownership compared only event, matcher and command. Ownership now also requires the marker id to match the manifest record (one new test, red before the fix).
  • After: On 8042fbb6, npx tsc --noEmit, npm run lint and npx vitest run pass: 372 files, 7,258 tests passed, 1 skipped. Tests cover repeat, update and removal preserving member entries; excluded-Codex sweeps leaving no HOME Codex file or trust; and a single uninstall reconcile under both manifests.

Found by an adversarial review against main plus two reported P1s. The full E2E suite and a real-CLI run were not repeated for this follow-up, as requested; the earlier CI run on 9ae91686 passed fork-safe E2E. Logs: /tmp/tai958-adversarial-review/adversarial-report.md, {marker-red,followup-red}.log. No real ~/.claude, ~/.claude2 or ~/.codex was used.

Related Issues

Fixes #955
Related: #954

Notes for Reviewers

Standards and Spec reviewed the whole branch in parallel. Both confirmed P2 findings were corrected; no findings remain.

Project trust uses the main checkout's realpath only when teamai has project hooks or managed Codex project MCP records. An explicit untrusted choice remains unchanged. #954 owns writing project MCP and its doctor check; this PR provides the shared project-trust API.

Built-ins stay in HOME. A new Codex worktree gets team hooks from its second session, after SessionStart seeds .codex/. Trust written during SessionStart also takes effect in the next session. Claude reads the main checkout immediately; other tools keep their existing layout. Legacy gates for live checkouts and disappeared directories are removed.

Merge Danger

Door: two-way. Code and layouts can be rolled back; trust already written to Codex config persists until the member changes it.
Blast Radius: hooks. The default now grants trust to teamai's exact managed hooks and, where needed, their project configuration.

@jeff-r2026 jeff-r2026 self-assigned this Oct 2, 2026
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • [P1 blocking] src/hooks.ts:1935 treats every hook in the file whose command appears in the managed manifest as TeamAI-owned. If a user hook uses the same command as a newly added team hook—possibly under another event—the first reconciliation preserves both, then trustCodexHooks() matches both by only sourcePath + command and silently trusts the user hook too. This violates the documented guarantee that user hooks remain untrusted/untouched. Track enough identity to select the exact generated entry, such as its event/index/key or complete rendered definition.

The PR description includes a sufficient representative real-CLI verification record.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • [P1 blocking] src/codex-trust.ts:243 still identifies hooks only by sourcePath + command. In user scope, if a member already has npm run lint under one event and the team adds the same command under another, the first reconcile preserves both, but this filter selects and trusts both Codex keys. Track the generated entry’s event/index or complete definition so the member hook remains untrusted.
  • [P1 blocking] src/hooks.ts:1261 treats every existing main-checkout Codex hook whose command equals a newly desired team command as already TeamAI-managed. For example, a member’s existing npm run lint hook is removed on the first project reconcile when the team adds that command, even if its event or options differ. Ownership cannot be inferred from command equality.

The PR description includes a sufficient representative real-CLI verification record.

@SaulMoro
SaulMoro force-pushed the fix/955-codex-hook-trust branch from 2ecf408 to b177c7a Compare October 2, 2026 05:11
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • [P1 blocking] src/codex-trust.ts:243 still matches hooks only by sourcePath + command. On the first user-scope reconcile, if a member hook uses the same command as a new team hook under another event, both remain in the file and this filter trusts both Codex keys. Include event/index or the complete rendered definition in the trust request.
  • [P1 blocking] src/hooks.ts:1277 still adds every desired command to priorTeamCommands for teamOnly, and src/hooks.ts:960 consequently treats matching entries under every event as managed. If the main checkout already contains a member hook with the same command as a newly added team hook, reconciliation deletes the member hook even when its event or options differ.

Neither previously reported ownership issue is resolved in the current diff. The PR description’s representative real-CLI verification record is sufficient.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • [P1 blocking] src/hooks.ts:489 assumes projectAnchor is always a usable main checkout. For repositories managed through a bare repository, resolveAnchors() intentionally returns the bare repo path as projectAnchor; project reconciliation therefore writes .claude/ and .codex/hooks.json inside the bare repository and trusts/lists that directory, while the linked worktree never receives or loads the team hooks. Detect a bare anchor and use an actual worktree—normally the current workspaceRoot—for hook placement and Codex project trust.

  • [P1 blocking] src/hooks.ts:468 hard-codes the main-checkout hook paths instead of using the project-scope toolPaths configuration. For example, a supported configuration with toolPaths.codex.settings: .custom-codex/hooks.json writes team hooks to <main>/.codex/hooks.json; the configured Codex target remains unchanged, so its team hooks do not run. Derive these paths from the configured project-scope tool entry while retaining Claude’s intentional settings.local.json handling.

  • [P2 non-blocking] src/hooks.ts:1015 requires the stored matcher-group index to remain unchanged. If a member inserts or reorders an unrelated group before a managed custom hook, the next reconcile no longer recognizes the old entry, preserves it, and appends another copy; later updates/removal leave that stale TeamAI command behind. When the recorded position no longer matches, safely recover a unique exact-definition match before treating the record as lost.

The previously reported same-command ownership/trust issues are resolved for unchanged hook layouts. The PR description includes sufficient representative real-CLI verification.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • [P1 blocking] src/hooks.ts:1033 cannot recognize pre-upgrade Codex team hooks containing timeout or additionalContextLimit, because legacy manifest records reconstruct only the matcher and command. After upgrading, a hook such as npm run lint with timeout: 30 is preserved as supposedly user-owned and a second copy is appended; updates and removal then leave the stale hook running. Recover legacy ownership using a unique match on the fields legacy manifests actually recorded.
  • [P3 nit] src/hooks.ts:1927 handles main-checkout files only after the installed-root gate at src/hooks.ts:1920. If the member deletes or relocates their HOME tool directory while a managed project hook remains in the main checkout, teamai hooks remove skips the tool and leaves that active hook behind. The main-checkout file itself should count as an installed removal target.

Previously reported ownership, bare-repository, configured-path, and reordered-group issues are resolved. The PR description includes sufficient representative real-CLI verification.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • [P1 blocking] src/hooks.ts:2172 excludes Codex from the legacy project-root sweep because its old and new hook paths coincide, but ownership is not transferred from the legacy managed-hooks.json to managed-main-checkout-hooks.json. A user upgrading directly from the pre-fix(hooks): unify hook-injection scope so project-scope init installs the SessionStart hook #370 layout with a project Codex hook such as npm run lint keeps the old gated entry, gets a new ungated copy appended, and both execute in the main checkout; teamai hooks remove also leaves the legacy entry behind. Reconcile the existing file using the legacy manifest before switching ownership, or migrate those records into the new manifest.

  • [P2 non-blocking] src/hooks.ts:247 hard-codes <project>/.codex/hooks.json in the untrusted-project warning. With the newly supported custom toolPaths.codex.settings, this points users to the wrong file; for an MCP-only project there may be no hooks file at all. Report the actual target paths or describe the project configuration generically.

The previously reported same-command ownership/trust, bare-repository, configured-path placement, reordered-group, legacy-option, and missing-HOME removal issues are resolved. The PR description includes sufficient representative real-CLI verification.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • [P1 blocking] src/hooks.ts:1993 applies teamOnly reconciliation to the main checkout, but both Claude and Codex still classify any command containing a TEAMAI_COMMAND_MARKERS substring as managed. From a linked worktree, an existing member hook such as teamai pull --silent && ./notify in the main checkout is therefore deleted on the first reconcile despite having no manifest ownership. In teamOnly mode, ownership should come from the current/legacy manifests rather than the broad built-in marker heuristic.

The previously reported issues are otherwise resolved. The PR description includes sufficient representative real-CLI verification.

In the main checkout, classify team-only entries by exact built-ins and
manifest records instead of marker substrings, so member commands such as
'teamai pull --silent && ./notify' survive reconcile and uninstall.
Sweep a coincident legacy Codex file when Codex is excluded from the main
pass, and have uninstall reconcile that file once under both ownerships.
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • [P1 blocking] src/hooks.ts:788 identifies main-checkout Claude hooks using only event, matcher, and command from the manifest. If a member has a hook with the same command/event/matcher but different timeout or description, the first reconcile preserves it and appends TeamAI’s entry; the next reconcile classifies both as owned and deletes the member entry. Match the generated description/id or store and compare the complete generated definition, as done for Codex.

The previously reported marker-substring ownership issue is resolved. The PR description includes sufficient representative real-CLI verification.

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
  • [P3 nit] src/hooks.ts:1964 skips non-removal reconciliation when both the HOME tool root and current linked worktree tool root are absent, even if an active main-checkout hook file exists. If a member deletes ~/.claude or ~/.codex and runs teamai pull from a worktree without its own tool directory, the shared main-checkout hooks remain stale and HOME built-ins are not restored. Treat an existing mainFile as an installed target for injection/update too, not only removal.

The previously reported Claude ownership issue is resolved in the current diff. The PR description includes sufficient representative real-CLI verification.

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.

[bug] Codex never runs teamai hooks: they need manual trust, and a pull invalidates it

2 participants