Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ All notable changes to this project will be documented in this file. See [standa

### 🐛 Bug Fixes

- Team hooks stay out of projects that never set up teamai. A project-scope install puts its hooks in the home directory, so they fire in every project on the machine, and with no config for the directory they used to run anyway: the end-of-session share reminder (shown there even with recall off, a case a configured team never sees), the TodoWrite recall nudge, and the local recording of sessions and skill usage that a later report from another project pushed to its team. A handler that needs a team now declares `requiresConfig`, and the dispatcher drops it when neither a project nor a user config resolves for the hook's `cwd`; only machine-level work runs there (CLI update check, session-start pull, local agent, package hints the pull stashed). A config that exists but fails to parse reads the same way, so it withholds team prompts rather than running all of them, and for hooks and skill usage an unreadable project config never falls back to the user scope; `teamai doctor` reports it. A host that sends no `cwd` (OpenClaw) resolves the project from the directory it runs the hook in, a `cwd` that no longer exists resolves to the user scope instead of failing the hook, and the legacy `teamai contribute-check` command that older installs still call follows the same rule (for [#748](https://github.com/Tencent/teamai-cli/issues/748)).
- Skill usage stays with the team of the project it was recorded in. Every scope used to append to one `~/.teamai/usage.jsonl`, so whichever project pulled next reported every project's skills to its own team, including skills that exist only in an unrelated private repo. Usage now goes to the data directory of the scope that resolves for the session's directory (`<dataHome>/usage.jsonl`: the project partition, `<repo>/.teamai` in single-repo mode, `~/.teamai` for the user scope), each report reads and truncates only its own file, and `teamai stats` shows the current scope's usage. A machine's first user scope starts with an empty file, since the events it held by then cannot be attributed; on a machine with only project scopes, events recorded before the upgrade stay unreported. Stats already pushed are not rewritten (for [#748](https://github.com/Tencent/teamai-cli/issues/748)).
- `teamai init` no longer hangs without a terminal. When the provider had no session it spawned `gh auth login --web` (or `gf auth login`, `cnb login`) with inherited stdio and waited for a browser device flow that nobody could complete, about five minutes for GitHub, then exited with the provider's error and no hint of the missing credential. Each login now refuses up front when the run is not interactive and names the credential to prepare (`GITHUB_TOKEN` / `GH_TOKEN`, `CNB_TOKEN`, or for TGit a prior `gf auth login`, since a `TGIT_TOKEN` PAT is REST-API-only and cannot clone). A run is non-interactive when stdin is not a TTY or when `CI` or `TEAMAI_NONINTERACTIVE` is set, so an agent sandbox with a pseudo-terminal can declare itself unattended, and every prompt in the CLI follows the same rule. `git` also runs with its prompts closed in that case — `GIT_TERMINAL_PROMPT=0`, `GIT_ASKPASS=echo` and `GCM_INTERACTIVE=never`, each only where the caller set nothing — so a missing clone credential fails at once instead of waiting on a terminal prompt or an askpass or credential-manager dialog. `ssh` keeps its own settings: its batch flag is only reachable through `GIT_SSH_COMMAND`, which would override each repository's `core.sshCommand` (for [#711](https://github.com/Tencent/teamai-cli/issues/711)).
- A `manifest/roles.yaml` that exists but does not parse now fails the pull for that scope instead of warning and syncing with no role filter at all, for a member with no role as much as for one with a role. The same applies to `init` and `push`, which each fell back to a guess at the namespaces when any error came out of the loader. The legacy role migration skips with a warning instead of failing, so every command, `pull` included, still loads the config and can fetch the fixed manifest; until it can run, the member holds no role rather than every role, so hooks, MCP servers and env variables scoped by `roles:` reach them no more than skills do. For a member with no active project that fallback meant an unfiltered sync, so a broken manifest delivered every namespace it was written to gate. Only an absent manifest still means "this team does not use roles"; an unreadable or empty file is an error, as it now is for `manifest/projects.yaml` too. `push` stops at its scan for such a manifest (exit 2) even with `--role <ns>`, since the scan needs it to tell which namespaces are the member's.
- `teamai members list` and `teamai projects members` read the roster registered before the reports switch, so a team upgrading past the orphan-branch split no longer sees "No team members registered" while its `members/` still lives on the default branch. The default-branch copy becomes a read-only inherited root, the way learnings' already was: listed in union with the `teamai-reports` copy, with the branch copy winning when the same file exists on both; nothing is copied or deleted, and a cold `members list` still does not publish the reports branch. Member registration merges against the inherited copy too, so a re-init keeps the original `registeredAt` and projects. Fixes [#735](https://github.com/Tencent/teamai-cli/issues/735).
Expand Down
4 changes: 3 additions & 1 deletion docs/designs/data-directory-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,9 @@ were removed.

**Functionization ≠ project-scoping.** All of these are class-A2 (machine-level):
the getters still return `~/.teamai/...`, unchanged. The project-scoped equivalents
already route through `getDataHome()`. The dashboard is likewise an A2 singleton
already route through `getDataHome()`. Skill usage moved there too (#748):
`usage.jsonl` lives in each scope's `getDataHome()`, because one shared file let a
project's report carry every project's skills. The dashboard is likewise an A2 singleton
(events carry `cwd`/`sessionId`); "two projects' events don't mix" is satisfied by
`getEventsPath()` reading `HOME` at call time, not by per-project dirs.

Expand Down
8 changes: 5 additions & 3 deletions docs/designs/skill-serving.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,11 @@ path (measured here from a 77-character one).
blocks instead (`blockedBy: "config"`), since recall and the source are then
unknown and the workflow would fail at `teamai contribute` — a project config
too, which detection alone would skip in favour of the user config
(`findUnreadableProjectConfig`). The Stop-hook reminder follows the same rule. The Stop-hook share
reminder is gated the same way (`contributeHintAllowed`, `src/hook-handlers.ts`),
because it points at this command. The gate lives in one place:
(`findUnreadableProjectConfig`). The Stop-hook share reminder is gated the same
way (`contributeHintAllowed`, `src/hook-handlers.ts`), because it points at this
command, with one difference: with no config at all it stays silent. The hook
fires in every project on the machine, and a directory without teamai has no
team to share with (#748). The gate lives in one place:
`resolveServableSkill` (`src/skill-content.ts`) is the only way to obtain a
packaged skill outside that module, and it returns `blocked` instead of the
skill, so a command cannot print a directory it never received.
Expand Down
8 changes: 4 additions & 4 deletions docs/designs/team-intelligence-platform.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Transform TeamAI from a simple skill-sharing CLI into a **Team Intelligence Plat
**Storage:** `~/.teamai/sessions/<year-month>.md` 按月聚合。

#### 2. Skill Usage Tracker (Local)
**What:** PostToolUse hook 检测 Claude Code 的 Skill 工具调用,追加写入 `~/.teamai/usage.jsonl`。
**What:** PostToolUse hook 检测 Claude Code 的 Skill 工具调用,追加写入该 scope 的 `<dataHome>/usage.jsonl`(会话所在目录对应的已配置项目,否则 user scope 的 `~/.teamai/`;未配置 teamai 的目录不记录,#748)。

**Data format (JSONL, one event per line):**
```jsonl
Expand All @@ -88,14 +88,14 @@ Skill Usage Statistics:
```

#### 3. Team Usage Aggregation
**What:** `teamai pull` 自动聚合本地 usage.jsonl 为 `stats/<user>.yaml`;Git 仓库写入独立的 `teamai-reports` 分支(不创建 MR)。
**What:** `teamai pull` 自动聚合当前 scope 的 usage.jsonl 为 `stats/<user>.yaml`;Git 仓库写入独立的 `teamai-reports` 分支(不创建 MR)。

**Push flow:**
```
teamai pull
│
▼
聚合 ~/.teamai/usage.jsonl → stats/<user>.yaml
聚合 <dataHome>/usage.jsonl → stats/<user>.yaml
│
▼
reports worktree → git add → git commit → git push (teamai-reports)
Expand Down Expand Up @@ -162,7 +162,7 @@ reports worktree → git add → git commit → git push (teamai-reports)
│ recommend.ts │ │
┌──────────────┐ │ digest.ts │ push --stats
│ Local │ │ stats.ts │ push --sessions
│ ~/.teamai/ │◀───│ team-push.ts ──┼────▶(直接 commit, 无 MR)
│ <dataHome>/ │◀───│ team-push.ts ──┼────▶(直接 commit, 无 MR)
│ usage.jsonl │ │ │
│ sessions/ │ └────────────────┘
│ <Y-M>.md │
Expand Down
2 changes: 1 addition & 1 deletion docs/product-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ Task: Fix duplicate project-level Hook injection
Consider running `/teamai share what this session taught me` to summarize what you learned and share it with your team (or run `teamai skill get share`).
```

The hint names the non-zero friction signals that triggered it and, when available, includes a redacted, single-line summary of the first task. The `share` workflow (`teamai skill get share`) summarizes the session and pushes a learning document directly to the team repo. Each session is prompted at most once. Teams can switch the hint off with `sharing.contributeHint.enabled: false` in `teamai.yaml` (members: `contributeHintEnabled` in local config) while keeping the rest of the Stop hook. The hint also needs recall to be on (it is off by default), because the workflow it points at is served only then.
The hint names the non-zero friction signals that triggered it and, when available, includes a redacted, single-line summary of the first task. The `share` workflow (`teamai skill get share`) summarizes the session and pushes a learning document directly to the team repo. Each session is prompted at most once. Teams can switch the hint off with `sharing.contributeHint.enabled: false` in `teamai.yaml` (members: `contributeHintEnabled` in local config) while keeping the rest of the Stop hook. The hint also needs recall to be on (it is off by default), because the workflow it points at is served only then, and it never appears in a directory where teamai is not set up.

### Team Knowledge Recall

Expand Down
2 changes: 1 addition & 1 deletion docs/product-overview.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ Task: Fix duplicate project-level Hook injection
Consider running `/teamai share what this session taught me` to summarize what you learned and share it with your team (or run `teamai skill get share`).
```

提示会列出实际触发它的非零摩擦信号;如果能取得首个任务摘要,还会在脱敏、单行化后附上任务上下文。`share` 工作流(`teamai skill get share`)自动总结 session 经验并推送到团队仓库。每个 session 最多提示一次。团队可在 `teamai.yaml` 设置 `sharing.contributeHint.enabled: false` 关闭该提示(成员可用本地配置 `contributeHintEnabled` 覆盖),Stop hook 的其余功能不受影响。该提示还需要开启 recall(默认关闭),因为它指向的工作流只在 recall 开启时提供。
提示会列出实际触发它的非零摩擦信号;如果能取得首个任务摘要,还会在脱敏、单行化后附上任务上下文。`share` 工作流(`teamai skill get share`)自动总结 session 经验并推送到团队仓库。每个 session 最多提示一次。团队可在 `teamai.yaml` 设置 `sharing.contributeHint.enabled: false` 关闭该提示(成员可用本地配置 `contributeHintEnabled` 覆盖),Stop hook 的其余功能不受影响。该提示还需要开启 recall(默认关闭),因为它指向的工作流只在 recall 开启时提供;在未配置 teamai 的目录中也不会出现。

### 团队知识检索

Expand Down
11 changes: 7 additions & 4 deletions docs/usage-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ teamai pull --dry-run # Dry run, no actual changes

A manual `teamai pull` ends by running the `teamai doctor` checks and printing each one that failed, with its fix — including whether the skills it just reported syncing are readable on disk for every enabled tool. It prints nothing when they all pass, and the exit code is unchanged. The SessionStart hook path and `--dry-run` run no checks at all, so session startup stays as fast as before. Provider checks (`gh`/`gf` authentication) are left to `teamai doctor`: the pull just used the provider.

> Project scope is isolated by default. When the current working directory contains a project-scope `.teamai/config.yaml`, `pull` processes that project and skips user scope unless the local config has `inheritUserScope: true`; in that case it first refreshes the safe user-resource channel. Without a project config in the current directory, `pull` processes user scope. User `env`, MCP definitions, sources, reporting, and writes remain isolated in project mode. Hooks are the one exception: a project scope's hooks are injected into your **HOME** tool settings (`~/.claude/settings.json`, …), not `<projectRoot>`, because the built-in hooks gate on the `cwd` handed to `hook-dispatch` and `~/.claude` always exists so the "installed tool" gate passes (see the Hooks section). Self single-repo mode keeps its hooks in the business repo so they travel on clone.
> Project scope is isolated by default. When the current working directory contains a project-scope `.teamai/config.yaml`, `pull` processes that project and skips user scope unless the local config has `inheritUserScope: true`; in that case it first refreshes the safe user-resource channel. Without a project config in the current directory, `pull` processes user scope. User `env`, MCP definitions, sources, reporting, and writes remain isolated in project mode. Hooks are the one exception: a project scope's hooks are injected into your **HOME** tool settings (`~/.claude/settings.json`, …), not `<projectRoot>`, because the built-in hooks gate on the `cwd` handed to `hook-dispatch` and `~/.claude` always exists so the "installed tool" gate passes (see the Hooks section). In a directory with no teamai config (no project config and no user scope), the team hooks do nothing: no reminders, and no session or skill usage is recorded; only machine-level work runs (the CLI update check, the session-start pull, the local agent, and package hints a pull stashed). A project config that exists but cannot be read counts as none, never as the user scope. Self single-repo mode keeps its hooks in the business repo so they travel on clone.

With role-based skills enabled, `pull`'s skill sync source becomes the contents of `skills/<namespace>/`, expanded according to `primaryRole + additionalRoles` and flattened into each local AI tool's skills directory. `rules/` and `docs/` keep their original sync behavior; `agents/<namespace>/` follows the role's `agents` namespaces (see [Agents Resource Type](#agents-resource-type)). `learnings/` at the root is shared with everyone, while `learnings/<project-id>/` subdirectories sync only for the directory's active projects (see [Multi-project](#multi-project-project-as-a-dimension-orthogonal-to-role)).

Expand Down Expand Up @@ -973,7 +973,7 @@ Teams that route knowledge sharing through their own review flow (for example, a

Only the nudge is affected: friction scoring, `teamai contribute --file`, and `/teamai` keep working when invoked manually.

The reminder is also withheld while recall is off (the default until `sharing.recall.enabled: true` in `teamai.yaml`, or `teamai recall enable` on one machine): it points at the `share` workflow, and `teamai skill get share` refuses until recall is on. It never appears on a read-only HTTP source, where `share` refuses too.
The reminder is also withheld while recall is off (the default until `sharing.recall.enabled: true` in `teamai.yaml`, or `teamai recall enable` on one machine): it points at the `share` workflow, and `teamai skill get share` refuses until recall is on. It never appears on a read-only HTTP source, where `share` refuses too, nor in a directory where teamai is not set up.

### Searching knowledge

Expand Down Expand Up @@ -1720,8 +1720,11 @@ On Windows, the update check, installation, and hook refresh run without opening
By default, `teamai pull` commits session/usage stats into the team repo.
Pull waits up to 5 seconds for the reporting batch, then continues its other
work while reporting finishes. A late successful push still updates the local
reported snapshots. Usage events are removed only after every selected target
confirms success; failed pushes preserve them. The affected sync locks remain
reported snapshots. Skill usage is recorded per scope, in the data directory of
the project teamai is set up for where the session ran (or the user scope), so
each target reports only its own; a directory without teamai records none. A
target removes its usage events only after it confirms success; failed pushes
preserve them. The affected sync locks remain
held until reporting finishes, preventing another pull from racing the report.

This is best-effort reporting, not crash-safe delivery: termination between a
Expand Down
Loading
Loading