teamai-cli 0.26.0 → 0.27.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teamai-cli",
3
- "version": "0.26.0",
3
+ "version": "0.27.0-beta.1",
4
4
  "description": "TeamAI — Make Every Team AI Native (skill sync + shared knowledge base, powered by Git)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -81,6 +81,7 @@
81
81
  "@types/node": "^20.17.0",
82
82
  "@types/semver": "^7.8.0",
83
83
  "@vitest/coverage-v8": "^3.2.7",
84
+ "esbuild": "^0.27.3",
84
85
  "fast-check": "^4.10.2",
85
86
  "opencode-ai": "1.18.23",
86
87
  "oxlint": "1.85.0",
@@ -90,6 +90,16 @@ says so and why.)
90
90
  session, use the name of **this** tool — do not assume Claude Code or Cursor.
91
91
  Some hosts need extra manual steps for hooks — see the troubleshooting
92
92
  reference ("Agent-specific caveats").
93
+ 5. **Team secrets: the user types the value, you run the CLI.** When the team
94
+ declares secrets (the session-start context lists them; `teamai env list`
95
+ shows them), run the CLIs that use them through `teamai env exec -- <command>`,
96
+ `--` first, so they get this team's value. It is for CLIs, not for starting
97
+ an agent: a secret named like a model profile's (`ANTHROPIC_*`) overrides it.
98
+ When a secret is missing, ask the user to run `teamai env set KEY` in their
99
+ own terminal. Never ask for a value in chat, pass one to `--stdin` or
100
+ `--secret`, read the files under `~/.teamai/secrets/`, or print one
101
+ (`teamai env exec -- env` and `printenv` do). Declaring a secret with
102
+ `teamai env add KEY --secret` takes no value, so you can run it.
93
103
 
94
104
  ## Daily commands
95
105
 
@@ -100,6 +110,7 @@ teamai status # Show local vs team differences
100
110
  teamai doctor # Diagnose configuration and hook problems
101
111
  teamai list # List resources (skills|rules|docs|env|agents|hooks|mcp)
102
112
  teamai recall <q> # Search what the team has already learned
113
+ teamai env exec -- <cmd> # Run a CLI with this directory's team env and secrets
103
114
  ```
104
115
 
105
116
  Every other command, every flag, and the flags `--help` hides live in the
@@ -109,6 +120,107 @@ generated reference below. Read it instead of guessing a flag.
109
120
  removing stale and local-only documents; an edited doc of a docs namespace you left
110
121
  is kept and named. Use a dedicated directory; preview with `--dry-run`.
111
122
 
123
+ `teamai pull` keeps a skill, rule or agent copy the user changed since teamai
124
+ delivered it, `--force` included, and names it (`Kept <path>: ...`). To share
125
+ the change, `teamai push`; when pull says the version teamai would deploy has
126
+ changed since, or push says so for that copy, merge that change into the copy first, or the push replaces it. To take the team version instead, delete
127
+ the copy and run `teamai pull --force`. The first pull after upgrading, and a
128
+ new worktree's first pull, still overwrite: nothing is recorded yet.
129
+
130
+ In project scope, `init` and `pull` also install a git hook in the repository's
131
+ local git config (`hook.teamai-post-checkout`, `hook.teamai-post-merge`; Git
132
+ 2.54+; older Git without `core.hooksPath` gets a marked block in `.git/hooks/`
133
+ scripts, and with it `teamai doctor` advises), beside any `core.hooksPath` manager or `.git/hooks` script. When a
134
+ worktree is created by `git worktree add` or an app that runs checkout hooks, it creates the project roots
135
+ of `enabledAgents` (else the ones the main checkout has) and pulls into it before
136
+ the command returns, from the team clone as last fetched when that was within
137
+ 24 h; a full pull then runs in the background. A branch switch does nothing.
138
+ After `git pull` it fetches the team repo (5 s cap, then the background pull) and
139
+ delivers; in single-repo mode it delivers what `git pull` brought, offline. It prints nothing and always
140
+ exits 0; a failure inside it is recorded, and `teamai doctor` names it (`Last git
141
+ hook run failed: ...`) with its fix, as does the next interactive `teamai pull`, once.
142
+ `teamai doctor` also reports whether the hook is installed, and why not.
143
+ `pull --dry-run` says when it would install or update the hook, writing nothing;
144
+ `teamai uninstall` removes only teamai's hook entries and blocks. For hosts that
145
+ skip checkout hooks, prepare the worktree before launch; see the new-worktree
146
+ section in `references/troubleshooting.md`.
147
+
148
+ A team agent (`agents/<name>.yaml`) can set `model: strong`, `model: fast`, or an
149
+ alias the team defines, instead of one tool's model. The team maps each alias per
150
+ tool in `models/aliases.yaml`, in that tool's own model value, with an optional effort:
151
+
152
+ ```yaml
153
+ aliases:
154
+ strong:
155
+ claude: { model: opus, effort: high }
156
+ codex: { model: gpt-6-sol, effort: high }
157
+ ```
158
+
159
+ `teamai pull` writes the mapped model into each tool's agent file, with the effort
160
+ in that tool's own field: `effort` for the Claude family, CodeBuddy, Qoder and Qoder CN,
161
+ `model_reasoning_effort` for the Codex family, `variant` for OpenCode. Cursor takes
162
+ effort inside its model string (`claude-opus-5[effort=high]`); Copilot, Kiro, WorkBuddy,
163
+ JoyCode, ZCode and OMP take none, and pull warns and drops an effort mapped for them.
164
+ Qoder CN uses the `qoder` entry; only the Claude and Codex variants and Qoder CN inherit
165
+ an entry, so never expect a `claude` model in Qoder or ZCode. A tool the alias does
166
+ not map gets no `model` and uses its default; a concrete model such as `opus` is
167
+ written as is; `tool_extras.<tool>.model` pins one tool and skips the alias. Suggest
168
+ YAML agents: a legacy `agents/<name>.md` is copied as is, so its alias is not resolved.
169
+ Before a team adds its first alias, every member updates teamai: an older CLI writes
170
+ `model: strong` literally, and its push can replace the alias with a concrete model.
171
+
172
+ A member overrides an entry on their machine in `~/.teamai/models/aliases.yaml`
173
+ (same `aliases:` shape). For one tool it replaces the team's whole entry, effort
174
+ included, and `~` or `default` gives that tool no model and no effort. Order per
175
+ tool: extras model, local entry, team entry, no model. Keys must be `strong`, `fast`
176
+ or a team alias; other names do nothing. One file serves every scope and every team
177
+ with that alias name. An ordinary `teamai pull` applies an edit. In the team file,
178
+ `default` is a literal model value and `~` is an error.
179
+
180
+ A role or project redefines an alias in `models/<ns>/aliases.yaml`, read where `<ns>`
181
+ is in `resources.models`, like `models/<ns>/models.yaml`. The namespace alias replaces
182
+ the root alias whole (a tool it does not map gets no `model`); the same alias in two
183
+ active namespaces holds alias agents, as a structural error does. A name defined in any
184
+ aliases file of the team repo, active or not, is an alias: with no active definition it
185
+ gives no `model`, and pull warns once per such alias. `teamai doctor` notes an agent's
186
+ alias defined in an inactive namespace; to use it, add `models: [<ns>]` to the role's or
187
+ project's resources, or rename the alias if it was meant as a concrete model id.
188
+
189
+ A structural error in any aliases file, active or not (bad YAML, a wrong type, a bad alias
190
+ name, an effort without a model, `~` in a team file, keys but no top-level `aliases:`)
191
+ holds every agent with a `model`; one
192
+ in the local file holds only agents whose `model` is an alias. Pull keeps
193
+ their copies and push skips them until the file its warning names is fixed; then an
194
+ ordinary `teamai pull` delivers them. An unknown
195
+ tool key, an unknown option field, or an alias named like `opus` or `inherit` is only
196
+ dropped, with a warning when an agent uses that alias; `gateways` is ignored.
197
+
198
+ On a tool switched with `teamai models switch`, alias agents get no effort (not even a
199
+ `tool_extras.<tool>` one, unless the extras also pin a `model`), and
200
+ Claude keeps only `opus`, `sonnet` or `haiku` (the switch routes those to the gateway)
201
+ while Codex, OpenCode, CodeBuddy and WorkBuddy get no `model`. No `model` means the
202
+ tool's native inheritance (for Codex, `[agents].default_subagent_model` or the parent's
203
+ model), not the profile's model. Variants such as tclaude are never switched. An ordinary
204
+ `teamai pull` after `models switch` or `models restore` rewrites the affected agents.
205
+
206
+ On push, an alias agent's model and alias effort are never read as edits: a copy that
207
+ matches the last pull or the current mapping is unedited, and a hand-edited model or
208
+ effort is reported as drift and not pushed (other edits still push), with where to change
209
+ it: the member's override file, the team aliases file the alias comes from, or `teamai models
210
+ restore --agent <tool>` for a switched tool. Writing an alias name in a deployed copy (`model: fast`)
211
+ and pushing proposes `model: <alias>`, except in a tool whose `tool_extras.<tool>.model` pins it,
212
+ where a changed value is drift on that pin. Never tell a user to push a concrete model over an alias.
213
+
214
+ To answer "why does this tool run this model", run `teamai doctor`. Each alias agent gets
215
+ a note with one line per tool: the model and effort it receives, then `[step: source]`.
216
+ `extras` = `tool_extras.<tool>.model`; `switched` = the tool runs a model profile;
217
+ `local` = the member's override (`tool default (chosen in <path>)` is their `~`/`default`);
218
+ `team` = the team file named; `default` = no model field (alias unmapped for that tool, or
219
+ no active file defines it). A Codex line with no effort means the session's effort carries
220
+ over. A line naming what "the last pull deployed" is fixed by an ordinary `teamai pull`.
221
+ The failing check `Agent model aliases can be resolved` names why agents are held (broken
222
+ aliases file, namespace conflict, unreadable switched-tool settings) and which file to fix.
223
+
112
224
  ## References
113
225
 
114
226
  In the files below, `{SKILL_DIR}` is the directory `teamai skill path core` prints; a reference file you open on its own writes that directory as `SKILL_DIR` in braces.
@@ -116,7 +228,7 @@ In the files below, `{SKILL_DIR}` is the directory `teamai skill path core` prin
116
228
  | File | When to load it |
117
229
  |---|---|
118
230
  | `{SKILL_DIR}/references/commands.md` | Before using any command not in the daily list, or any flag. Generated from the CLI's own command table, so it cannot drift. |
119
- | `{SKILL_DIR}/references/troubleshooting.md` | A command fails, a hook does not fire, or a host needs manual steps. |
231
+ | `{SKILL_DIR}/references/troubleshooting.md` | A command fails, a hook does not fire, a host needs manual steps, or a recalled doc got no upvote. |
120
232
  | `{SKILL_DIR}/references/contribute-member.md` | A member wants to publish a skill, rule or doc they already have. Any member can, not just admins. |
121
233
 
122
234
  `teamai skill get core --full` prints this skill with all three references
@@ -12,7 +12,7 @@ Generated: do not edit by hand. Regenerate with
12
12
  ## Global options
13
13
 
14
14
  - `-V, --version` — output the version number
15
- - `--dry-run` — Preview mode, no changes made
15
+ - `--dry-run` — Preview mode, no changes made; a command with no preview exits 1 without running
16
16
  - `-v, --verbose` — Verbose output
17
17
 
18
18
  ## init
@@ -28,7 +28,7 @@ Generated: do not edit by hand. Regenerate with
28
28
  - `--no-inherit-user-scope` — Disable user-scope inheritance for this project
29
29
  - `--role <id>` — Primary role ID (e.g. hai_dev) for non-interactive setup
30
30
  - `--project <ids>` — Active logical project(s) from manifest/projects.yaml (comma-separated); scopes which project resources and learnings this directory syncs. Pass "all" to activate every project the manifest declares (a snapshot taken now)
31
- - `--agent <name>` — AI tools to set up (e.g. claude, codex, cursor, codebuddy, workbuddy, dsh). Repeatable or comma-separated. In single-repo mode, selects which tool dirs to create; omit for an interactive picker. Additive on repeated runs.
31
+ - `--agent <name>` — AI tools to set up (e.g. claude, codex, cursor, codebuddy, workbuddy, dsh). Repeatable or comma-separated. In single-repo mode, selects which tool dirs to create; a custom agent defined only in teamai.yaml's toolPaths also gets its root created here (git-backed init only — an HTTP init has no local teamai.yaml to read custom paths from). Omit for an interactive picker. Additive on repeated runs.
32
32
  - `--force` — Overwrite existing config without confirmation
33
33
 
34
34
  ## push
@@ -193,13 +193,23 @@ Generated: do not edit by hand. Regenerate with
193
193
  - `--reveal` — Show env variable values in plaintext (default: masked)
194
194
  - `teamai env list` — List team environment variables
195
195
  - `--reveal` — Show env variable values in plaintext (default: masked)
196
- - `teamai env add <key> <value>` — Add or update a team environment variable
197
- - `-d, --description <desc>` — Description for the variable
198
- - `--role <ns>` — Write to env/<ns>/env.yaml instead of env/env.yaml
196
+ - `teamai env add <key> [value]` — Add or update a team environment variable, or declare a secret with --secret
197
+ - `-d, --description <desc>` — Description for the variable or secret
198
+ - `--secret` — Declare a secret in env/secrets.yaml: no value, each member sets their own
199
+ - `--url <url>` — Where a member gets a value for the secret (with --secret)
200
+ - `--role <ns>` — Write to env/<ns>/ instead of env/ (env.yaml, or secrets.yaml with --secret)
199
201
  - `--project <id>` — Write to the project's env namespace (resources.env in manifest/projects.yaml)
200
- - `teamai env remove <key>` — Remove a team environment variable
201
- - `--role <ns>` — Remove from env/<ns>/env.yaml instead of env/env.yaml
202
+ - `teamai env remove <key>` — Remove a team environment variable or declared secret
203
+ - `--secret` — Remove the declared secret only (env/secrets.yaml), for a key env.yaml also sets
204
+ - `--role <ns>` — Remove from env/<ns>/ instead of env/
202
205
  - `--project <id>` — Remove from the project's env namespace (resources.env in manifest/projects.yaml)
206
+ - `teamai env set <key>` — Set your value for a secret the team declares, or an env variable it sets, for this directory's team, on this machine (prompts without echo)
207
+ - `--stdin` — Read the value from piped stdin
208
+ - `--from-env <var>` — Read the value from this environment variable each time it is used; no copy is stored
209
+ - `--global` — Set a secret for every team on this machine; a value set for a team still wins
210
+ - `teamai env unset <key>` — Remove your value for a secret or env variable, for this directory's team, from this machine
211
+ - `--global` — Remove the value set for every team on this machine instead
212
+ - `teamai env exec <command...>` — Run a command with this directory's team env variables and secrets (put -- before the command)
203
213
 
204
214
  ## hooks
205
215
 
@@ -243,7 +253,7 @@ Generated: do not edit by hand. Regenerate with
243
253
  - `--base-url <url>` — Personal profiles: new gateway root URL
244
254
  - `--protocol <protocols>` — Personal profiles: serve models over these protocols too
245
255
  - `--model <ids>` — Personal profiles: add model IDs
246
- - `teamai models switch <profile>` — Point agents at a model profile (every compatible agent by default)
256
+ - `teamai models switch [profile]` — Point agents at a model profile (every compatible agent by default); omit the profile to pick one
247
257
  - `--agent <name>` — Only switch this agent. Repeatable or comma-separated.
248
258
  - `--model <id>` — Default model to select (defaults to the first in the profile)
249
259
  - `--dry-run` — Show what would change without writing
@@ -296,6 +306,7 @@ Generated: do not edit by hand. Regenerate with
296
306
  - `teamai recall [query...]` — Search team learnings knowledge base
297
307
  - `--depth <level>` — Recall depth: route (entry-points only) | context (module-level, default) | lookup (full graph traversal)
298
308
  - `--check` — Relevance precheck only: print RELEVANT/NOT_RELEVANT + top score; no file reads, no upvote
309
+ - `--caller <name>` (hidden) — Internal, set by the teamai-recall subagent to mark its own runs; do not pass it yourself
299
310
  - `teamai recall disable` — Disable automatic knowledge-base recall
300
311
  - `teamai recall enable` — Enable automatic knowledge-base recall
301
312
  - `teamai recall status` — Show recall feature status
@@ -363,13 +374,13 @@ Generated: do not edit by hand. Regenerate with
363
374
 
364
375
  ## review
365
376
 
366
- - `teamai review [id]` — Inspect and process .teamai/pending-review.jsonl items
377
+ - `teamai review [id]` — Inspect and process .teamai/pending-review.jsonl items; --dry-run validates apply decisions and previews rejections without writing documents or removing pending items
367
378
  - `--apply` — Apply the change for the given id (only for codebase-section)
368
379
  - `--reject` — Reject the given id without applying
369
380
  - `--reason <msg>` — Reason for reject
370
381
  - `--all-apply` — Apply all items at or below --max-risk
371
382
  - `--max-risk <level>` — Risk ceiling for --all-apply: high|medium|low (default medium)
372
- - `--json` — Machine-readable output
383
+ - `--json` — Machine-readable output (dry-run decisions include dryRun: true; ok reports validation only)
373
384
 
374
385
  ## ci
375
386
 
@@ -117,12 +117,32 @@ The doc lands in the team's `learnings/` and appears for teammates on their next
117
117
  - Teammates receive it automatically on their next session, or via `teamai pull`.
118
118
 
119
119
  Before listing rules, `push` refreshes copies whose bodies still match a recorded
120
- sync revision. Copilot's generated `applyTo` header does not count as a local
121
- edit: unedited old instructions update in native format, including under
122
- `COPILOT_HOME` in user scope. Genuine local body edits remain push candidates.
120
+ sync revision. The header teamai generates for a tool's own rules format
121
+ (Cursor `.mdc`, JoyCode's own `.mdc`, Copilot `applyTo`, Kiro `inclusion`, Qoder
122
+ `trigger`, CodeBuddy and WorkBuddy `alwaysApply`, Oh My Pi `alwaysApply`/`globs`) does not count as a local edit: unedited old copies update in that
123
+ format, including under `COPILOT_HOME` in user scope. Genuine local body edits remain push candidates.
123
124
  Rule pre-sync leaves tools excluded by `enabledAgents` or `disabledAgents` untouched.
124
125
  When only team `paths` change, `applyTo` refreshes if the local file still matches
125
126
  a recorded version's generated copy; locally edited headers are kept.
127
+ The copies push refreshes are recorded, so a later `teamai pull` still updates them.
128
+ Oh My Pi and Kiro read only the top of their rules directories, so a namespaced
129
+ rule is written flat there (`rules/fe/style.md` as `fe.style.md`); an edit of that file
130
+ pushes back to `rules/fe/style.md`. Push requires a delivery record for the flat copy.
131
+ A personal file with that name is neither refreshed before push nor offered as
132
+ an edit of the team rule.
133
+ If two namespaced rules flatten to the same name, neither is written; `teamai doctor`
134
+ reports the collision even when no other rule reaches that tool. Rename one in the
135
+ team repo, then run `teamai pull`. Doctor also reports team-owned OpenCode globs
136
+ or inline blocks left after the last rule is removed.
137
+
138
+ A new file in the rules directory of a tool with a rules format of its own
139
+ (Cursor, JoyCode, Copilot, Kiro, Qoder, CodeBuddy, WorkBuddy, Oh My Pi) is the
140
+ member's own rule in that tool's format: push never offers it, and pull leaves
141
+ it. To author a new team rule, write it as a plain `.md` in `.claude/rules/`
142
+ (scope it with `paths:` frontmatter, which teamai renders into each tool's
143
+ format), then run `teamai push`. A YAML comment after an unquoted glob stays
144
+ outside its scope: `paths: **/*.ts # TypeScript files` matches `**/*.ts`, including
145
+ when written as a block-list entry under `paths:`.
126
146
 
127
147
  ## If push is denied
128
148
 
@@ -16,8 +16,11 @@ reports before anything else.
16
16
 
17
17
  This is the #1 onboarding issue. In order:
18
18
 
19
- 1. **Open a fresh session.** Resources sync on **session start** via a hook, not
20
- at init time. An empty skills folder right after `teamai init` is normal.
19
+ 1. **Did init pick this tool?** `teamai init` ends with a pull, but a project-scope
20
+ init creates only the directories of tools named with `--agent` or picked in
21
+ its interactive tool picker. Run without a terminal and without `--agent`, it
22
+ creates none, and a tool's project directory appears when that tool opens a session there. Re-run
23
+ `teamai init <repo> --agent <tool>` to add the tool and fill it now.
21
24
  2. **Sync manually to confirm:**
22
25
  ```bash
23
26
  teamai pull
@@ -28,15 +31,30 @@ This is the #1 onboarding issue. In order:
28
31
  ```bash
29
32
  teamai hooks inject
30
33
  ```
31
- 4. **Wrong scope?** Project-scope hooks are written to your HOME tool settings
32
- (e.g. `~/.claude/settings.json`), not the project folder — that is intentional.
34
+ 4. **Wrong scope?** Project-scope built-in hooks are written to your HOME tool
35
+ settings (e.g. `~/.claude/settings.json`), not the project folder; the team's own
36
+ hooks for Claude Code and Codex go to the main checkout
37
+ (`.claude/settings.local.json`, `.codex/hooks.json`). That is intentional.
38
+ Existing Claude/Codex main-checkout hook files count as installed targets
39
+ even when HOME and current worktree tool roots are missing. Injection and
40
+ pull update team hooks and restore HOME built-ins; removal clears managed
41
+ main-checkout hooks without recreating HOME roots.
42
+ If Git-hook installation fails after writing agent hooks, `hooks inject`,
43
+ `init` and self-repo bootstrap still attempt Codex trust. Injection preserves
44
+ the installation error without reporting overall success. Init reports the
45
+ error and retains exit code 1 while completing local setup, including HTTP
46
+ initialization. Bootstrap records the error in the debug log and continues
47
+ local setup.
48
+ In project scope, `teamai hooks remove` preserves other projects' gated team
49
+ hooks in HOME, while removing the shared built-in hooks.
33
50
  If you initialized project scope but expected machine-wide resources, re-run
34
51
  with `--scope user`.
35
52
  5. **Tool has no hook surface** (e.g. Gemini CLI, JoyCode): there is no auto-sync;
36
53
  run `teamai pull` manually each time.
37
- 6. **Claude Code reads a different directory** (`CLAUDE_CONFIG_DIR` is set).
38
- `teamai doctor` reports `Claude Code root matches CLAUDE_CONFIG_DIR` when the
39
- directory the variable names is not the one this config syncs to. Re-run
54
+ 6. **Claude Code or Codex reads a different directory** (`CLAUDE_CONFIG_DIR` or
55
+ `CODEX_HOME` is set). `teamai doctor` reports `Claude Code root matches
56
+ CLAUDE_CONFIG_DIR` / `Codex root matches CODEX_HOME` when the directory the
57
+ variable names is not the one this config syncs to. Re-run
40
58
  `teamai init` from a shell that has the variable exported; it records the root
41
59
  and moves the install. If the check says the value cannot be synced to (outside
42
60
  your home, or nested deeper than `~/.config/<name>`), fix the variable first.
@@ -64,6 +82,66 @@ This is the #1 onboarding issue. In order:
64
82
  `recall` refuses the same way with `Nothing was searched: <file>: <reason>`:
65
83
  no team knowledge was searched, so do not report that the team has none.
66
84
 
85
+ ## "Last git hook run failed: ..." / a new worktree lacks team resources
86
+
87
+ In project scope, teamai's git hook syncs on `git worktree add` and `git pull`
88
+ silently and always exits 0, so its failures surface only here: `teamai doctor`
89
+ names the last one with its fix, and the next interactive `teamai pull` says it
90
+ once. The causes are a team repo fetch that failed or hit the 5 s post-merge
91
+ cap without the background pull finishing it, and another teamai process
92
+ holding the project's sync lock longer than the hook waits, or incomplete resource,
93
+ hook or MCP delivery. Only a complete startup sync clears the recorded failure.
94
+ Run `teamai pull`
95
+ in the checkout (after a stuck pull ends, or once the team repo is reachable);
96
+ `~/.teamai/debug.log` has the details. If doctor reports `Git hook syncs new
97
+ worktrees and git pull` as failing, follow its fix: `teamai pull` installs it.
98
+ Git older than 2.54 has no config hooks: teamai then adds a marked block to
99
+ `.git/hooks/post-checkout` and `post-merge`, unless `core.hooksPath` is set (or a
100
+ hook there is a symlink or not an executable shell script), in which case doctor's fix says to upgrade Git
101
+ or, if the team agrees, to commit its guarded `command -v teamai ... || true`
102
+ line into the manager's post-checkout and post-merge hooks.
103
+ Existing hook contents and permissions stay unchanged; read/write errors propagate
104
+ from `init` and `hooks inject`, and Git-started pulls record them. An unreadable
105
+ project config prevents sync and keeps its reason in `~/.teamai/debug.log`.
106
+
107
+ Hosts that skip checkout hooks need `teamai pull` in the new checkout before the AI
108
+ tool starts. For Codex CLI 0.160.0, use `git worktree add`, run `teamai pull` there, then
109
+ launch `codex exec -C <worktree>`. Its native `codex exec --worktree` path creates
110
+ the checkout without `post-checkout`, so SessionStart sync arrives after startup
111
+ discovery.
112
+
113
+ ## "KEY is not set. Run `teamai env set KEY`"
114
+
115
+ `pull`, `teamai mcp list`, `teamai env list`, `teamai doctor` and
116
+ `teamai env exec` (on stderr) print this for a secret the team declares in
117
+ `env/secrets.yaml` that has no value on this machine, naming the MCP servers
118
+ that need it and where to get one. It is a note, not a failure: `doctor` exits
119
+ as it would without it. The value is the user's: ask them to run
120
+ `teamai env set KEY` in their own terminal (it prompts without echo), then
121
+ `teamai pull` to update the MCP servers; a CLI run through `teamai env exec`
122
+ gets it on its next run. Never ask for the value in chat or pipe one to
123
+ `teamai env set --stdin`. A note that an entry "may hold an old" value means an earlier pull wrote
124
+ it and it stays until a pull finds the value.
125
+
126
+ `KEY reads VAR, which is not set` means the user's value for KEY is a
127
+ reference to VAR (`--from-env`) and VAR is unset in this environment. Ask the
128
+ user whether to set VAR in their shell or replace the reference with the
129
+ command in the line; do not choose for them.
130
+
131
+ ## "Did not write <tool>'s MCP servers to <file>" / `withheld:`
132
+
133
+ `pull` prints this, and `teamai mcp list` (`withheld:`) and `teamai doctor`
134
+ report it, when a project MCP config would get a resolved `${VAR}` value that
135
+ git would commit: the file could not be kept out of git first. It is left as
136
+ it was, and an entry an earlier pull wrote stays. The line names the reason and the
137
+ fix. For `git already tracks <file>`, tell the user: `git rm --cached <file>`
138
+ (the file stays on disk), commit that, and rotate the token if the file was
139
+ ever committed with it; then `teamai pull`. Do not run `git rm` or commit for
140
+ them. For an exclude file that is not writable, one another teamai command
141
+ held, or a git error, relay the fix the line gives.
142
+
143
+ For new HTTP local-agent MCP installs, a failed initial ownership-manifest write leaves the MCP config and Git exclusions unchanged. Retry the install after fixing the manifest write error. A bare Copilot entry beside `mcpServers` is removed only with a matching ownership record proving a completed bare write. Older records without that evidence preserve the bare entry. A bare ownership record cannot claim a same-named member entry under `mcpServers`: updates skip the collision, and removal leaves that keyed entry alone. An unmarked Copilot record needs a matching keyed hash that does not also match the bare entry. Completed writes record `bare: true` or `bare: false`; missing placement remains unproven, including after a failed placement-record write. Existing JSON MCP updates keep the old ownership until the config write completes; a later manifest failure restores the config. `uninstall_mcp` keeps ownership if reading or writing the config fails, and restores the entry if removing its manifest record fails. MCP reconcile also restores all configs written before an ownership-save failure. If restoration fails too, repair the named configs and ownership records before retrying; the error reports both failures, and configs still carrying credentials stay excluded from Git.
144
+
67
145
  ## Permission / access denied
68
146
 
69
147
  `init`, `pull`, or `push` failing with a permission error usually means the user
@@ -109,9 +187,10 @@ broken machine):
109
187
  | Tool | Hooks status | Why |
110
188
  |-----------------------|---------------------------|---------------------------------------------------------------------|
111
189
  | Claude Code (`claude`)| Installed | Fully supported — this is the main, working path |
112
- | Codex | Written but **trust-gated** or skipped | Codex gates non-managed hooks behind an explicit trust step; `teamai doctor` prints a reminder to trust them |
113
- | Cursor | Often not written | Uses its own hook mechanism; broader CLI support is still pending |
114
- | CodeBuddy / WorkBuddy | Skipped **by design** | They only accept versioned plugins (`plugin@version`); teamai writes raw entries into a `hooks` field, which they don't take |
190
+ | Codex | Installed and trusted | Codex runs only trusted hooks; teamai trusts the ones it writes through `codex app-server`, and `teamai doctor` names any Codex will not run |
191
+ | Cursor | Installed | Also runs `~/.claude/settings.json`. That copy exits only when `~/.cursor/hooks.json` or the project `.cursor/hooks.json` contains `--tool cursor` |
192
+ | Copilot CLI | Installed in self mode | Also runs a trusted project's `.claude/settings.json`. That copy exits only when `.github/hooks/teamai.json` contains `--tool copilot`. `COPILOT_CLI` alone does not skip |
193
+ | CodeBuddy / WorkBuddy | Installed | Claude-format hooks in their own `settings.json` |
115
194
 
116
195
  Practical rule: if you set up with `--agent claude`, expect **only** Claude to show
117
196
  hooks installed. A tool you are not using, or one that is not a supported hook
@@ -127,17 +206,31 @@ step — do not assume auto-sync just works.
127
206
 
128
207
  ### Codex
129
208
 
130
- Codex gates non-managed hooks behind an explicit **trust** step. `teamai init` /
131
- `teamai hooks inject` may write the hooks, but Codex won't run them until the user
132
- trusts them (`teamai doctor` prints a reminder when it detects this). Guide the
133
- user to trust the teamai hooks in Codex, then reopen a session. Until then, run
134
- `teamai pull` manually.
209
+ Codex runs a non-managed hook only once it is **trusted**. `teamai init`, `pull`
210
+ and `teamai hooks inject` trust the hooks they write (and, in a project, the main
211
+ checkout, or the current worktree for a bare repository) through `codex app-server`.
212
+ Trust written by a session-start pull applies from the next Codex session. `teamai doctor` names any teamai hook Codex will not
213
+ run. Then: run `teamai pull`; if `codex` is not on PATH or `codexTrustEnabled: false`
214
+ is set in `config.yaml`, guide the user to trust the teamai hooks in Codex `/hooks`,
215
+ then reopen a session. A new linked worktree gets the team hooks from its second
216
+ Codex session (the first creates its `.codex/`). Member hooks with the same command
217
+ are preserved and remain untouched by automatic trust. Codex ownership uses the
218
+ recorded event, position and complete entry. A moved entry is recovered only by a
219
+ unique full-definition match. Legacy records recover only a unique event, matcher
220
+ and command match; `timeout` and `additionalContextLimit` were not recorded.
221
+ Pre-#370 project Codex ownership is imported from the main checkout's
222
+ `.teamai/managed-hooks.json` before reconciliation or direct removal.
223
+ Unrecorded or ambiguous legacy team-hook copies are preserved. Project hook paths follow `toolPaths`;
224
+ Claude uses `settings.local.json` beside its configured settings file. A custom
225
+ Codex path that Codex does not load is reported as `not loaded` by doctor.
135
226
 
136
227
  ### Cursor
137
228
 
138
- Cursor uses its own hook mechanism and may not receive teamai's hooks yet. If
139
- `teamai hooks list` shows Cursor without hooks, treat it as a manual-sync tool: run
140
- `teamai pull` at the start of each session.
229
+ Cursor writes hooks to `~/.cursor/hooks.json` and also runs `~/.claude/settings.json`. `hook-dispatch --tool claude` and team hook commands written for `claude` exit only when `CURSOR_VERSION` is set and `~/.cursor/hooks.json` or `$CURSOR_PROJECT_DIR/.cursor/hooks.json` contains `--tool cursor`. A setup with only Claude has no second copy, so those hooks still run inside Cursor. Claude Code does not set `CURSOR_VERSION`. An already installed team hook picks up the guard on the next `teamai pull` or `teamai hooks inject`. If `teamai hooks list` shows Cursor without hooks, run `teamai pull` at the start of the session.
230
+
231
+ ### Copilot CLI
232
+
233
+ In self mode, teamai writes hooks into the project, and Copilot CLI runs a trusted project's `.claude/settings.json` as well as its own `.github/hooks/teamai.json`. `hook-dispatch --tool claude` and team hook commands written for `claude` exit only when `COPILOT_PROJECT_DIR` is set and that file contains `--tool copilot`. `COPILOT_CLI` is not a signal: Copilot sets it on every subprocess, including a Claude session started from its shell. Copilot does not run `~/.claude/settings.json`, so this duplicate does not happen outside self mode. Re-run `teamai pull` or `teamai hooks inject` so an already installed team hook picks up the guard.
141
234
 
142
235
  ### ChatGPT App
143
236
 
@@ -161,9 +254,63 @@ Gemini CLI, JoyCode, and similar tools have no TeamAI-writable hook surface —
161
254
  there is no auto-sync. Tell the user to run `teamai pull` manually at the start of
162
255
  each session.
163
256
 
257
+ ## "A recalled doc got no upvote"
258
+
259
+ A recalled doc is upvoted once per session when the session that ran the recall
260
+ opens it within 24 hours: a file read, a reader command (`cat`, `sed -n`, …), or
261
+ a search whose output shows its lines. Listing the doc does not count, and
262
+ neither does working from the recall subagent's summary alone; only the opt-in
263
+ judge (`TEAMAI_UPVOTE_JUDGE=1`) credits that. `teamai stats` shows each recent
264
+ session's runs, recalled docs and adopted docs. Per agent:
265
+
266
+ - **Claude Code, Codex (0.134+), CodeBuddy (2.103.1+), WorkBuddy, Qoder,
267
+ OpenCode, OMP**: both a recall the main agent runs and one the
268
+ `teamai-recall` subagent runs are credited when the main agent opens the doc.
269
+ On OMP the subagent's recall needs the main session's file on disk, so a
270
+ `--no-session` run credits only the main agent's own recalls.
271
+ - **Cursor, Copilot CLI, ZCode, Pi**: only a recall the main agent runs
272
+ itself. A subagent's recall is not linked to the main session, and Pi has no
273
+ TeamAI subagent.
274
+ - **OpenClaw, Hermes, Kiro, JoyCode**: no PostToolUse hook, so recalls never
275
+ vote.
276
+
277
+ A read after the session's last Stop is credited at SubagentStop, at Copilot CLI's
278
+ SessionEnd, or at the next `teamai pull`.
279
+
164
280
  ## Still stuck
165
281
 
166
282
  - Re-run the failing command with `-v` / `--verbose` for detail.
167
283
  - `teamai status` shows exactly how local differs from the team repo.
168
284
  - Report unexpected behavior at https://github.com/Tencent/teamai-cli/issues
169
285
  with the agent name, platform, and the step that failed.
286
+
287
+ ## "Pull left an instruction file unchanged"
288
+
289
+ If pull reports incomplete TeamAI markers, it keeps the entire file unchanged.
290
+ Fix the named block so it has exactly one start marker followed by one end
291
+ marker, then run `teamai pull` again. Other files can still sync successfully.
292
+
293
+ Pull keeps retired instruction blocks until every installed tool that wrote the
294
+ file has a working replacement. Repair the named target, extension or plugin
295
+ and run `teamai pull` again. Excluded tools' current and retired files stay
296
+ unchanged and are excluded from doctor's stale-instruction check.
297
+ When a native project file retains a TeamAI block, the session hook skips that
298
+ block, including cached HTTP prompts, until cleanup succeeds. Other blocks
299
+ still reach the hook. Doctor reports malformed markers in retired files;
300
+ repair them before retrying pull.
301
+ If a block's source cannot be resolved, its old block stays even when other
302
+ blocks sync. Repair the source and pull again to complete its migration.
303
+ HTTP prompt commands verify earlier deliveries against the current prompt
304
+ before cleaning shared instructions. Older destination contents do not count;
305
+ culture and recall stay because HTTP prompt commands do not replace them.
306
+ An HTTP prompt sync that cannot clean retired blocks reports a failed ACK and
307
+ keeps its previous cache and manifest for the server's retry.
308
+ OpenClaw HTTP prompts require an existing resolved user workspace, but not an
309
+ existing `AGENTS.md`: the prompt sync creates that file and preserves personal
310
+ text already in it.
311
+
312
+ OpenCode registration saves ownership before activating a new config entry.
313
+ If the state write fails, repair the state directory's permissions and retry
314
+ `teamai pull`; the entry is not activated without its removal ownership.
315
+ If the config write fails, ownership stays available for retry. Entries the
316
+ member already listed are never claimed.
@@ -46,13 +46,17 @@ and create-repo URLs, and the per-provider caveats, and points at
46
46
  install. Let `teamai init` set up every AI tool already installed (omitting
47
47
  `--agent` gives an interactive picker; select all detected tools). **After init,
48
48
  report which agents were set up** — in the user's language, which tools now
49
- auto-start TeamAI, and which detected tools were skipped and why (e.g. Codex
50
- trust-gate, CodeBuddy design). Verify the real per-tool result with
49
+ auto-start TeamAI, which detected tools were skipped and why (e.g. CodeBuddy
50
+ design), and any installed hooks that still need trust (e.g. Codex with
51
+ automatic trust disabled or unavailable). Verify the real per-tool result with
51
52
  `teamai doctor` and `teamai hooks list`.
52
- 3. **After init, resources appear on the NEXT session.** `teamai init` injects a
53
- session-start hook that auto-runs `teamai pull`. Empty skills/rules directories
54
- right after init are normal; they fill in when the user opens a fresh session in
55
- this tool. To sync immediately, run `teamai pull`.
53
+ 3. **`teamai init` ends with a pull.** In user scope, and in project scope for each
54
+ tool named with `--agent` (or picked in init's tool picker when a person runs it
55
+ in a terminal), the team's skills, rules and MCP servers are in place when init
56
+ exits; there is no need to run `teamai pull` after it. Run from an agent shell
57
+ (no terminal), a project-scope init without `--agent` creates no tool directory: a tool's directory appears and
58
+ fills when the user opens that tool in the project. Init also injects a
59
+ session-start hook that keeps resources synced from then on.
56
60
  4. **Finish with `teamai doctor`.** Every setup or onboarding flow ends by running
57
61
  it and resolving what it reports before you call the job done.
58
62
 
@@ -69,6 +69,15 @@ to add them to the repo.
69
69
 
70
70
  ## Step 4 — Initialize with the URL (you run it)
71
71
 
72
+ Before running `init`, ask whether the user wants to activate any logical
73
+ projects this team repo declares. If `init` lists **Available projects**, show
74
+ the names/IDs and ask which belong to this setup; enter the corresponding
75
+ comma-separated numbers. Press Enter for none only when the user explicitly
76
+ chooses no project. If the IDs are already known, pass `--project id1,id2` to
77
+ skip the picker. For a non-interactive run, ask first and pass `--project`:
78
+ without it, init keeps `projects: []` and prints a `teamai projects set <id>`
79
+ follow-up instead of waiting for a choice.
80
+
72
81
  ```bash
73
82
  # this project only (run from inside the project)
74
83
  teamai init https://<platform>/<org>/<repo>
@@ -93,13 +102,14 @@ teamai init --http https://your-team-host/api --token <api-key>
93
102
  This is a read-only consumer mode — `push` / `contribute` are not available, but
94
103
  skills and rules still sync.
95
104
 
96
- **Claude Code kept in a different directory (`CLAUDE_CONFIG_DIR`):** `init` records
97
- that directory (as `toolRoots.claude` in the local config) and syncs every Claude
98
- path there, so run `init` from a shell that has the variable exported. Re-running
99
- `init` after changing it moves the install (the old root's hooks, managed MCP
100
- servers and delivered model credentials are removed; its skills and rules are left
101
- and named in the output). To end the relocation, run `init` once with the variable
102
- set but blank: `CLAUDE_CONFIG_DIR= teamai init …`.
105
+ **Claude Code or Codex kept in a different directory (`CLAUDE_CONFIG_DIR`,
106
+ `CODEX_HOME`):** `init` records that directory (as `toolRoots.claude` /
107
+ `toolRoots.codex` in the local config) and syncs every path of that tool there,
108
+ so run `init` from a shell that has the variable exported. Re-running `init` after
109
+ changing it moves the install (the old root's hooks and managed MCP servers are
110
+ removed, and for Claude Code its delivered model credentials; its skills and rules
111
+ are left and named in the output). To end the relocation, run `init` once with the
112
+ variable set but blank: `CLAUDE_CONFIG_DIR= teamai init …` or `CODEX_HOME= teamai init …`.
103
113
 
104
114
  ## Step 5 — Verify with doctor
105
115
 
@@ -116,11 +126,14 @@ section "Which tools actually get hooks".
116
126
 
117
127
  ## Step 6 — Confirm the skills actually arrived
118
128
 
119
- Team resources sync on **session start**, so they may be empty right after init.
120
- To confirm now:
129
+ `teamai init` ends with a pull, so the team's skills, rules and MCP servers are
130
+ already in place in user scope, and in project scope for each tool named with
131
+ `--agent` or picked in init's interactive tool picker. A project-scope init run
132
+ without a terminal and without `--agent` creates no tool directory; a
133
+ tool's directory fills when the user first opens that tool in the project. To
134
+ confirm:
121
135
 
122
136
  ```bash
123
- teamai pull # sync immediately
124
137
  teamai list # see the team skills / rules / docs you now have
125
138
  ```
126
139