teamai-cli 0.26.0-beta.5 → 0.27.0-beta.0

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-beta.5",
3
+ "version": "0.27.0-beta.0",
4
4
  "description": "TeamAI — Make Every Team AI Native (skill sync + shared knowledge base, powered by Git)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,7 +28,7 @@
28
28
  "test:watch": "vitest",
29
29
  "test:coverage": "vitest run --coverage",
30
30
  "typecheck": "tsc --noEmit",
31
- "lint": "oxlint --deny-warnings --report-unused-disable-directives",
31
+ "lint": "oxlint --type-aware --deny-warnings --report-unused-disable-directives",
32
32
  "release": "standard-version",
33
33
  "prepublishOnly": "npm run build"
34
34
  },
@@ -84,6 +84,7 @@
84
84
  "fast-check": "^4.10.2",
85
85
  "opencode-ai": "1.18.23",
86
86
  "oxlint": "1.85.0",
87
+ "oxlint-tsgolint": "7.0.2003",
87
88
  "standard-version": "^9.5.0",
88
89
  "tsup": "^8.3.0",
89
90
  "typescript": "^5.7.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,89 @@ 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
+ A team agent (`agents/<name>.yaml`) can set `model: strong`, `model: fast`, or an
131
+ alias the team defines, instead of one tool's model. The team maps each alias per
132
+ tool in `models/aliases.yaml`, in that tool's own model value, with an optional effort:
133
+
134
+ ```yaml
135
+ aliases:
136
+ strong:
137
+ claude: { model: opus, effort: high }
138
+ codex: { model: gpt-6-sol, effort: high }
139
+ ```
140
+
141
+ `teamai pull` writes the mapped model into each tool's agent file, with the effort
142
+ in that tool's own field: `effort` for the Claude family, CodeBuddy, Qoder and Qoder CN,
143
+ `model_reasoning_effort` for the Codex family, `variant` for OpenCode. Cursor takes
144
+ effort inside its model string (`claude-opus-5[effort=high]`); Copilot, Kiro, WorkBuddy,
145
+ JoyCode, ZCode and OMP take none, and pull warns and drops an effort mapped for them.
146
+ Qoder CN uses the `qoder` entry; only the Claude and Codex variants and Qoder CN inherit
147
+ an entry, so never expect a `claude` model in Qoder or ZCode. A tool the alias does
148
+ not map gets no `model` and uses its default; a concrete model such as `opus` is
149
+ written as is; `tool_extras.<tool>.model` pins one tool and skips the alias. Suggest
150
+ YAML agents: a legacy `agents/<name>.md` is copied as is, so its alias is not resolved.
151
+ Before a team adds its first alias, every member updates teamai: an older CLI writes
152
+ `model: strong` literally, and its push can replace the alias with a concrete model.
153
+
154
+ A member overrides an entry on their machine in `~/.teamai/models/aliases.yaml`
155
+ (same `aliases:` shape). For one tool it replaces the team's whole entry, effort
156
+ included, and `~` or `default` gives that tool no model and no effort. Order per
157
+ tool: extras model, local entry, team entry, no model. Keys must be `strong`, `fast`
158
+ or a team alias; other names do nothing. One file serves every scope and every team
159
+ with that alias name. An ordinary `teamai pull` applies an edit. In the team file,
160
+ `default` is a literal model value and `~` is an error.
161
+
162
+ A role or project redefines an alias in `models/<ns>/aliases.yaml`, read where `<ns>`
163
+ is in `resources.models`, like `models/<ns>/models.yaml`. The namespace alias replaces
164
+ the root alias whole (a tool it does not map gets no `model`); the same alias in two
165
+ active namespaces holds alias agents, as a structural error does. A name defined in any
166
+ aliases file of the team repo, active or not, is an alias: with no active definition it
167
+ gives no `model`, and pull warns once per such alias. `teamai doctor` notes an agent's
168
+ alias defined in an inactive namespace; to use it, add `models: [<ns>]` to the role's or
169
+ project's resources, or rename the alias if it was meant as a concrete model id.
170
+
171
+ A structural error in any aliases file, active or not (bad YAML, a wrong type, a bad alias
172
+ name, an effort without a model, `~` in a team file, keys but no top-level `aliases:`)
173
+ holds every agent with a `model`; one
174
+ in the local file holds only agents whose `model` is an alias. Pull keeps
175
+ their copies and push skips them until the file its warning names is fixed; then an
176
+ ordinary `teamai pull` delivers them. An unknown
177
+ tool key, an unknown option field, or an alias named like `opus` or `inherit` is only
178
+ dropped, with a warning when an agent uses that alias; `gateways` is ignored.
179
+
180
+ On a tool switched with `teamai models switch`, alias agents get no effort (not even a
181
+ `tool_extras.<tool>` one, unless the extras also pin a `model`), and
182
+ Claude keeps only `opus`, `sonnet` or `haiku` (the switch routes those to the gateway)
183
+ while Codex, OpenCode, CodeBuddy and WorkBuddy get no `model`. No `model` means the
184
+ tool's native inheritance (for Codex, `[agents].default_subagent_model` or the parent's
185
+ model), not the profile's model. Variants such as tclaude are never switched. An ordinary
186
+ `teamai pull` after `models switch` or `models restore` rewrites the affected agents.
187
+
188
+ On push, an alias agent's model and alias effort are never read as edits: a copy that
189
+ matches the last pull or the current mapping is unedited, and a hand-edited model or
190
+ effort is reported as drift and not pushed (other edits still push), with where to change
191
+ it: the member's override file, the team aliases file the alias comes from, or `teamai models
192
+ restore --agent <tool>` for a switched tool. Writing an alias name in a deployed copy (`model: fast`)
193
+ and pushing proposes `model: <alias>`, except in a tool whose `tool_extras.<tool>.model` pins it,
194
+ where a changed value is drift on that pin. Never tell a user to push a concrete model over an alias.
195
+
196
+ To answer "why does this tool run this model", run `teamai doctor`. Each alias agent gets
197
+ a note with one line per tool: the model and effort it receives, then `[step: source]`.
198
+ `extras` = `tool_extras.<tool>.model`; `switched` = the tool runs a model profile;
199
+ `local` = the member's override (`tool default (chosen in <path>)` is their `~`/`default`);
200
+ `team` = the team file named; `default` = no model field (alias unmapped for that tool, or
201
+ no active file defines it). A Codex line with no effort means the session's effort carries
202
+ over. A line naming what "the last pull deployed" is fixed by an ordinary `teamai pull`.
203
+ The failing check `Agent model aliases can be resolved` names why agents are held (broken
204
+ aliases file, namespace conflict, unreadable switched-tool settings) and which file to fix.
205
+
112
206
  ## References
113
207
 
114
208
  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 +210,7 @@ In the files below, `{SKILL_DIR}` is the directory `teamai skill path core` prin
116
210
  | File | When to load it |
117
211
  |---|---|
118
212
  | `{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. |
213
+ | `{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
214
  | `{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
215
 
122
216
  `teamai skill get core --full` prints this skill with all three references
@@ -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
 
@@ -262,7 +272,7 @@ Generated: do not edit by hand. Regenerate with
262
272
 
263
273
  - `teamai session` — Record and inspect coding-session summaries
264
274
  - `teamai session save` — Record a privacy-scrubbed summary of a coding session to a local monthly log
265
- - `--session-id <id>` — Session to record (default: most recent, or $CLAUDE_SESSION_ID)
275
+ - `--session-id <id>` — Session to record (default: the agent's session, e.g. $CLAUDE_CODE_SESSION_ID, or the most recent)
266
276
  - `--push` — Also push the summary to the team repo (feeds `teamai digest`)
267
277
  - `--force` — Push even if the session is not flagged as valuable
268
278
  - `--include-prompt` — Include the redacted first-prompt line in the pushed summary (default: off)
@@ -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
 
@@ -123,6 +123,7 @@ edit: unedited old instructions update in native format, including under
123
123
  Rule pre-sync leaves tools excluded by `enabledAgents` or `disabledAgents` untouched.
124
124
  When only team `paths` change, `applyTo` refreshes if the local file still matches
125
125
  a recorded version's generated copy; locally edited headers are kept.
126
+ The copies push refreshes are recorded, so a later `teamai pull` still updates them.
126
127
 
127
128
  ## If push is denied
128
129
 
@@ -34,9 +34,10 @@ This is the #1 onboarding issue. In order:
34
34
  with `--scope user`.
35
35
  5. **Tool has no hook surface** (e.g. Gemini CLI, JoyCode): there is no auto-sync;
36
36
  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
37
+ 6. **Claude Code or Codex reads a different directory** (`CLAUDE_CONFIG_DIR` or
38
+ `CODEX_HOME` is set). `teamai doctor` reports `Claude Code root matches
39
+ CLAUDE_CONFIG_DIR` / `Codex root matches CODEX_HOME` when the directory the
40
+ variable names is not the one this config syncs to. Re-run
40
41
  `teamai init` from a shell that has the variable exported; it records the root
41
42
  and moves the install. If the check says the value cannot be synced to (outside
42
43
  your home, or nested deeper than `~/.config/<name>`), fix the variable first.
@@ -64,6 +65,38 @@ This is the #1 onboarding issue. In order:
64
65
  `recall` refuses the same way with `Nothing was searched: <file>: <reason>`:
65
66
  no team knowledge was searched, so do not report that the team has none.
66
67
 
68
+ ## "KEY is not set. Run `teamai env set KEY`"
69
+
70
+ `pull`, `teamai mcp list`, `teamai env list`, `teamai doctor` and
71
+ `teamai env exec` (on stderr) print this for a secret the team declares in
72
+ `env/secrets.yaml` that has no value on this machine, naming the MCP servers
73
+ that need it and where to get one. It is a note, not a failure: `doctor` exits
74
+ as it would without it. The value is the user's: ask them to run
75
+ `teamai env set KEY` in their own terminal (it prompts without echo), then
76
+ `teamai pull` to update the MCP servers; a CLI run through `teamai env exec`
77
+ gets it on its next run. Never ask for the value in chat or pipe one to
78
+ `teamai env set --stdin`. A note that an entry "may hold an old" value means an earlier pull wrote
79
+ it and it stays until a pull finds the value.
80
+
81
+ `KEY reads VAR, which is not set` means the user's value for KEY is a
82
+ reference to VAR (`--from-env`) and VAR is unset in this environment. Ask the
83
+ user whether to set VAR in their shell or replace the reference with the
84
+ command in the line; do not choose for them.
85
+
86
+ ## "Did not write <tool>'s MCP servers to <file>" / `withheld:`
87
+
88
+ `pull` prints this, and `teamai mcp list` (`withheld:`) and `teamai doctor`
89
+ report it, when a project MCP config would get a resolved `${VAR}` value that
90
+ git would commit: the file could not be kept out of git first. It is left as
91
+ it was, and an entry an earlier pull wrote stays. The line names the reason and the
92
+ fix. For `git already tracks <file>`, tell the user: `git rm --cached <file>`
93
+ (the file stays on disk), commit that, and rotate the token if the file was
94
+ ever committed with it; then `teamai pull`. Do not run `git rm` or commit for
95
+ them. For an exclude file that is not writable, one another teamai command
96
+ held, or a git error, relay the fix the line gives.
97
+
98
+ 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.
99
+
67
100
  ## Permission / access denied
68
101
 
69
102
  `init`, `pull`, or `push` failing with a permission error usually means the user
@@ -111,7 +144,7 @@ broken machine):
111
144
  | Claude Code (`claude`)| Installed | Fully supported — this is the main, working path |
112
145
  | 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
146
  | 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 |
147
+ | CodeBuddy / WorkBuddy | Installed | Claude-format hooks in their own `settings.json` |
115
148
 
116
149
  Practical rule: if you set up with `--agent claude`, expect **only** Claude to show
117
150
  hooks installed. A tool you are not using, or one that is not a supported hook
@@ -161,6 +194,27 @@ Gemini CLI, JoyCode, and similar tools have no TeamAI-writable hook surface —
161
194
  there is no auto-sync. Tell the user to run `teamai pull` manually at the start of
162
195
  each session.
163
196
 
197
+ ## "A recalled doc got no upvote"
198
+
199
+ A recalled doc is upvoted once per session when the session that ran the recall
200
+ opens it within 24 hours: a file read, a reader command (`cat`, `sed -n`, …), or
201
+ a search whose output shows its lines. Listing the doc does not count, and
202
+ neither does working from the recall subagent's summary alone; only the opt-in
203
+ judge (`TEAMAI_UPVOTE_JUDGE=1`) credits that. `teamai stats` shows each recent
204
+ session's runs, recalled docs and adopted docs. Per agent:
205
+
206
+ - **Claude Code, Codex (0.134+), CodeBuddy (2.103.1+), WorkBuddy, Qoder,
207
+ OpenCode**: both a recall the main agent runs and one the `teamai-recall`
208
+ subagent runs are credited when the main agent opens the doc.
209
+ - **Cursor, Copilot CLI, ZCode, OMP, Pi**: only a recall the main agent runs
210
+ itself. A subagent's recall is not linked to the main session, and Pi has no
211
+ TeamAI subagent.
212
+ - **OpenClaw, Hermes, Kiro, JoyCode**: no PostToolUse hook, so recalls never
213
+ vote.
214
+
215
+ A read after the session's last Stop is credited at SubagentStop, at Copilot CLI's
216
+ SessionEnd, or at the next `teamai pull`.
217
+
164
218
  ## Still stuck
165
219
 
166
220
  - Re-run the failing command with `-v` / `--verbose` for detail.
@@ -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
 
@@ -31,12 +31,58 @@ run `teamai pull`).
31
31
  ```bash
32
32
  teamai mcp list # team MCP servers + per-tool install status
33
33
  teamai mcp inject # push team MCP servers into every AI tool's config
34
+ teamai mcp remove --dry-run # preview removal without changing tool configs or managed records
34
35
  teamai mcp remove # remove teamai-managed MCP servers
35
36
  ```
36
37
 
37
38
  MCP definitions travel with the team repo like skills/rules — edit, then the
38
39
  members pick them up on sync.
39
40
 
41
+ A server with a `${VAR}` the tool cannot expand itself gets the resolved value
42
+ written into its project config (`.mcp.json`, `.cursor/mcp.json`, ...). Before
43
+ that write, teamai lists the file in the clone's `.git/info/exclude`, inside a
44
+ `# [teamai:mcp-exclude:start]` block; the committed `.gitignore` is never touched.
45
+ A file under a symlinked directory is listed and checked where the write lands
46
+ (`.cursor/` linking to `config/`: `/config/mcp.json`); a symlink at the file
47
+ itself is replaced by the write.
48
+ When it cannot (git already tracks the file, a rule in the member's git ignore
49
+ files re-includes it, `.git/info` is not writable, the exclude file is held by
50
+ another teamai command, or git errors), it leaves the file as it was, warns, and
51
+ `teamai mcp list` shows `withheld: <tool> — <reason>. <fix>`. Apply the fix it
52
+ names (a tracked file: `git rm --cached <file>` and rotate the token; a
53
+ re-including rule such as `!/.mcp.json`: remove it), then run `teamai pull`. A pull or `teamai mcp remove` takes a line out
54
+ once its file no longer holds a resolved value; `teamai uninstall` does so in
55
+ every worktree. A file written under a `toolPaths.<tool>.mcpProject` the team
56
+ later changes or removes stays listed until it is deleted or holds no server;
57
+ for one an older teamai wrote, the first pull finds the path in the team repo's
58
+ history of `teamai.yaml`, or among the built-in paths teamai has since changed
59
+ (not one the same tool maps today), and lists it while it holds any server; one
60
+ git tracks is recorded instead and listed once the member runs `git rm --cached`
61
+ on it. `teamai doctor` checks those paths until that pull. A file written for a
62
+ tool the team moved elsewhere (recorded, or found in that history), that another
63
+ tool still maps, stays listed while it holds a server that tool did not write,
64
+ one of the member's own included. The built-in location of a tool the team drops
65
+ from `toolPaths` or moves elsewhere stays listed while it holds any server; one
66
+ another tool maps today (CodeBuddy's `.mcp.json`, which Claude maps) while it
67
+ holds a server that tool did not write. A file two tools map, with no pull on
68
+ this version having recorded it, needs a `managed-mcp.json` record from each of
69
+ them. While a worktree has no `managed-mcp.json` at all (lost, or before its
70
+ first pull), an untracked config holding a server no record claims is listed,
71
+ and that server noted: it keeps the line until it leaves the file. So is the
72
+ file of a tool `managed-mcp.json` has no record for, when a pull writes that
73
+ tool's first record (its record lost, or teamai's first delivery to it). While that
74
+ note cannot be written (another teamai command holds the record), the line stays
75
+ until a later pull writes it. A Copilot project config's bare top-level servers
76
+ still count once another tool writes `mcpServers` into the file. On an HTTP-backed
77
+ team the local agent's `install_mcp` lists a project config before writing a
78
+ server with any header, env value, argument or URL (only a bare stdio command is not), fails the install when it cannot, and only
79
+ `teamai uninstall` takes that line out. The next sync or `teamai pull` in the
80
+ workspace also lists a file an older local agent wrote a credential into; `teamai doctor` checks those files too.
81
+
82
+ ### Pi MCP delivery
83
+
84
+ Pi 0.99.0+ receives stdio and streamable HTTP servers through the existing MCP commands and `teamai pull`; SSE is skipped. User scope writes `~/.pi/agent/mcp.json`, project scope writes `.pi/mcp.json` (Pi requires project trust). TeamAI keeps Pi's default codemode exposure and converts timeout milliseconds to seconds. Relocated Pi agent directories (`PI_CODING_AGENT_DIR` / `PI_CONFIG_DIR`) are unsupported. Local exposure/enabled edits on managed servers survive until the team definition changes; doctor reports differences from the team entry. Extensions that replace `/mcp` must be removed to use Pi's built-in MCP.
85
+
40
86
  ## Invite a member
41
87
 
42
88
  There is **no CLI invite flag.** Inviting is done on the Git platform's website:
@@ -150,10 +196,12 @@ teamai push # share the updated teamai.yaml
150
196
 
151
197
  ```bash
152
198
  teamai env list # what reaches this directory, each with its namespace (values masked)
153
- teamai env list --reveal # show values in plaintext
199
+ teamai env list --reveal # show variable values in plaintext (never a secret's)
154
200
  teamai env add <KEY> <VALUE> # add or update in env/env.yaml
155
201
  teamai env add <KEY> <VALUE> --project <id> # or --role <ns>: in that namespace's env/<ns>/env.yaml (warns if nothing declares <ns>)
156
202
  teamai env remove <KEY> # remove (same --role / --project)
203
+ teamai env add <KEY> --secret -d "<what it is for>" --url <where to get one> # declare a secret in env/secrets.yaml, no value (same --role / --project)
204
+ teamai env remove <KEY> --secret # remove a declared secret (plain `env remove` does too when env.yaml does not set <KEY>)
157
205
  teamai remove mcp <name> # root mcp/mcp.yaml if it has the name, else the one namespace file; --role / --project pick a namespace
158
206
  ```
159
207
 
@@ -171,17 +219,38 @@ and push it with git. `teamai doctor` lists each override.
171
219
  state is kept. Fix the file the warning names. A hooks or MCP file with none of
172
220
  its top-level keys (`server:` for `servers:`) counts as one that does not parse.
173
221
  - Per-entry `projects:` (and `roles:` on env) no longer works: such an entry reaches
174
- nobody. `roles:` on hooks and MCP still filters for one more minor release. Pull
175
- and `teamai doctor` name the namespace file each entry belongs in; move it there.
222
+ nobody. `roles:` on hooks and MCP still filters for one more minor release. Pull,
223
+ the list commands (`teamai env list`, `teamai mcp list`, `teamai hooks list`,
224
+ `teamai list <env|hooks|mcp> --source repo`), `teamai status` and
225
+ `teamai doctor` name the namespace file each entry belongs in; move it there.
226
+ When `teamai env add` updates a variable carrying either removed key, it keeps
227
+ the key and warns that pull will not deliver the variable, naming that file.
176
228
  - An env, hook or MCP entry with a key its schema does not know (a mistyped `role:`)
177
- also reaches nobody. Pull and `teamai doctor` name the file, entry and key; correct
178
- the key or remove it. A key a later teamai version adds is unknown to an older one,
179
- so upgrade every member before the team uses a new entry key.
229
+ also reaches nobody. Pull, the list commands, `teamai status` and
230
+ `teamai doctor` name the file, entry and key; correct the key or remove it.
231
+ A key a later teamai version adds is unknown to an older one, so upgrade every
232
+ member before the team uses a new entry key.
180
233
  - Team model profiles work the same way: `models/<ns>/models.yaml`, declared under
181
234
  `resources.models`, replaces the root profile with the same `id` for members who
182
235
  have `<ns>` active. A member's API key is bound to the profile's gateway origin:
183
236
  when an override points at another host, their pull leaves the agent alone and
184
237
  asks them to run `teamai models switch team:<id>` to set the key for it.
238
+ - Secrets are declared with no value in `env/secrets.yaml` or `env/<ns>/secrets.yaml`
239
+ (active through `resources.env`; a namespace entry replaces the root entry with the
240
+ same key): a `secrets:` list of `key`, optional `description` and optional `url`
241
+ (where a member gets one). Never put a value there: `teamai env add <KEY> --secret`
242
+ takes none and rejects one. Declare with it or edit the file in the team repo;
243
+ `teamai push` picks it up. Each member sets their own value with `teamai env set KEY`
244
+ in their terminal (`--global` for every team on their machine; a team value still
245
+ wins). `teamai env list` shows each secret as `team`, `global`, `environment`,
246
+ `missing` or `unreadable` and never shows a value, `--reveal` included. The `description` is what
247
+ agents see: the session-start hook lists each declared key with it and tells the
248
+ agent to run the CLIs that need them through `teamai env exec --`, so say which
249
+ tool or server uses the key. As the agent, run `teamai env add <KEY> --secret`
250
+ yourself and leave the value to each member's own terminal. A key declared as a secret
251
+ and also set in `env.yaml` is a secret: its `env.yaml` value is not delivered. A
252
+ secrets file that does not parse keeps `env.sh` and MCP servers as they were, and
253
+ `teamai doctor` fails a check naming the file.
185
254
  - Have every member upgrade before declaring `env`, `hooks`, `mcp`, `models` or `docs` in a
186
255
  manifest: teamai 0.25.0 and the 0.26.0 betas reject those keys and their pull stops.
187
256
 
@@ -165,6 +165,15 @@ suggested form `TeamAi-<team-name>`.)
165
165
 
166
166
  Use the **full URL**, never `owner/repo`:
167
167
 
168
+ Ask whether the user wants to activate any logical projects this team repo
169
+ declares. If `init` lists **Available projects**, show the names/IDs and ask
170
+ which belong to this setup; enter the corresponding comma-separated numbers.
171
+ Press Enter for none only when the user explicitly chooses no project. If the
172
+ IDs are already known, pass `--project id1,id2` to skip the picker. For a
173
+ non-interactive run, ask first and pass `--project`: without it, init keeps
174
+ `projects: []` and prints a `teamai projects set <id>` follow-up instead of
175
+ waiting for a choice.
176
+
168
177
  ```bash
169
178
  # project scope (default) — run from inside the project directory
170
179
  teamai init https://<platform>/<org>/<repo-name>
@@ -55,3 +55,16 @@ and give it your team repo URL."*
55
55
  and neither should you.
56
56
  - If the user only wants to stop auto-sync for one tool but keep TeamAI otherwise,
57
57
  that is the `--agent <tool>` form, not a full uninstall.
58
+ - In a project, uninstall also takes teamai's lines out of `.git/info/exclude`
59
+ (the `# [teamai:mcp-exclude:start]` block) for MCP configs it proves hold no
60
+ resolved `${VAR}` value. A line names the path a write lands in: for a config
61
+ under a symlinked directory, the link's target (`/config/mcp.json` for
62
+ `.cursor/` linking to `config/`). For one it cannot prove clean (including one written
63
+ under a `toolPaths` mapping since changed, at the built-in location of a tool
64
+ the team dropped or moved that no other tool maps, or in a nested repository's
65
+ linked worktree, that still holds servers, and one written for a tool since moved
66
+ (or at its built-in location) that another tool maps, holding a server that tool
67
+ did not write) it keeps the line
68
+ and warns, naming the file and why: have the user remove teamai's servers from
69
+ that file, then delete the line (with the last one, the block's markers). Do not
70
+ delete a kept line while its file still holds a token.