Make Every Team AI Native
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
fix(ci): pick an explicit release-notes base on the same lineage (#512) GitHub's automatic base selection takes the newest tag reachable from the tagged commit. Release commits are created detached and never land on a branch, so no release tag is an ancestor of the next one and every recent release compared back to v0.22.0 — the last release that happened to be merged into main. The 0.23.x stables are worse: they were cut from an internal mirror sync, so their merge-base with main sits at v0.17.4 and their notes span three months of unrelated PRs. Resolve the base explicitly and feed it to the generate-notes API. A prerelease compares against the tag before it; a stable release compares against the previous stable tag. Both skip tags whose parent is absent from this release's history, so a mirror-sync tag can no longer poison the range. Co-authored-by: Cursor <cursoragent@cursor.com> | 3 天前 | |
fix(search): deduplicate recall results by stable content identity (#298) The dedup key included `score` and `tokens.length`. Neither identifies a document, so results were collapsed in both directions. Identical content survived deduplication: `score` carries the vote bonus (+0.5/vote), so two copies of one re-shared learning drift apart as they collect votes independently and both consume a `limit` slot. Distinct content was collapsed: `tokens.length` only counts tokens, so two entries sharing type, title, date, author, token count and score were merged and one was silently dropped. Key on the fields a re-share copies verbatim instead — type, domain, title, date, author and the full token set. Tags and body reach the key through the token set, already normalized. The tokens are sorted because tag order follows hand-authored frontmatter, which a re-share may reorder. `domain` is listed separately: it comes from frontmatter, never reaches the token set, and was previously kept apart only by the domain multiplier inside `score`. The index schema and public CLI behavior are unchanged. Co-authored-by: Oreo9 <x9276@qq.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> | 25 天前 | |
docs: align README and usage guide with TeamAI product architecture Reframe public docs around Team Execution × Team Context × Team Improvement, and add vision.md as the longer product write-up. Co-authored-by: Cursor <cursoragent@cursor.com> | 10 天前 | |
fix(partition): adopt pre-#546 legacy-named partitions instead of stranding them (#551) * fix(partition): adopt pre-#546 legacy-named partitions instead of stranding them #546 widened the partition slug prefix from the anchor's basename to its whole path but kept the sha256 suffix — without migrating existing installs. On an upgraded CLI, a partition still named <basename>-<hash> (every install created before #546) stops matching projectSlug(anchor): detectProjectConfig finds no config (the project looks uninitialized), planMigration would re-copy a retired workspace into a second, empty partition, and status --all flags the perfectly good data as "corrupt — dir name does not match anchor". Both formats share the same hash, so an anchor's legacy name is computable exactly — no directory scanning. resolvePartitionDir becomes the seam for "the partition that actually holds this project's data" (detection, init, migration): when the canonical current-format directory is absent and a legacy-named one exists, it ATOMICALLY RENAMES the legacy partition into place — a same-parent metadata move, no data copied, and an interruption leaves either name intact. An authoritative current-format partition is never clobbered by a leftover legacy one; a rename that is genuinely impossible (read-only home) keeps serving the legacy directory so no data is stranded. status --all stays read-only and reports a legacy-named partition as "active (legacy name; renamed automatically on next command)" instead of corrupt. Verified end-to-end with the real CLI in an isolated HOME: a legacy .teamai migrates into the readable whole-path slug; status --all reverse-resolves it as [active]; a partition renamed to the pre-#546 format is reported active-legacy (no rename, no corrupt), then adopted by the next command (detection) with data intact, and pull runs through the adopted partition. * fix(partition): rebase repo.localPath when adopting a legacy partition A pre-#546 partition stores repo.localPath as an ABSOLUTE path to its team-repo clone (<legacyPartition>/team-repo). resolvePartitionDir renamed the directory but left config.yaml pointing at the now-gone old path, so every later `pull` read the team config from a dead directory and silently skipped the sync ("Team config (teamai.yaml) not found. Skipping.", exit 0) — the project could never sync again after an upgrade. Adoption now rebases repo.localPath onto the new partition (mirroring migrate.ts's rebaseConfigPaths). The rewrite is idempotent and self-healing: a modern install whose localPath already sits in the canonical dir is left untouched, an external clone outside the partition is left untouched, and an adoption interrupted between the rename and the config rewrite is finished by the next command. Reproduced with the real CLI (isolated HOME, real local git team repo): a legacy-named partition + two `pull --force` runs printed "Team config not found. Skipping." (exit 0) before this fix; after it both runs print "Synced 1 skills" and localPath points at the live clone. Regression tests: localPath rebased off the legacy dir / external localPath left alone / modern-install no-op. Verified they fail when the rebase is disabled. * fix(partition): write the adopted config.yaml atomically to prevent truncation The localPath rebase overwrote config.yaml with a plain (non-atomic) write. By that point the legacy partition has already been renamed away, so config.yaml is the partition's ONLY copy — a write that fails partway (ENOSPC, EFBIG, crash mid-write) truncates it with no source to recover from, and the CLI still prints "Your original data is unchanged, re-run to retry" while the data is in fact corrupt and cannot self-recover. Add writeFileAtomic (same-dir temp + rename, preserving mode) alongside the existing writeJsonAtomic, and use it for the adopted config.yaml. rename(2) is atomic, so a failed write removes the temp file and leaves the original config.yaml byte-for-byte intact; the next command retries the idempotent rebase and converges. Reproduced with the real CLI (isolated HOME, real local git team repo, NO fs mock) by injecting a write failure with RLIMIT_FSIZE=128: before: pull --force reports EFBIG, config.yaml truncated 453 -> 128 bytes, legacy partition already moved, retry after lifting the limit exits 0 but never syncs and cannot recover after: config.yaml preserved at 453 bytes, retry after lifting the limit prints "Synced 1 skills" and localPath points at the live clone Regression test: the localPath rewrite failing mid-write leaves config.yaml intact with no leftover temp file. Verified it fails when the write is made non-atomic. Full suite: 3109 passed, 0 regressions; tsc clean. | 8 小时前 | |
refactor(codebase): remove domains subsystem, drift command, and legacy aggregate (#225) Fully migrate team knowledge to teamwiki and drop the deprecated docs/team-codebase pipeline. The domains.yaml assignment, domain aggregation, and legacy codebase-lint paths are no longer produced or consumed by the real knowledge base. Removed: - src/domains/ (schema, store, cluster, recommend, review, index) - src/aggregate.ts (regenerateAggregate -> docs/team-codebase) - src/drift-cmd.ts and the hidden `teamai domains drift` command - src/codebase-lint.ts (legacy docs/team-codebase lint; teamwiki lint stays) - examples/ci/*-teamai-sync.{yml,yaml} (add-empty sync templates) Cleaned up: - import-repo/import-repo-list/import-org: drop dead domain functions, loadDomains/saveDomains/recommendDomain imports, step-5 aggregate, --skip-aggregate, and the unused --bootstrap flag - review-cmd/pull: drop appendHistory calls and domains.yaml sync block - codebase-cmd: --lint now only runs the teamwiki knowledge-graph lint - examples/ci: README + codebase-lint.yml point at teamwiki/ codebase summaries and the knowledge graph continue to be produced under teamwiki/ via import-repo -> extractCodebase + aggregateGlobalGraph + deepEnrich, unchanged. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> | 1 个月前 | |
refactor(http): remove the dead /repo team-repo snapshot path (#180) The `GET {baseUrl}/repo` endpoint (方案一 HTTP team-repo snapshot) was never implemented on the backend — it answers 200 with the SPA HTML shell, so `fetchRepoSnapshot` always threw RepoNotAvailableError and both `init --http` and `pull` permanently fell through to their reporting-only branch. Skills, rules and CLAUDE.md are delivered entirely via the report/sync/ack lifecycle (the local-agent bypass), which is verified live against the backend. Remove the client for a path that never runs: - delete src/source-http.ts (fetchRepoSnapshot / materializeHttpRepo / removeMaterializedSkill / RepoSnapshot / RepoFile / RepoNotAvailableError) - init --http: drop the /repo fetch + reporting-only fallback; always write the teamai.yaml stub and wire report/sync/ack - pull: the http backend has no tree to clone — return report/sync delivery - tests/mock: delete source-http.test.ts, drop /repo + seedRepo from the mock server, rewrite the http-repo integration case to the no-clone behavior - scripts/mock-teamai-server.mjs + README (EN/zh): drop the /repo contract; the download_url shape now hangs off the sync response Also folds in small HTTP cleanups: reuse manifestKind() in removeLocalAgentHttp and hoist the static config.js import in sourceAddHttp. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> | 2 个月前 | |
feat(codebase): expose wiki deep-enrich Expose the existing deep-enrich engine as `teamai codebase --deep-enrich`, so the bundled wiki skill can generate deep knowledge without the hidden top-level command or a separate team-wiki CLI. Related to #360 (slice 3; does not close the full issue). | 4 天前 | |
fix(partition): adopt pre-#546 legacy-named partitions instead of stranding them (#551) * fix(partition): adopt pre-#546 legacy-named partitions instead of stranding them #546 widened the partition slug prefix from the anchor's basename to its whole path but kept the sha256 suffix — without migrating existing installs. On an upgraded CLI, a partition still named <basename>-<hash> (every install created before #546) stops matching projectSlug(anchor): detectProjectConfig finds no config (the project looks uninitialized), planMigration would re-copy a retired workspace into a second, empty partition, and status --all flags the perfectly good data as "corrupt — dir name does not match anchor". Both formats share the same hash, so an anchor's legacy name is computable exactly — no directory scanning. resolvePartitionDir becomes the seam for "the partition that actually holds this project's data" (detection, init, migration): when the canonical current-format directory is absent and a legacy-named one exists, it ATOMICALLY RENAMES the legacy partition into place — a same-parent metadata move, no data copied, and an interruption leaves either name intact. An authoritative current-format partition is never clobbered by a leftover legacy one; a rename that is genuinely impossible (read-only home) keeps serving the legacy directory so no data is stranded. status --all stays read-only and reports a legacy-named partition as "active (legacy name; renamed automatically on next command)" instead of corrupt. Verified end-to-end with the real CLI in an isolated HOME: a legacy .teamai migrates into the readable whole-path slug; status --all reverse-resolves it as [active]; a partition renamed to the pre-#546 format is reported active-legacy (no rename, no corrupt), then adopted by the next command (detection) with data intact, and pull runs through the adopted partition. * fix(partition): rebase repo.localPath when adopting a legacy partition A pre-#546 partition stores repo.localPath as an ABSOLUTE path to its team-repo clone (<legacyPartition>/team-repo). resolvePartitionDir renamed the directory but left config.yaml pointing at the now-gone old path, so every later `pull` read the team config from a dead directory and silently skipped the sync ("Team config (teamai.yaml) not found. Skipping.", exit 0) — the project could never sync again after an upgrade. Adoption now rebases repo.localPath onto the new partition (mirroring migrate.ts's rebaseConfigPaths). The rewrite is idempotent and self-healing: a modern install whose localPath already sits in the canonical dir is left untouched, an external clone outside the partition is left untouched, and an adoption interrupted between the rename and the config rewrite is finished by the next command. Reproduced with the real CLI (isolated HOME, real local git team repo): a legacy-named partition + two `pull --force` runs printed "Team config not found. Skipping." (exit 0) before this fix; after it both runs print "Synced 1 skills" and localPath points at the live clone. Regression tests: localPath rebased off the legacy dir / external localPath left alone / modern-install no-op. Verified they fail when the rebase is disabled. * fix(partition): write the adopted config.yaml atomically to prevent truncation The localPath rebase overwrote config.yaml with a plain (non-atomic) write. By that point the legacy partition has already been renamed away, so config.yaml is the partition's ONLY copy — a write that fails partway (ENOSPC, EFBIG, crash mid-write) truncates it with no source to recover from, and the CLI still prints "Your original data is unchanged, re-run to retry" while the data is in fact corrupt and cannot self-recover. Add writeFileAtomic (same-dir temp + rename, preserving mode) alongside the existing writeJsonAtomic, and use it for the adopted config.yaml. rename(2) is atomic, so a failed write removes the temp file and leaves the original config.yaml byte-for-byte intact; the next command retries the idempotent rebase and converges. Reproduced with the real CLI (isolated HOME, real local git team repo, NO fs mock) by injecting a write failure with RLIMIT_FSIZE=128: before: pull --force reports EFBIG, config.yaml truncated 453 -> 128 bytes, legacy partition already moved, retry after lifting the limit exits 0 but never syncs and cannot recover after: config.yaml preserved at 453 bytes, retry after lifting the limit prints "Synced 1 skills" and localPath points at the live clone Regression test: the localPath rewrite failing mid-write leaves config.yaml intact with no leftover temp file. Verified it fails when the write is made non-atomic. Full suite: 3109 passed, 0 regressions; tsc clean. | 8 小时前 | |
ci: add Coding CI pipeline, vitest coverage, E2E migration, and auto-release (merge request !63) Squash merge branch 'feat/ci-pipeline' into 'master' ## Summary - 新增 `.coding-ci.yaml`:4 stage Coding CI 流水线(validate → build → e2e → publish) - lint + test 并行执行 - E2E 有 token 时才跑 - publish 版本预检 + `.npmrc` 自动清理 - 新增 `vitest.config.ts`(单元测试 + coverage: cobertura/text) - 新增 `vitest.e2e.config.ts`(E2E: 60s timeout) - 迁移 `test/e2e.mjs` → `src/__tests__/e2e/e2e.test.ts`(vitest) - 新增 --version、--help、status、pull/push --dry-run 测试 - 无 token 时自动 skip - 新增 `.versionrc`(standard-version changelog 配置) - 新增 scripts: `test:e2e`, `test:coverage`, `release` - 修复 2 个 update.test.ts 失败测试 ## Test plan - [x] `npm run test` — 275 tests passed - [x] `npm run test:e2e` — 5 passed, 6 skipped (no token) - [x] `npm run build` — dist/ 正常产出 - [x] `.coding-ci.yaml` YAML 语法校验通过 - [ ] TGit 上配置 CI 变量后验证流水线 ## CI 变量(需在 TGit 手动配置) | 变量 | 类型 | |------|------| | `TEAMAI_TEST_TOKEN_SECRET` | masked | | `TEAMAI_TEST_REPO_URL_SECRET` | masked | | `NPM_TOKEN_SECRET` | masked + protected | | 5 个月前 | |
ci: publish prereleases to their own dist-tag, never latest (#317) Both release pipelines (GitHub Actions + Coding CI) hard-coded `npm publish` with no --tag, so npm's default sent every build — prerelease versions included — to the `latest` dist-tag. That is the tag the CLI's auto-update check reads (src/update.ts), so pushing a `vX.Y.Z-beta.N` tag would have force-fed the beta to every user. Derive the dist-tag from the version string instead: a prerelease (e.g. 0.21.0-beta.0 -> beta, 0.22.0-rc.1 -> rc) publishes to its own channel; a plain release still goes to `latest`. GitHub Releases for prereleases are now flagged as pre-release. Document the beta flow in CLAUDE.md. | 21 天前 | |
test(hooks): pin golden hook fixtures to LF so the byte anchor holds on Windows (#497) src/__tests__/hooks-golden.test.ts compares injectHooks() output against fixtures/hooks/<tool>.json with expect(got).toBe(want), and readFile(..., 'utf-8') does not normalise line endings. The repository has no .gitattributes, and Git for Windows defaults to core.autocrlf=true, so all five fixtures were checked out with CRLF and every case failed for anyone using default Windows git settings. CI stayed green because the matrix only runs ubuntu-latest and macos-latest. Pin them with `text eol=lf`, plus the same rule for .gitattributes itself so it stays stable on Windows too. The fixture pattern matches only those five files, which only hooks-golden.test.ts reads. | 3 天前 | |
feat(codebase): wiki-engine extraction + AI enrichment + knowledge reconciler (#55) Deterministic code knowledge pipeline: extract → enrich → reconcile → compile → index. Wiki-engine modules: - Interface scanner (HTTP/MQ/RPC, 5 languages) - Dependency path tracer + code-graph overlay - Doc-graph extractor + manifest schema Extraction pipeline (teamai codebase --extract): - Code collector with priority sorting + incremental mode - Per-language extractors (TS/Go/Java/Python/Rust/Config) - Graph-index builder + evidence page renderer AI enrichment layer: - enrich-with-ai.ts: per-module responsibility inference - knowledge-reconciler.ts: 9-phase product↔code cross-mapping - manifest-compiler.ts: structured component docs with wiki-links - rebuild-wiki-index.ts: domain-grouped router.md + stats index.md Import integration: - import-repo: full pipeline (extract + enrich + compile + reconcile) - import-org: simplified whitelist-based flow (no AI clustering) - Remove legacy domain classification (domains.yaml) Tests: 39 new unit tests for wiki-engine modules + hook-output Co-authored-by: jaelgeng <jaelgeng@tencent.com> | 2 个月前 | |
ci: add Coding CI pipeline, vitest coverage, E2E migration, and auto-release (merge request !63) Squash merge branch 'feat/ci-pipeline' into 'master' ## Summary - 新增 `.coding-ci.yaml`:4 stage Coding CI 流水线(validate → build → e2e → publish) - lint + test 并行执行 - E2E 有 token 时才跑 - publish 版本预检 + `.npmrc` 自动清理 - 新增 `vitest.config.ts`(单元测试 + coverage: cobertura/text) - 新增 `vitest.e2e.config.ts`(E2E: 60s timeout) - 迁移 `test/e2e.mjs` → `src/__tests__/e2e/e2e.test.ts`(vitest) - 新增 --version、--help、status、pull/push --dry-run 测试 - 无 token 时自动 skip - 新增 `.versionrc`(standard-version changelog 配置) - 新增 scripts: `test:e2e`, `test:coverage`, `release` - 修复 2 个 update.test.ts 失败测试 ## Test plan - [x] `npm run test` — 275 tests passed - [x] `npm run test:e2e` — 5 passed, 6 skipped (no token) - [x] `npm run build` — dist/ 正常产出 - [x] `.coding-ci.yaml` YAML 语法校验通过 - [ ] TGit 上配置 CI 变量后验证流水线 ## CI 变量(需在 TGit 手动配置) | 变量 | 类型 | |------|------| | `TEAMAI_TEST_TOKEN_SECRET` | masked | | `TEAMAI_TEST_REPO_URL_SECRET` | masked | | `NPM_TOKEN_SECRET` | masked + protected | | 5 个月前 | |
docs: tighten CLAUDE.md and add AGENTS.md for other agents Keep agent instructions short and tool-agnostic: drop internal tnpm/release detail, require real e2e plus a PR test report, and avoid adding CLI commands unless necessary. Co-authored-by: Cursor <cursoragent@cursor.com> | 10 天前 | |
fix(mcp): resolve requires from PATH including Windows PATHEXT (#540) * fix(mcp): resolve requires from PATH including Windows PATHEXT teamai mcp inject skipped servers with requires: [uvx] on Windows because requirementsMet spawned /bin/sh -c 'command -v', which ENOENTs when /bin/sh is absent even if uvx.exe is on PATH. Scan PATH (and PATHEXT on win32) instead of a POSIX shell, and keep rejecting unsafe names. Fixes #539 * docs: link MCP requires Windows fix to PR #540 | 13 小时前 | |
docs: tighten CLAUDE.md and add AGENTS.md for other agents Keep agent instructions short and tool-agnostic: drop internal tnpm/release detail, require real e2e plus a PR test report, and avoid adding CLI commands unless necessary. Co-authored-by: Cursor <cursoragent@cursor.com> | 10 天前 | |
chore: update LICENSE to Tencent open source MIT license (merge request !170) Squash merge branch 'chore/update-license-for-opensource' into 'master' chore: update LICENSE to Tencent open source MIT license Update LICENSE file for open source release with Tencent copyright notice. | 4 个月前 | |
fix(codebase): write fallback evidence manifest so deep-enrich can start (#522) Extract now writes teamwiki/evidence/code/<project>/_manifest.json from module or component facts when AI enrichment is skipped, fails, or finds no qualifying modules. Public empty-manifest errors no longer tell the user to re-run extract. Hidden teamai deep-enrich exits non-zero when there are no components. Fixes #508. | 2 天前 | |
fix(codebase): write fallback evidence manifest so deep-enrich can start (#522) Extract now writes teamwiki/evidence/code/<project>/_manifest.json from module or component facts when AI enrichment is skipped, fails, or finds no qualifying modules. Public empty-manifest errors no longer tell the user to re-run extract. Hidden teamai deep-enrich exits non-zero when there are no components. Fixes #508. | 2 天前 | |
chore(deps): upgrade high/critical severity dependencies (#514) Resolve all 10 Critical+High Dependabot alerts: - smol-toml ^1.3.1 -> ^1.7.1 (runtime, CVE-2026-85730) - js-yaml override ^3.15.1 -> ^3.15.2 (runtime, CVE-2026-84375) - vitest / @vitest/coverage-v8 2.x -> 3.2.7 (clears CVE-2026-47429; stays on 3.x) - override brace-expansion 1.1.18/2.1.4/5.0.9, nanoid 3.3.19, postcss 8.5.28, picomatch 4.0.7 npm audit: critical/high now 0. build + tsc + full vitest suite (3006 tests) pass. | 3 天前 | |
chore(deps): upgrade high/critical severity dependencies (#514) Resolve all 10 Critical+High Dependabot alerts: - smol-toml ^1.3.1 -> ^1.7.1 (runtime, CVE-2026-85730) - js-yaml override ^3.15.1 -> ^3.15.2 (runtime, CVE-2026-84375) - vitest / @vitest/coverage-v8 2.x -> 3.2.7 (clears CVE-2026-47429; stays on 3.x) - override brace-expansion 1.1.18/2.1.4/5.0.9, nanoid 3.3.19, postcss 8.5.28, picomatch 4.0.7 npm audit: critical/high now 0. build + tsc + full vitest suite (3006 tests) pass. | 3 天前 | |
Initial release: tad v0.1.0 Team AI DevKit CLI for sharing skills, rules, docs, and hooks across team members via TGit. | 6 个月前 | |
Initial release: tad v0.1.0 Team AI DevKit CLI for sharing skills, rules, docs, and hooks across team members via TGit. | 6 个月前 | |
fix(test): bump vitest default timeout to 15s for CI stability (#150) CI workers can be resource-constrained under parallel test load, which caused otherwise-fast unit tests to sporadically exceed vitest's 5s default timeout (e.g. agent-skills, ci-extract-mr, import-dir suites), observed on the internal Coding CI runner. Co-authored-by: Cursor <cursoragent@cursor.com> | 2 个月前 | |
Phase 0+Phase 1+P4.4: 实现了subagent推送,检索subagent,初始化知识库导入以及MR自动learnings&codebase更新功能(团队级别codebase文档功能有待完善) (#28) * docs:知识库飞轮系统设计roadmap文档 * feat: phase 1 - agents support, multi-index search, recall subagent & todowrite hint - Add agents resource type (pull/push/remove support) - Add builtin-agents with teamai-recall agent - Add multi-source search index (rules + skills + agents) - Add todowrite hint injection for recall subagent - Add phase1 e2e tests and unit tests for new features - Update README (zh-CN + en) with agents documentation * docs: update roadmap文档 * feat(search): P1.4 domain inference + search weighting Introduce KnowledgeDomain (technical/ops/support/neutral) inferred from frontmatter > tags > path > type fallback. Apply per-domain score multipliers at search time (technical ×1.0, neutral ×0.85, ops ×0.5, support ×0.3) plus a skills/rules type bonus (×1.1). Bump SEARCH_INDEX_VERSION 2→3; legacy v2 indexes auto-rebuild on next pull. --other=P1.4 domain inference and search weighting * test(validation): add Phase 1 E2E suite and acceptance report Move phase1-e2e.test.ts to validation/ alongside the acceptance report, update import paths and vitest.e2e.config.ts to pick up the new location. Fix pre-existing E2E isolation bug: loadStateForScope mock used mockResolvedValue (shared object reference), causing test-1 to mutate state.lastPullRev and trigger the rev-based early-exit in test-2/3. Switch to mockImplementation(() => ({ lastPull: null })) so each call gets a fresh object. All 5 E2E tests now pass. --other=Phase 1 validation * docs: update roadmap — add P4.6 learning promotion mechanism Learning entries that accumulate confidence ≥ 0.90 (5+ upvotes, 2+ contributors, 14+ days old) are prompted for promotion to docs/skills/rules based on content type, regardless of origin or domain. Add P4.6 step to Phase 4, dependency graph, implementation detail, work-estimate table, and architecture overview diagram. --other=roadmap update * docs(validation): update phase1 report to reflect E2E bug fix All 5 E2E tests now pass. Update P1.2 status to ✅, remove E2E failure notes and known issues, set pass rate to 100%. --other=phase1 report update * docs(validation): add runtime evidence appendix to phase1 report Append Appendix A1-A4 with captured outputs from demo-phase1.test.ts: agents sync paths, CLAUDE.md full content after injection, search-index.json entry listing (4 types covered), and recall("api") full STDOUT including envelope markers and domain-weighted scores. Knowledge content in A3/A4 is anonymised (titles, authors, file paths replaced with generic placeholders). Also commit demo-phase1.test.ts as the reproducible evidence runner. --other=phase1 report runtime evidence * feat(search): query-aware domain weights + IDF scoring (v4 index) 改动 A — 查询感知 domain 权重: 将静态一维 DOMAIN_WEIGHT 改为二维查询-文档权重矩阵,新增 inferQueryDomain() 从查询 token 推断查询域。搜索 k8s/deploy 等 ops 相关问题时,ops 条目不再被打五折,technical 查询行为与原来一致。 改动 B — IDF 降权: buildIndex() 末尾计算 df(文档频率)map 并写入索引;search() 中 为每个 token 匹配乘以 log((N+1)/(df+1))+1 的 IDF 权重,高频通用词 (api、deploy、error)自动降权,低频专有词(deepgemm、mooncake) 权重保持不变。 索引版本 3 → 4;isLegacyIndex() 新增 !index.df 判断触发重建。 旧 v3 索引在下次 teamai pull 时自动重建,search() 对无 df 字段的 旧索引降级为 idf=1.0,不报错。 --other=search quality improvements * feat(import): add teamai import command — Phase 0 cold-start + P4.4 MR pipeline ## 新增命令:teamai import 支持五种知识来源: - --dir <path>:扫描本地目录,AI 分类为 rule/doc/learning - --from-claude:迁移 ~/.claude/rules 等 AI 工具规则目录 - --workspace:基于当前 git 仓库生成 codebase.md - --from-mr <url>:从已合并 MR 提炼 learning + codebase 更新建议(P4.4) - --from-iwiki <id/url>:从 iWiki Space 批量导入文档 ## 新增核心模块 - src/utils/ai-client.ts:claude -p 子进程封装(并发 ≤ 3,60s 超时) - src/utils/dedup.ts:Jaccard 相似度重复检测(14 天窗口,≥ 60% 标记 superseded) - src/utils/iwiki-client.ts:iWiki MCP HTTP 客户端(JSON-RPC 2.0,零外部依赖) - src/import-local.ts:本地文件扫描/AI 分类/交互确认/推送 - src/import-mr.ts:MR 三层解析/双路 AI 提炼/dedup/推送 - src/import-iwiki.ts:iWiki 导入(复用 import-local.ts 基础设施) - src/codebase.ts:codebase.md 生成/增量更新 ## 扩展现有接口 - providers/types.ts:GitProvider 新增可选 fetchMergeRequest() 方法 - providers/github/mr-fetch.ts:gh pr view 实现 - providers/tgit/mr-fetch.ts:gf mr 实现 - types.ts:新增 MRData/ClassifiedItem/LearningDraft/CodebaseSuggestion/ImportSession ## 测试 & 文档 - ai-client.test.ts:5 tests(spawn mock + 并发控制) - dedup.test.ts:11 tests(关键词提取 + Jaccard + 文件扫描) - validation/phase0-p44-acceptance-report-public.md:Phase 0 + P4.4 验收报告 --story=132854480 【产品需求】teamai-cli Phase 0 冷启动 + P4.4 MR 提炼流水线 * fix(import): support claude-internal CLI + gh REST API fallback + real PR demo ## ai-client.ts - detectClaudeCli():按 claude → claude-internal 优先级探测,结果缓存, 进程内只探测一次,两者均不可用时给出清晰错误提示 - DEFAULT_TIMEOUT_MS:60s → 180s,适应大 diff 下 AI 提炼耗时 ## providers/github/mr-fetch.ts - gh CLI 不可用时自动降级到 GitHub REST API(内置 https,零依赖) - 支持公开仓库无 token 访问;有 GITHUB_TOKEN 环境变量时自动携带 ## import-mr.ts - codebase 建议 JSON 解析:先提取 {…} 块再解析, 兼容 AI 在 JSON 前附加说明文字的输出格式 ## index.ts - import 子命令新增 --all 选项,跳过交互确认 ## validation - phase0-p44-acceptance-report-public.md A4:替换为基于真实 PR #2 的端到端操作记录(真实终端输出 + AI 真实生成的 learning.md 和 codebase-suggestions.json 完整原文) --story=132854480 【产品需求】teamai-cli Phase 0 冷启动 + P4.4 MR 提炼流水线 * fix(codebase): require file-path prefix in module descriptions for agent guidance codebase.ts 和 import-mr.ts 的提示词中"主要模块"格式要求均已更新: - 之前:**模块名** — 功能说明(AI 可能只写中文名,无路径索引) - 之后:**文件或目录路径** — 功能说明(明确要求带路径,便于 agent 定位) codebase.ts: 全量生成 prompt 示例改为 **src/utils/git.ts** — 功能说明 import-mr.ts: codebase 建议 prompt 新增正确/错误示例,强制路径前缀 同步更新验收报告 A4: - codebase-suggestions.json 从 8 条无路径条目 → 11 条带路径条目(真实重跑输出) - 更新后 codebase.md 主要模块列表格式与更新前一致 --story=132854480 【产品需求】teamai-cli Phase 0 冷启动 + P4.4 MR 提炼流水线 * feat(codebase): upgrade workspace + MR prompts to A1-level documentation quality codebase.ts — gatherRepoContext 扩展: - 增加 package.json(依赖和 scripts) - 增加入口文件(src/index.ts)命令注册全文 - 增加类型定义文件(src/types.ts)关键接口 - 文件树深度 maxdepth 3→4,过滤 dist/ worktrees/ - 截断上限:FILE_TREE 3000→5000 字符,DOC 1000→2000 字符 - 新增 META_MAX_CHARS 常量(2500 字符) codebase.ts — 全量生成 prompt 重写: - 提供完整 8+ 章节格式骨架(项目概述/技术栈/目录结构/数据配置/ 核心数据流/关键接口/配置系统/性能可靠性/测试覆盖/备注) - 目录结构要求带分组框树形图(┌─ 功能分组 ──┐ 风格) - 技术栈要求表格含版本信息 - 项目概述要求带 emoji 核心能力 bullet list - 核心数据流要求带缩进 → 的流程图格式 import-mr.ts — codebase 建议 prompt 升级: - 新增 existingCodebaseMd 参数,注入现有文档全文作为格式样本 - AI 参考现有文档的分组和粒度生成风格一致的增量条目 import.ts — --from-mr 分支: - 调用前读取 repoPath/docs/codebase.md 传入 existingCodebaseMd - 确保 MR 增量更新与初始生成风格一致 - 复用已导入的顶层 fs 模块,删除内联 dynamic import --story=132854480 【产品需求】teamai-cli Phase 0 冷启动 + P4.4 MR 提炼流水线 * docs(validation): replace A1+A4 codebase docs with real teamai-cli generated output A1 附录:替换为 teamai import --workspace 对 upstream/main 真实生成的 codebase.md(含分组框目录树、表格技术栈、emoji 核心能力、流程图数据流) A4 附录: - Step 1:更新 codebase-before.md 为同一真实生成版本 - Step 2/3:终端输出更新为 3 条建议(主要模块 7 条/关键路径 4 条/架构决策) - Step 3:learning.md 更新为最新真实 AI 输出 - Step 4:更新前后对比基于真实文档 - Step 5:飞轮闭环统计数字更新(3 条建议) --story=132854480 【产品需求】teamai-cli Phase 0 冷启动 + P4.4 MR 提炼流水线 * feat(mr-hint): add SessionStart hook to hint AI about unimported merged MRs P4.4 优化:在每次 Session 开始时检测当前 git 仓库的 origin remote, 查询近 7 天内已合入但尚未通过 teamai import 处理的 MR,并通过 additionalContext 提示 AI 在任务完成后建议用户运行 teamai import --from-mr。 核心实现: - src/mr-hint.ts:新增 mrHint() 入口,支持 TGit REST API 和 GitHub gh CLI 双路查询;per-repo 磁盘缓存(30 天 TTL)避免重复提示相同 MR - src/hooks.ts:注册 SessionStart hook,更新 TEAMAI_COMMAND_MARKERS 和 TEAMAI_HOOK_SUBCOMMANDS,同步 buildCursorHooks - src/index.ts:注册 mr-hint 子命令 - 同步修复 hooks.test.ts、usage-tracking.test.ts、doctor.test.ts 中 todowrite-hint 加入后遗留的计数断言 --other=P4.4 MR 合入统一处理流水线(SessionStart 触发提示) * feat(mr-hint): add GitHub REST API fallback when gh CLI unavailable gh CLI 不在环境中时自动回退到 GitHub REST API(/repos/.../pulls), 逻辑与 providers/github/mr-fetch.ts 保持一致; 支持公开仓库无 token,有 GITHUB_TOKEN 时自动携带以提升限速上限。 --other=P4.4 MR 合入统一处理流水线(mr-hint GitHub REST fallback) * docs(validation): update public acceptance report for P4.4 mr-hint trigger mechanism 新增 P4.4 触发机制优化章节,更新附录 A1(基于含 mr-hint 模块的代码库 真实生成),追加附录 A4.2(SessionStart hook 自动感知 merged PR 的真实 运行场景,含 GitHub REST API fallback 演示与幂等性验证)。 --other=P4.4 MR 合入统一处理流水线验收报告更新 * feat(ai-client): support login shell PATH + extend CLI candidates - 用 bash -lc 包裹探测和调用,解决 ~/.nvm 路径下 CLI 不在 PATH 的问题 - 探测顺序扩展为 claude / claude-internal / codex / codex-internal / codebuddy / workbuddy / openclaw - 新增 shellEscape() 避免 prompt 中单引号破坏 shell 命令 - 超时从 180s 降至 120s - 测试补充 execFileSync mock,修复预存 5 个失败用例 feat(import-mr): interactive codebase review loop + apply suggestions - 新增 reviewCodebaseSuggestions():AI 实时修订循环,用户输入意见 → AI 修订 → 再展示,直到 y 确认或 n 跳过 - --output 模式:apply 后写 codebase-after.md(完整 Markdown) - repoPath 模式:apply 后写回 docs/codebase.md - 修复 codebase.ts apply prompt,确保输出完整文档而非摘要 feat(import): add --existing-codebase option allow 用户显式指定 before codebase.md 路径,不依赖团队仓库; 优先级高于从 repoPath/docs/codebase.md 自动读取 --other=P4.4 MR 合入统一处理流水线优化 * docs(validation): update A4 with real before/after codebase demo 用真实运行产物替换 A4 中的 codebase before/after 内容: - Step 1 before:由 teamai import --workspace 在 PR #2 合入前的代码库生成 - Step 3 suggestions:teamai import --from-mr PR #2 的真实 AI 输出 - Step 4(新增):apply suggestions 后的 codebase-after.md,含 diff 展示 三阶段格式统一,均为同一版本 prompt 的真实运行产物 --other=P4.4 验收报告 A4 codebase before/after 更新 * fix(providers): add TGit REST API fallback + multi-shell CLI detection - mr-fetch.ts: 新增 fetchTGitMRViaApi(),gf CLI 不可用时自动 fallback 到 git.woa.com REST API(使用 ~/.netrc OAuth token); diff 获取失败时降级为空字符串而非中断流程 - ai-client.ts: detectClaudeCli() 对每个候选依次尝试 bash -lc → zsh -lc → which,覆盖 fish/CI 容器等非标准 shell 环境 --other=fix-risk-items * docs: 验收文档typo更正 * fix(ai-client): multi-CLI compat + shell injection hardening - ai-client.ts: detectClaudeCli 解析 CLI 绝对路径并校验存在性, spawn 改为直调 absPath + 参数数组,删除 shellEscape 去 shell; 新增 buildCliArgs 区分 codex/codex-internal 用 'exec' 子命令、 其余 CLI 用 '-p',修复 codex 系 CLI 调用失败问题 - providers/tgit/mr-fetch.ts: execSync → execFileSync 数组参数, 消除 mrIid/repoArg 命令注入风险 - mr-hint.ts: TEAMAI_MR_HINT_CWD 增加 path.resolve + statSync 校验,非法路径静默跳过 - ai-client.test.ts: mock 适配新探测语义(command -v 返回路径 + existsSync=true) --other=phase0-p44-cli-compat-and-security * feat(codebase): align with llm-wiki — frontmatter / index / lint / multi-source 参照 docs/llm-wiki.md 的持久化知识库理念,对 codebase 文档生成做四项优化: - frontmatter:generateCodebaseMd 输出顶部注入 YAML frontmatter (title / lastUpdated / source / generator / schemaVersion), 支持去重旧 frontmatter,便于跨会话溯源 - 索引体系:新增 generateCodebaseIndex 导出,从 codebase.md 抽取 二级章节 + 一句摘要 + 关键词,输出 codebase-index.md,加速 LLM 跨 会话定位 - 健康检查:新增 lintCodebaseMd 导出,AI 检测矛盾/过时/孤儿/缺失 四类问题,返回 LintReport(含 severity 分级),不修改文档 - 多源聚合:generateCodebaseMd 入参新增 learningsSuggestions 与 learningsDir,gatherLearningsContext 内部函数读取 learnings/*.md frontmatter tags 做高频统计,融合 P4.4 MR 建议进 prompt - prompt 模板新增"架构决策与权衡""已知限制与演进方向"两章节 - import.ts workspace 流程串入索引生成 + lint 报告打印 - types.ts 新增 LintIssue / LintReport 接口 - 新增 codebase.test.ts 11 个单元测试,全部通过 --other=phase4-codebase-llm-wiki-alignment * feat(search): codebase-index.md high-weight + skip codebase.md - 新增常量 CODEBASE_INDEX_FILENAME / CODEBASE_FULL_FILENAME / CODEBASE_INDEX_WEIGHT_BOOST(×1.5) - entryFromMdFile:同目录存在 codebase-index.md 时自动跳过 codebase.md,避免全量文档与索引文件重复命中 - search():codebase-index.md 命中时额外乘以 1.5 权重 boost, recall 时章节摘要优先返回 - 兼容 subagent 与 fallback recall 两条路径,boost 在本地索引 阶段生效,无额外 AI 调用开销 - 新增 3 个测试用例(跳过逻辑 / 权重 boost / fallback 路径), search-index.test.ts 共 26 tests 全通过 --other=phase4-codebase-index-search-boost * docs(validation): refresh A1/A4 with llm-wiki-optimized codebase output 用新版 CLI(llm-wiki 优化后)重新执行 A1/A4,更新公开版验收报告产物: - codebase-before.md:含 YAML frontmatter、架构决策与权衡、 已知限制与演进方向两新章节 - codebase-index.md:新增章节索引文件(11 行索引表) - codebase-after.md:PR #2 应用建议后的更新版 - learning.md / codebase-suggestions.json:最新 AI 提炼产物 - 记录实际使用模型:claude-internal v1.1.9(DeepSeek-V3.1-Terminus) - 保持 tgit → [internal] 等脱敏风格 --other=phase0-p44-acceptance-report-refresh * docs(validation): replace codebase-after full content with before/after diff A4 Step 4 中 codebase-after.md 展示方式由全文改为 unified diff, 更直观反映 MR 建议应用后的变更:新增"主要模块"章节(+8 行), 包含 import-local/import-mr/import-iwiki/codebase 四个关键模块说明 --other=phase0-p44-acceptance-report-refresh * docs(roadmap): add Phase 6 — Phase 5 hardening Phase 5 shipped the team-level codebase aggregation pipeline; in shipping it we deliberately deferred several reliability concerns to keep each step deliverable. Phase 6 captures those deferrals as a focused hardening pass — no new capability surface, just turning the Phase 5 deliverables into something safe to run in production indefinitely. Six sub-steps, three independent (P6.0 / P6.1 / P6.5) and three chained (P6.2 → P6.3 → P6.4): - P6.0 Real TGit listOrgRepos (replace stub) - P6.1 Cache lifecycle (LRU + size cap + GC command) - P6.2 Section-level diff with HTML-anchor in-place updates - P6.3 pending-review CLI (review / apply / reject) - P6.4 Domain-drift auto-apply workflow - P6.5 Global codebase doc lint (cross-file consistency) Appendix C dependency table updated with all six rows. Phase 5 leftovers explicitly out of scope here (二级业务域 / 跨仓重复 检测 / search-index 联动 / agent 检索效果量化) are listed under the new "遗留至 Phase 7" block. --other=phase6-roadmap * feat(import): Phase 5 — team-level codebase aggregation Lift teamai-cli's codebase knowledge base from a single-repo, local view into a team-wide, multi-repo aggregation that can be initialized in one command, kept in sync incrementally, and audited end-to-end. The work ships in five sub-steps but is a single coherent feature; merging as one commit per the project's MR rules. What's new ========== P5.0 Business-domain dictionary (src/domains/*) Zod schema + YAML store + AI batch clustering + single-repo recommendation + interactive review CLI + jsonl audit log. Library only; no CLI wiring at this step. P5.1 Single remote repo import `teamai import --from-repo <url>` shallow-clones into ~/.teamai/cache/repos/<provider>/<owner>/<repo>, reuses the existing generateCodebaseMd scanner, and writes a per-repo summary at docs/team-codebase/repos/<slug>.md. Three-tier auth (HTTPS+token / HTTPS-anonymous / SSH); tokens are always redacted in error output. P5.2 Batch import + domain aggregation `--from-repo-list <yaml>` drives a whitelist with per-entry { url, domain, auth, priority } plus org entries (deferred). Failures on one repo don't block siblings. Pure-template aggregator emits docs/team-codebase/domains/domain-<name>.md and a top-level docs/team-codebase/index.md from the per-repo files. Default output root moved to docs/team-codebase to avoid colliding with the existing teamai-cli self-codebase at docs/codebase.md. P5.3 Incremental sync + domain drift `--incremental` skips the full clone when the cache is hit and LAST_SYNC is present, falling back to a fresh shallowClone if fetch fails. After scan, a fresh recommendDomain pass is compared against the existing assignment; divergent recommendations (different domain + confidence > 0.5 + delta > 0.4) land in domains.history.jsonl as a drift event without auto-reassigning. AI failures never block the main flow. CI scheduling examples shipped under examples/ci/ for GitHub Actions and Coding CI. P5.4 Org bootstrap + iWiki dual output `--from-org <org> --bootstrap` lists repos via gh api (paged with /orgs/<o>/repos -> /users/<o>/repos fallback), AI-clusters them, and walks the user through reviewDomains to produce both domains.yaml and repo-whitelist.yaml before chaining into importFromRepoList for the first full sync. TGit listOrgRepos is a stub (Phase 6). `--from-iwiki --iwiki-dual` extracts business APIs / external knowledge / glossary into docs/team-codebase/external-knowledge.md guarded by HTML comment anchors so future syncs replace bodies in place. `--require-review` defers section writes to .teamai/pending-review.jsonl. A small source-conflict helper flags multi-source updates within a 24h window. Filesystem layout introduced ============================ docs/team-codebase/ index.md # business-domain map + repo index domains/domain-*.md # per-domain aggregate repos/<slug>.md # per-repo detail (from --from-repo) external-knowledge.md # iwiki-extracted sections .teamai/ domains.yaml # business-domain dictionary domains.draft.yaml # AI cluster draft domains.history.jsonl # decision audit repo-whitelist.yaml # repo allowlist source-marks.jsonl # multi-source conflict tracking pending-review.jsonl # deferred high-risk changes ~/.teamai/cache/repos/ # shallow-clone cache + LAST_SYNC Surface area ============ CLI flags added to `teamai import`: --from-repo / --from-repo-list / --from-org / --bootstrap --depth / --ssh / --domain / --concurrency / --skip-aggregate --incremental / --max-repos / --exclude-archived --include-pattern / --exclude-pattern / --skip-import --iwiki-dual / --require-review Tests ===== ~95 new unit tests across 14 new test files. Full suite passes 1165/1170 (the one remaining failure in types.test.ts pre-dates this change). tsc clean (only the pre-existing recall.test.ts error is left). Out of scope (tracked in Phase 6) ================================= - TGit listOrgRepos real implementation (currently a stub) - Cache LRU + 5GB cap + GC command - Section-level diff with in-place anchor updates - pending-review CLI to consume the deferred changes - Domain-drift auto-apply workflow - Global codebase doc lint --other=phase5-team-codebase * feat(agents): multi-CLI subagent sync + security hardening - introduce YAML intermediate spec for team agents with renderers for claude / claude-internal / codebuddy / codex / codex-internal / cursor - add agents path for codex / codex-internal / cursor in toolPaths - pull renders per target tool format (.md / .toml); push reverses native files back to YAML, warns and skips on conflicts - legacy agents/*.md still synced to claude-family for back-compat Security fixes: - clone.ts: drop token-in-URL, use http.extraHeader + sanitizeGitUrl - ai-client.ts: execFileSync with shell:false, timeout, CLI whitelist - path-safety.ts: assertSafePath + assertSafeResourceName - import-local.ts: path traversal guard on --dir / output - push.ts --skill / status.ts --agent: assertSafeResourceName - env-commands.ts: env list masked by default, add --reveal flag CSIG fixes: - parseAgentYaml returns ParseResult instead of throwing - fileContentEqual catch logs the error instead of swallowing * feat: Phase 6 — Phase 5 hardening pass Phase 5 shipped the team-codebase aggregation pipeline; in shipping it we deliberately deferred a handful of reliability concerns to keep each step deliverable. Phase 6 closes those gaps -- no new capability surface, just turning the Phase 5 outputs into something safe to run in production indefinitely. Six sub-steps, three independent and three chained, merged here as one commit per project MR rules. What's in the box ================= P6.5 Global codebase doc lint (src/codebase-{lint,cmd}.ts) A deterministic, AI-free cross-file lint over docs/team-codebase and the .teamai/ controls. 12 categories spanning anchor integrity, repo/whitelist consistency, sync staleness, frontmatter completeness, multi-source conflict, etc. `--fix` is intentionally narrow: only mechanical low-risk actions (orphan-md → archived, schemaVersion backfill, index counts refresh). High issues that aren't fixable end up in the skipped list. Exit 1 when any high remains so CI can gate merges. `--json` for downstream tooling. P6.0 TGit listOrgRepos real implementation Replace the Phase 5.4 stub. Uses TGit's GitLab-style OpenAPI: GET https://git.woa.com/api/v3/groups/<encoded-path>/projects with token from the existing gfGetOAuthToken() helper. Multi-level group paths are URL-encoded whole. Pagination loops to maxRepos (200 default). Field mapping matches the GitHub side; primaryLanguage is left blank because the list endpoint doesn't return it. Errors are clean: 404 → "group not found or no access", other HTTP → "TGit API HTTP <code>: <body>", missing token → explicit hint about ~/.netrc / TAI_PAT_TOKEN. Token never reaches a log line or Error message. P6.1 Cache lifecycle (LRU + size cap + GC command) The Phase 5 shallow-clone cache only grew. P6.1 adds an explicit metadata file at ~/.teamai/cache/repos/.cache-index.json; every successful clone/fetch in importFromRepo now refreshes its row via touchCacheEntry() (wrapped in try/catch + log.debug so cache bookkeeping never blocks the import). GC algorithm: stale-evict > 30d, then if over cap (5GB default, override via TEAMAI_CACHE_MAX_BYTES or --max-bytes) evict by ascending last_used until totalBytes ≤ cap*0.8. Eviction order is safe: fs.remove first, only then splice from index; failures land in skipped[]. getCacheStatus auto-heals index entries whose physical directory is gone. CLI: `teamai cache --status | --gc [--dry-run] [--max-bytes N] [--stale-days N] [--json]`. P6.2 Section-level diff + in-place anchor updates The Phase 5 --incremental flag skipped clone but still rewrote docs/team-codebase/repos/<slug>.md whole, producing churn even when the source repo had no real change. P6.2 turns those summaries into anchored sections so unchanged content survives byte-equal across sync runs. Every `## title` block is wrapped in <!-- managed-by: import --from-repo, section: <slug>, source: ..., syncedAt: ... --> ## <title> <body> <!-- /managed-by: <slug> --> Section slugs derive mechanically from the title; duplicate slugs in one file get -2 / -3 suffixes so split / parse stay aligned. generateCodebaseMd is intentionally NOT touched -- the AI still emits one whole markdown blob, and the --workspace path that maintains the teamai-cli self codebase (docs/codebase.md) is untouched. Anchors only apply to per-repo team-codebase outputs; importFromRepo runs the AI output through mergeWithAnchors() per slug: - same body hash → kept (old syncedAt + source preserved) - body changed → rewritten with fresh body + new meta - present in fresh only → added (appended) - present in old only → removed (dropped) The frontmatter rule that actually delivers byte-equal: if all sections are kept, the prelude is also taken from old, otherwise from fresh. Without this tie-break the fresh `lastUpdated: <ISO>` in frontmatter would mtime-bump the file every run. P6.3 pending-review CLI Phase 5.4 wrote .teamai/pending-review.jsonl when --require-review fired but provided no consumer. P6.3 adds the consumer: teamai review # list (sorted by risk desc) teamai review <id> # show details teamai review <id> --apply # apply + drop + audit teamai review <id> --reject [--reason ...] teamai review --all-apply [--max-risk medium|low] Schema upgraded to {id, ts, kind, target, payload, source, risk} with backward-compat: loadPendingReview() normalises old rows on read, computing id from sha1(file|section|ts).slice(0,12) and inferring risk from a small hardcoded set of high-risk sections. iwiki-dual.ts now writes through appendPendingReview() so new rows always land in canonical shape. --apply only handles kind=codebase-section -- it calls patchManagedSection (P6.2), writes a fresh syncedAt, drops the row, appends an audit event. Other kinds gracefully degrade to "not auto-applicable". Atomic write through .tmp+rename so partial writes can't corrupt the jsonl. P6.4 Domain-drift auto-apply workflow P5.3 detected drift and wrote history.jsonl, but gave the user no way to act on it. P6.4 turns drift into an actionable backlog: detectDomainDrift now dual-writes -- besides history.jsonl, every new event lands in pending-review.jsonl as kind=domain-drift, deduped 24h per url so a re-drifting repo stays one open item instead of growing. CLI: teamai domains drift # list teamai domains drift <repoUrl> --apply teamai domains drift <repoUrl> --lock teamai domains drift --apply-all [--threshold 0.8] Apply does the actual reassignment (splice old → push new, auto-create the new domain after a TTY confirmation; non-TTY refuses), updates confidence/signal from the recommendation, audits via appendHistory(reassign), drops the row, then calls regenerateAggregate so domain-*.md and index.md catch up. Lock sets RepoEntry.locked=true and clears stale drift items for the url. apply-all walks confidence-desc, applies above threshold, failures don't abort the batch. CLI surface added ================= teamai cache --status | --gc teamai codebase --lint [--fix] teamai review [id] [--apply | --reject | --all-apply] teamai domains drift [url] [--apply | --lock | --apply-all] Tests ===== ~150 new unit tests across 14 new test files. Full suite passes 1344 / 0 failing on this branch (Phase 5's pre-existing recall.test.ts / types.test.ts failures were fixed in main between Phase 5 and now and stay green here too). tsc clean. Out of scope (tracked as Phase 7 in roadmap_jael.md) ==================================================== - Two-level domain hierarchy (e.g. AI/inference, platform/CI) - Active cross-repo duplicate detection - codebase.md ↔ search-index/recall integration - agent retrieval effectiveness metrics --other=phase6-team-codebase-hardening * chore: stop tracking local drafts (roadmap, validation, .codebuddy) These files exist in the working tree to support local iteration on the team-codebase pipeline (personal roadmap notes, internal phase acceptance reports, and the .codebuddy plan tree), but they should not land in the upstream open-source repository. Add them to .gitignore and untrack them via `git rm --cached` so they: - stay on disk for local use - stop showing up in `git status` for daily work - disappear from the diff against upstream when sending PRs This is a tracking change only -- no code or test behaviour is affected. * docs(readme): trim subagent phase notes and add Phase 5/6 commands Three adjustments based on mentor feedback: 1. Remove the "Recall via subagent (Phase 1)" subsection from both README.md and README.zh-CN.md. The phase-numbered design note was useful during development but reads as roadmap detail on the public README. The downstream paragraph that explains what teamai recall actually returns -- the [<type>] tags plus the four-category index table -- is kept; that one is product behaviour, not phase trivia. 2. Tighten the public-facing tool list to the openly distributed editors (Claude Code, Codex, Cursor, CodeBuddy IDE, OpenClaw, WorkBuddy) and the matching ~/.claude/skills, ~/.codex/skills, ~/.cursor/skills, ~/.codebuddy/skills paths. No code changes: the underlying tool registry, sync paths, usage tracker, agent format dispatch, and AI-client probing are all left untouched, so existing setups keep working as before -- only the public README is shorter. 3. Add the recent commands that landed in PR #6 / #8 to the table: teamai import --from-repo / --from-repo-list / --from-org / --from-iwiki [--iwiki-dual] teamai cache --status | --gc teamai codebase --lint [--fix] teamai review [id] [--apply | --reject | --all-apply] teamai domains drift [url] [--apply | --lock | --apply-all] Documentation only; no code or test changes. * fix(p5-p6): address audit findings — 2 blockers, 1 major, 5 medium Independent review of the Phase 5 / Phase 6 codebase surfaced eight issues; this commit fixes all of them. No new dependencies. Blockers ======== 1. iwiki anchor prefix mismatch broke `teamai review --apply`. iwiki-dual writes `<!-- managed-by: import --from-iwiki, ... -->` but the parser in section-patcher locked the prefix to `--from-repo`, so any pending-review item produced via --iwiki-dual --require-review threw `section not found` on apply. Fix: relax the parsing regex on both sides (parseSections and patchManagedSection) to accept `--from-(?:repo|iwiki)`. Writers stay as-is so the source-of-truth is still recoverable from the anchor metadata. 2. `--from-org` silently dropped private repos. `&type=public` was hardcoded into the GitHub list-org-repos URL, so on enterprise / internal orgs (mostly private) bootstrap produced a near-empty draft without error. Removed the query parameter -- relying on the caller's auth visibility (gh CLI or GITHUB_TOKEN) is the right default and matches GitHub's `type=all`. Major ===== 3. tryEndpointPrefix returned success when the first page was empty. Combined with the bug above, an internal org with all-private repos returned [] from `/orgs/<x>` and never tried `/users/<x>`. Fix: when items.length === 0 && page === 1, return false so the outer code falls back to the user endpoint. Applied to both the gh CLI branch and the fetch branch. Medium ====== 4. ReDoS hardening on section-patcher anchor regexes. `[^>]*?` could be coaxed into exponential backtracking by hostile input. Replaced with `[^>\n]{0,256}?` -- bounded character class plus length cap. Applied to all four open/close anchor regexes (parseSections + patchManagedSection, both directions). 5. 10 MB hard cap on YAML / JSON config reads. loadDomains, loadCacheIndex, loadRepoList, and loadPendingReview now stat() before readFile and reject anything over 10 MB. Stops a malformed or hostile config file from blowing up memory. 6. Final path-safety check before per-repo writeFile. importFromRepo now calls assertSafePath() (an existing helper from PR #7) on the resolved repos/<slug>.md path. Defence-in-depth on top of the existing slug sanitisation; refuses to write outside the configured reposDir even if a future code path generates a weird slug. 7. SSRF guard + 50 MB response cap on outbound HTTP. gh-org and gf-org's fetch path now sets `redirect: 'manual'` and throws on any 3xx, and reads the body as a stream that cancels the reader and throws once total bytes exceed 50 MB. The gh CLI branch is unaffected -- gh handles redirects itself. 8. Backup before mergeWithAnchors fallback. When the existing repo file has corrupt / unclosed anchors, parseSections used to silently throw and importFromRepo fell back to a full-rewrite, losing every prior syncedAt timestamp. It now writes the old file to <repoMdPath>.bak (single overwrite, no accumulation) before doing the fallback wrap, so the prior state is recoverable. Tests ===== - iwiki-review-apply.test.ts (new): end-to-end -- iwiki-dual writes to pending-review.jsonl with --require-review, then `teamai review <id> --apply` is asserted to actually mutate external-knowledge.md (string contains the new body, anchor still says `--from-iwiki`). This was the regression that the parsing-regex fix unblocks. - gh-org.test.ts (new): three cases -- private repos visible without type=public; first-page empty on /orgs/ falls back to /users/; /orgs/ 404 also falls back to /users/. - section-patcher.test.ts: added cases for splitToSections / parseSections / patchManagedSection on iwiki-flavoured anchors. - domains-store / cache-index / repo-list / review-store: each gained a test that writes an actual 11 MB file to a tmpdir and asserts the loader rejects it (not mocked). - import-repo-merge.test.ts: corrupted-anchor case asserts the .bak file appears with the original content. 96 test files / 1356 tests pass / 0 failures (12 added by this commit). tsc has only the pre-existing recall.test.ts error (carried over from main, unrelated). Line length still ≤ 120 across all touched files. --other=fix-p5-p6-audit * fix(test): add missing `type` field to recall.test.ts SearchIndexEntry mock upstream CI's `Type check` step (npx tsc --noEmit) blocked PR #28 with: src/__tests__/recall.test.ts(62,7): error TS2741: Property 'type' is missing in type '{ filename, title, author, date, tags, tokens, votes }' but required in type 'SearchIndexEntry'. SearchIndexEntry was extended in Phase 1 with a required `type: KnowledgeType` field for the multi-bucket index, but the mock factory in the recall vote test didn't follow. Local vitest doesn't type-check the source so the bug never surfaced; upstream CI does run tsc strictly and the typecheck step gates everything else (unit tests, build, e2e), which is why PR #28 failed at the very first step. The test only exercises recallVote's counter path -- the `type` field is never read -- so 'learnings' is just a representative default; behaviour is unchanged. Verified locally: - npx tsc --noEmit → 0 errors (was 1) - npx vitest run recall → 9/9 passing (unchanged) - npx vitest run → 1360/1360 passing (unchanged) - npm run build → success * fix(test): resolve cross-platform path assertion failures in review-cmd.test.ts --story=fix-github-actions-test-failures Replace absolute path assertions with expect.stringContaining() to handle different tmp directory paths on macOS (/private/var) vs Linux (/var) --------- Co-authored-by: jaelgeng <jaelgeng@tencent.com> | 3 个月前 |
TeamAI — Make Every Team AI Native
TeamAI 统一管理团队的 Skills、Rules、MCP 和知识,驾驭 Claude Code、Codex、CodeBuddy、WorkBuddy、OpenCode、Cursor 等 AI Agents。
贡献者
感谢每一位为 TeamAI 贡献代码的伙伴!
由 contrib.rocks 生成。
快速开始
安装
npm install -g teamai-cli
团队管理员 / 个人使用者
在 Git 托管平台(GitHub、GitLab、GitCode、CNB、TGit,或私有 Git 服务)创建共享经验仓库,授予团队成员写权限,然后运行 teamai init https://github.com/yourorg/yourrepo。
还没有团队仓库? 可以从内置了成套 skills、rules、review agents 的模板起步。浏览 teamai-hub org,点 Use this template 生成自己的仓库,再对它执行
teamai init。
团队成员
# 二选一:按你想要的安装范围选择其中一条
# 项目级初始化(默认,资源安装到项目目录下)
cd /path/to/my-project
teamai init https://github.com/yourorg/yourrepo
# 或者,用户级初始化(资源安装到 ~/ 下)
teamai init https://github.com/yourorg/yourrepo --scope user
初始化完成后,每次开启 AI 会话时都会自动拉取管理员发布的 skills / rules 等 Harness 更新,无需手动同步。
完整使用指南:docs/usage-guide.zh-CN.md(English)— 涵盖从团队创建到日常使用的全流程。
产品架构
Team Execution × Team Context (beta) × Team Improvement (beta):
| 层 | 要解决的问题 | 当前 CLI 中的体现 |
|---|---|---|
| Team Execution | 让每个 Agent 按团队的方式工作 | init / pull / push,skills、rules、agents、hooks、MCP、env |
| Team Context (beta) | 让每个 Agent 理解整个团队 | recall、learnings、代码知识图谱、teamwiki... |
| Team Improvement (beta) | 让每一次执行都成为团队能力的积累 | 基于摩擦信号的经验分享、sessions、digest、dashboard... |
功能概览
| Agent | Team Execution | Team Context (beta) | Team Improvement (beta) | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| skills | rules | docs | env | agents | hooks | mcp | learnings | codebase | teamwiki | usage | sessions | dashboard | |
| Claude Code | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Codex | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Cursor | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| CodeBuddy | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| WorkBuddy | ✓ | ✓ | ✓ | ✓ | — | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| OpenCode | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | — | — | — |
| OpenClaw | ✓ | ✓ | ✓ | ✓ | — | — | — | ✓ | ✓ | ✓ | — | — | — |
| Hermes | ✓ | — | ✓ | ✓ | — | — | — | ✓ | ✓ | ✓ | — | — | — |
| DeepSeek Harness | ✓ | — | ✓ | — | — | — | — | ✓ | ✓ | ✓ | — | — | — |
| Qoder | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| ZCode | ✓ | — | ✓ | — | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
Git 托管平台 —— GitHub · GitLab · GitCode · CNB · TGit · 私有 Git 服务。
分发策略
管理员一次配置、随 teamai pull 分发给每位成员的团队级设置:
| 能力 | 命令 | 作用 |
|---|---|---|
| 项目(Projects) | teamai projects |
将工作目录绑定到一个或多个逻辑项目,使其同步该项目的 skills、knowledge 以及隔离的 learnings。与角色正交。 |
| 角色(Roles) | teamai roles |
定义「角色 → 命名空间」映射,让每位成员只同步与自身角色匹配的 skills。 |
| 标签(Tags) | teamai tags |
给 skills / rules 打标签,成员只订阅自己需要的标签。 |
| 订阅源(Sources) | teamai source |
订阅额外的 skill 仓库——其他团队的公开仓库,或本团队内的公共/共享仓库;已订阅的 skills 会在 pull 时自动同步。 |
learnings 隔离:仓库 learnings/ 根目录对所有人共享;learnings/<project-id>/ 为项目私有。详见使用指南。
Team Execution
One Team. One Harness. Every Agent.
TeamAI 把 skills、rules、docs、hooks 统一存放在共享 Git 仓库,通过「push → 评审合并 → pull」的流程分发到每位成员的本地 AI 工具,并支持订阅其他团队或公共仓库的 Harness。
工作原理
teamai push → 创建分支 + MR → reviewer 审批合并
↓
SessionStart hook → teamai pull → 同步到本地 AI 工具
分发内容
每类资源分发到每个 Agent:
| 资源 | 团队仓库中的位置 | 备注 |
|---|---|---|
| Skills | skills/<name>/SKILL.md |
|
| Rules | rules/*.md |
|
| Docs | docs/ |
项目基础文档,默认不全量加载(渐进式披露) |
| Agents | agents/<name>.yaml |
|
| Culture | culture.md |
团队使命、价值观与协作准则——注入各 Agent 的 CLAUDE.md / AGENTS.md,成为每次会话的行事底色 |
| CLAUDE.md | claudemd/*.md |
|
| Env | env/ |
通用环境变量、团队级开关;不建议直接放密钥 |
| Hooks | hooks/hooks.yaml |
|
| MCP | mcp/mcp.yaml |
|
| Packages | teamai.yaml |
目前只支持 npm 包和 Claude 插件 |
| Models | — | 暂时没有对全部 provider 实现 |
文件格式与完整工作流见使用指南。
Team Context (beta)
Every agent understands how the team works.
除了分发 Harness,TeamAI 还把团队沉淀的经验和代码结构组织成可检索的知识库,让 AI 在需要时自动召回。
自动经验沉淀
Session 结束时,Stop hook 按摩擦信号对 session 评分——这些信号表明本次 session 踩到了值得记录的东西:你打断或纠正了 AI、拒绝了某次工具调用,或 AI 反复重试出错的工具。又长又顺(工具调用很多但没有摩擦)的 session 不会触发;真正较劲过的 session 才会。达标后 AI 会显示如下英文提示:
[teamai] This session may contain a problem worth documenting: you interrupted the AI twice, the AI retried failing tools 8 times.
Task: Fix duplicate project-level Hook injection
Consider running /teamai-share-learnings to summarize what you learned and share it with your team.
提示会列出实际触发它的非零摩擦信号;如果能取得首个任务摘要,还会在脱敏、单行化后附上任务上下文。/teamai-share-learnings skill 自动总结 session 经验并推送到团队仓库。每个 session 最多提示一次。团队可在 teamai.yaml 设置 sharing.contributeHint.enabled: false 关闭该提示(成员可用本地配置 contributeHintEnabled 覆盖),Stop hook 的其余功能不受影响。
团队知识检索
让 AI 在执行任务前自动检索团队积累的知识。该功能默认关闭,需显式开启——团队可在 teamai.yaml 设 sharing.recall.enabled: true 作为默认值,成员也可本地覆盖:
teamai recall enable # 开启:部署 teamai-recall 子 agent + 注入引导规则
teamai recall disable # 关闭:移除子 agent 和规则
teamai recall status # 查看生效状态(团队默认 + 用户覆盖)
通过子 agent 检索:开启后 teamai pull 会把内置的 teamai-recall 子 agent 部署到各 AI 工具的 agents/ 目录。AI 在任务开始前调用它——由子 agent 提取关键词、执行检索、读取命中的源文件,最后返回结构化的团队知识摘要。subagent 会先做相关性预检(teamai recall --check),当任务与团队知识无关时直接跳过检索。子 agent 底层调用的仍是 teamai recall 命令,也可手动直接运行:
$ teamai recall "port conflict"
[1/2] MR review caught a port-conflict bug ★1 [user]
Author: member-a | Score: 18.5 | Tags: troubleshooting, networking
[2/2] Deployment configuration best practices [project]
Author: member-b | Score: 12.0 | Tags: deploy, config
Matched: conflict | Missing: port
代码知识图谱
teamai import 将源码仓库解析为 teamwiki/ 下的结构化图谱,实现结构感知的检索:
teamai import --from-repo https://github.com/org/repo
teamai import --from-org myorg # 批量导入所有仓库
teamai codebase --extract /path/to/repo # 本地提取到 teamwiki/
teamai codebase --deep-enrich --project my-service --output /path/to/repo # 从提取结果生成深度知识文档
teamai codebase --reconcile --output /path/to/repo # 将产品文档映射到代码页面
teamai codebase --lint --output /path/to/repo # 检查本地提取的图谱
只要 extract 发现了组件,就会写入 teamwiki/evidence/code/<project>/_manifest.json(包括跳过 AI 增强或增强没有产出的情况),因此 --deep-enrich 可以接着跑。
图谱存储组件、接口、配置和跨仓库依赖边。teamai recall 利用图谱进行增强排名。
当召回命中 codebase 页面时,结果会附带一行 Sources:,列出相关源文件路径,供 agent 直接作为代码改动的入口,无需重新探索代码库。
依赖边来自两条并行的提取轨道,重叠时以 AST 结果优先:
- AST 轨(TypeScript/JavaScript、Python、Go):使用 WASM 版 tree-sitter 解析器,将
import/require、调用点、以及 TSimplements子句解析为精确的文件到文件DEPENDS_ON/REFERENCES/IMPLEMENTS边(标记为code-ast,带置信度权重)。 - 启发式轨(所有语言,含 Java/Rust):基于正则的提取(标记为
code-heuristic),同时覆盖 AST 轨未支持的语言。
WASM 解析器是纯 JavaScript 依赖,无需任何原生编译工具链。若因任何原因加载失败,提取会降级到启发式轨并记录一条 AST_UNAVAILABLE gap。设置 TEAMAI_SKIP_AST=1 可强制仅使用启发式提取。
Team Improvement (beta)
Every execution makes the entire team smarter.
Maintenance
随着 skills 和知识积累,可以把团队不再使用的内容清掉。teamai recall maintenance 会归档低置信度 learnings,并标出过时的 skills、rules 和 docs,供清理或更新:
teamai recall maintenance --prune --dry-run # 预览
teamai recall maintenance --prune --archive # 归档无用 learnings
teamai recall maintenance --update-quality # 为过时 skills / docs 生成更新草稿
洞察团队实际如何使用 AI 工具,也是把 session 中的摩擦转化为共享 Skill、Rule 和知识的起点:
| 能力 | 命令 | 呈现内容 |
|---|---|---|
| 用量(Usage) | teamai digest |
团队周报——近 7 天成功率、对话、活跃时长、估算成本、缓存与纠偏趋势,以及历史累计数据。 |
| 会话(Sessions) | teamai session save |
脱敏的单会话摘要(工具序列、对话轮次、干预次数),喂给周报的 Session Highlights。 |
| 看板(Dashboard) | teamai dashboard |
Web 看板,展示实时会话,以及本机近 7 天相对前 7 天的趋势。 |
| 知识库健康(KB Health) | teamai dashboard → KB Health |
内置于看板的报告页面,展示知识库使用情况与健康状态——各类型覆盖率、高频召回条目、沉默条目、召回趋势、作者贡献及维护控制台。 |
命令一览
| 命令 | 说明 |
|---|---|
teamai init |
初始化:OAuth 登录、关联仓库、注册成员、注入 hooks |
teamai pull |
拉取团队资源并注入到本地 AI 工具 |
teamai push |
推送本地资源到分支并创建合并请求 |
teamai packages [install] [target] |
安装团队 npm 包和 Claude 插件。裸 teamai packages 安装全部;teamai packages install <target> 添加单个并更新声明 |
teamai status |
显示本地与团队仓库的差异及资源数量,包含 namespace 下的技能和子目录中的文档 |
teamai contribute |
将 session 经验分享到团队仓库 |
teamai recall <query> |
搜索团队知识库(BM25 + 图谱增强) |
teamai recall enable/disable/status |
开关或查看 recall 状态 |
teamai recall promote [learningId] |
将高置信度 learning 晋升为正式知识(skills/rules/docs) |
teamai recall maintenance |
维护知识库健康:清理低置信度 learnings、回写置信度、标记过时条目 |
teamai import |
导入知识(--dir、--from-repo、--from-org、--from-repo-list、--from-mr) |
teamai codebase --extract [path] |
提取代码事实并在 teamwiki/ 下构建本地图谱 |
teamai codebase --deep-enrich |
从已提取的 evidence 生成深度知识文档 |
teamai codebase --reconcile |
将产品文档与提取的代码知识进行对账 |
teamai codebase --lint |
知识图谱健康检查 |
teamai ci extract-mr --url <url> |
CI:从 MR 提取知识、发评论、合并后写入 |
teamai members |
查看团队成员 |
teamai projects |
将工作目录绑定到一个或多个逻辑项目 |
teamai roles |
管理团队角色和命名空间 |
teamai tags |
管理基于标签的 skill/rule 过滤 |
teamai skill exclude add/remove/list |
管理不参与本地同步的 skills(使用指南) |
teamai source |
管理 skill 订阅源(其他团队或本团队公共仓库) |
teamai remove <type> <name> |
删除资源并创建 MR |
teamai session save |
将脱敏后的 session 摘要记录到月度日志(--push 可喂给 digest) |
teamai digest |
生成团队周报 |
teamai doctor |
诊断配置问题 |
teamai uninstall |
移除所有 teamai 资源和 hooks |
许可证
贡献
欢迎提交 PR!请先阅读 CONTRIBUTING.md。