teamai-cli 0.24.0-beta.5 → 0.24.0-beta.6
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/CHANGELOG.md +1 -0
- package/README.md +5 -1
- package/README.zh-CN.md +5 -1
- package/dist/index.js +184 -90
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -17,6 +17,7 @@ All notable changes to this project will be documented in this file. See [standa
|
|
|
17
17
|
### 📝 Documentation
|
|
18
18
|
|
|
19
19
|
- Align the public usage guides, drop internal-only details, and cover the missing commands and configuration ([#442](https://github.com/Tencent/teamai-cli/pull/442)).
|
|
20
|
+
- Document `teamai projects` in the bilingual READMEs as the lead distribution control, including learnings isolation ([#490](https://github.com/Tencent/teamai-cli/pull/490), for [#487](https://github.com/Tencent/teamai-cli/issues/487)).
|
|
20
21
|
|
|
21
22
|
## [0.23.0](https://github.com/Tencent/teamai-cli/compare/v0.22.0...v0.23.0) (2026-09-08)
|
|
22
23
|
|
package/README.md
CHANGED
|
@@ -103,10 +103,13 @@ Team-wide settings an admin configures once and delivers to every member on `tea
|
|
|
103
103
|
|
|
104
104
|
| Capability | Command | What it does |
|
|
105
105
|
|------------|---------|--------------|
|
|
106
|
+
| **Projects** | `teamai projects` | Bind a working directory to one or more logical projects so it syncs that project's skills, knowledge, and isolated learnings. Orthogonal to roles. |
|
|
106
107
|
| **Roles** | `teamai roles` | Define role → namespace mappings so each member syncs only the skills for their role. |
|
|
107
108
|
| **Tags** | `teamai tags` | Tag skills / rules so members subscribe to just the tags they need. |
|
|
108
109
|
| **Sources** | `teamai source` | Subscribe to additional skill repos — other teams' public repos, or shared/public repos within your own org; subscribed skills sync automatically on pull. |
|
|
109
110
|
|
|
111
|
+
Learnings isolation: `learnings/` at the repo root is shared with everyone; `learnings/<project-id>/` is project-private. See the [usage guide](docs/usage-guide.md#multi-project-project-as-a-dimension-orthogonal-to-role).
|
|
112
|
+
|
|
110
113
|
## Team Execution
|
|
111
114
|
|
|
112
115
|
> One Team. One Harness. Every Agent.
|
|
@@ -237,7 +240,7 @@ Insight into how the team actually uses its AI tools, and a starting point for t
|
|
|
237
240
|
| `teamai pull` | Pull team resources and inject into local AI tools |
|
|
238
241
|
| `teamai push` | Push local resources to a branch and open a Merge Request |
|
|
239
242
|
| `teamai packages [install] [target]` | Install declared npm packages and Claude plugins; with a target, also update `teamai.yaml`. Bare `teamai packages` installs everything; `teamai packages install <target>` adds one |
|
|
240
|
-
| `teamai status` | Show local vs team repo diff |
|
|
243
|
+
| `teamai status` | Show local vs team repo diff and resource counts, including namespaced skills and nested docs |
|
|
241
244
|
| `teamai contribute` | Share session experience to team repo |
|
|
242
245
|
| `teamai recall <query>` | Search the team knowledge base (BM25 + graph-boost) |
|
|
243
246
|
| `teamai recall enable/disable/status` | Toggle or check recall state |
|
|
@@ -250,6 +253,7 @@ Insight into how the team actually uses its AI tools, and a starting point for t
|
|
|
250
253
|
| `teamai codebase --lint` | Knowledge graph health check |
|
|
251
254
|
| `teamai ci extract-mr --url <url>` | CI: extract knowledge from MR, post comments, write after merge |
|
|
252
255
|
| `teamai members` | List team members |
|
|
256
|
+
| `teamai projects` | Bind a working directory to one or more logical projects |
|
|
253
257
|
| `teamai roles` | Manage team roles and namespaces |
|
|
254
258
|
| `teamai tags` | Manage tag-based skill/rule filtering |
|
|
255
259
|
| `teamai skill exclude add/remove/list` | Manage skills excluded from local sync ([usage guide](docs/usage-guide.md#excluding-skills-you-dont-need)) |
|
package/README.zh-CN.md
CHANGED
|
@@ -103,10 +103,13 @@ teamai init https://github.com/yourorg/yourrepo --scope user
|
|
|
103
103
|
|
|
104
104
|
| 能力 | 命令 | 作用 |
|
|
105
105
|
|------|------|------|
|
|
106
|
+
| **项目(Projects)** | `teamai projects` | 将工作目录绑定到一个或多个逻辑项目,使其同步该项目的 skills、knowledge 以及隔离的 learnings。与角色正交。 |
|
|
106
107
|
| **角色(Roles)** | `teamai roles` | 定义「角色 → 命名空间」映射,让每位成员只同步与自身角色匹配的 skills。 |
|
|
107
108
|
| **标签(Tags)** | `teamai tags` | 给 skills / rules 打标签,成员只订阅自己需要的标签。 |
|
|
108
109
|
| **订阅源(Sources)** | `teamai source` | 订阅额外的 skill 仓库——其他团队的公开仓库,或本团队内的公共/共享仓库;已订阅的 skills 会在 pull 时自动同步。 |
|
|
109
110
|
|
|
111
|
+
learnings 隔离:仓库 `learnings/` 根目录对所有人共享;`learnings/<project-id>/` 为项目私有。详见[使用指南](docs/usage-guide.zh-CN.md#多项目project-作为与-role-正交的维度)。
|
|
112
|
+
|
|
110
113
|
## Team Execution
|
|
111
114
|
|
|
112
115
|
> One Team. One Harness. Every Agent.
|
|
@@ -237,7 +240,7 @@ teamai recall maintenance --update-quality # 为过时 skills / docs 生
|
|
|
237
240
|
| `teamai pull` | 拉取团队资源并注入到本地 AI 工具 |
|
|
238
241
|
| `teamai push` | 推送本地资源到分支并创建合并请求 |
|
|
239
242
|
| `teamai packages [install] [target]` | 安装团队 npm 包和 Claude 插件。裸 `teamai packages` 安装全部;`teamai packages install <target>` 添加单个并更新声明 |
|
|
240
|
-
| `teamai status` |
|
|
243
|
+
| `teamai status` | 显示本地与团队仓库的差异及资源数量,包含 namespace 下的技能和子目录中的文档 |
|
|
241
244
|
| `teamai contribute` | 将 session 经验分享到团队仓库 |
|
|
242
245
|
| `teamai recall <query>` | 搜索团队知识库(BM25 + 图谱增强) |
|
|
243
246
|
| `teamai recall enable/disable/status` | 开关或查看 recall 状态 |
|
|
@@ -250,6 +253,7 @@ teamai recall maintenance --update-quality # 为过时 skills / docs 生
|
|
|
250
253
|
| `teamai codebase --lint` | 知识图谱健康检查 |
|
|
251
254
|
| `teamai ci extract-mr --url <url>` | CI:从 MR 提取知识、发评论、合并后写入 |
|
|
252
255
|
| `teamai members` | 查看团队成员 |
|
|
256
|
+
| `teamai projects` | 将工作目录绑定到一个或多个逻辑项目 |
|
|
253
257
|
| `teamai roles` | 管理团队角色和命名空间 |
|
|
254
258
|
| `teamai tags` | 管理基于标签的 skill/rule 过滤 |
|
|
255
259
|
| `teamai skill exclude add/remove/list` | 管理不参与本地同步的 skills([使用指南](docs/usage-guide.zh-CN.md#排除个人不需要的-skill)) |
|
package/dist/index.js
CHANGED
|
@@ -11974,6 +11974,10 @@ var init_skills = __esm({
|
|
|
11974
11974
|
}
|
|
11975
11975
|
dest = path28.join(wsDir, "skills", item.name);
|
|
11976
11976
|
} else if (tool === "hermes") {
|
|
11977
|
+
if (!await pathExists(getHermesHome())) {
|
|
11978
|
+
log.debug(`Skipping skill sync for ${tool}: tool not installed`);
|
|
11979
|
+
continue;
|
|
11980
|
+
}
|
|
11977
11981
|
dest = path28.join(getHermesHome(), "skills", item.name);
|
|
11978
11982
|
} else {
|
|
11979
11983
|
if (!await ResourceHandler.isToolInstalled(toolPath.skills, baseDir)) {
|
|
@@ -12421,10 +12425,7 @@ var init_docs = __esm({
|
|
|
12421
12425
|
}
|
|
12422
12426
|
async scanTeamForPull(_teamConfig, localConfig) {
|
|
12423
12427
|
const docsDir = path31.join(localConfig.repo.localPath, "docs");
|
|
12424
|
-
if (
|
|
12425
|
-
const files = await listFiles(docsDir);
|
|
12426
|
-
const realFiles = files.filter((f) => !f.startsWith("."));
|
|
12427
|
-
if (realFiles.length === 0) return [];
|
|
12428
|
+
if (await this.countDocFiles(docsDir) === 0) return [];
|
|
12428
12429
|
return [{
|
|
12429
12430
|
name: "docs",
|
|
12430
12431
|
type: "docs",
|
|
@@ -12433,8 +12434,8 @@ var init_docs = __esm({
|
|
|
12433
12434
|
}];
|
|
12434
12435
|
}
|
|
12435
12436
|
async countDocFiles(sourcePath) {
|
|
12436
|
-
const files = await
|
|
12437
|
-
return files.filter((f) =>
|
|
12437
|
+
const files = await listFilesRecursive(sourcePath);
|
|
12438
|
+
return files.filter((f) => f.split("/").every((segment) => !segment.startsWith("."))).length;
|
|
12438
12439
|
}
|
|
12439
12440
|
async pushItem(_item, _teamConfig, _localConfig) {
|
|
12440
12441
|
}
|
|
@@ -23270,13 +23271,10 @@ async function status(options) {
|
|
|
23270
23271
|
log.info("Team resources:");
|
|
23271
23272
|
const repoPath = localConfig.repo.localPath;
|
|
23272
23273
|
const counts = {};
|
|
23273
|
-
|
|
23274
|
-
counts.skills = skillsDirs.length;
|
|
23274
|
+
counts.skills = (await new SkillsHandler().scanTeamForPull(teamConfig, localConfig)).length;
|
|
23275
23275
|
const rulesFiles = (await listFilesRecursive(path66.join(repoPath, "rules"))).filter((f) => f.endsWith(".md"));
|
|
23276
23276
|
counts.rules = rulesFiles.length;
|
|
23277
|
-
|
|
23278
|
-
const docFiles = docsExists ? (await listFiles(path66.join(repoPath, "docs"))).filter((f) => !f.startsWith(".")) : [];
|
|
23279
|
-
counts.docs = docFiles.length;
|
|
23277
|
+
counts.docs = await new DocsHandler().countDocFiles(path66.join(repoPath, "docs"));
|
|
23280
23278
|
const envYamlPath = path66.join(repoPath, "env", "env.yaml");
|
|
23281
23279
|
let envCount = 0;
|
|
23282
23280
|
if (await pathExists(envYamlPath)) {
|
|
@@ -23561,6 +23559,7 @@ var init_status = __esm({
|
|
|
23561
23559
|
init_resources();
|
|
23562
23560
|
init_fs();
|
|
23563
23561
|
init_skills();
|
|
23562
|
+
init_docs();
|
|
23564
23563
|
init_known_agents();
|
|
23565
23564
|
init_agent_skills();
|
|
23566
23565
|
init_types();
|
|
@@ -26985,46 +26984,78 @@ var ai_client_exports = {};
|
|
|
26985
26984
|
__export(ai_client_exports, {
|
|
26986
26985
|
callClaude: () => callClaude,
|
|
26987
26986
|
callClaudeParallel: () => callClaudeParallel,
|
|
26988
|
-
getAICliName: () => getAICliName
|
|
26987
|
+
getAICliName: () => getAICliName,
|
|
26988
|
+
pickWindowsCommand: () => pickWindowsCommand,
|
|
26989
|
+
resolveCliPath: () => resolveCliPath
|
|
26989
26990
|
});
|
|
26990
|
-
import {
|
|
26991
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
26992
|
+
import spawn2 from "cross-spawn";
|
|
26991
26993
|
import { existsSync } from "fs";
|
|
26994
|
+
function pickWindowsCommand(whereOutput) {
|
|
26995
|
+
const lines = whereOutput.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
26996
|
+
for (const ext of WIN_EXEC_EXTENSIONS) {
|
|
26997
|
+
const hit = lines.find((line) => line.toLowerCase().endsWith(ext));
|
|
26998
|
+
if (hit !== void 0) return hit;
|
|
26999
|
+
}
|
|
27000
|
+
return null;
|
|
27001
|
+
}
|
|
27002
|
+
function whereOnWindows(cmd) {
|
|
27003
|
+
try {
|
|
27004
|
+
const out = execFileSync2("where", [cmd], {
|
|
27005
|
+
encoding: "utf8",
|
|
27006
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
27007
|
+
shell: false,
|
|
27008
|
+
timeout: CLI_DETECT_TIMEOUT_MS
|
|
27009
|
+
});
|
|
27010
|
+
const p = pickWindowsCommand(out);
|
|
27011
|
+
return p !== null && existsSync(p) ? p : null;
|
|
27012
|
+
} catch {
|
|
27013
|
+
return null;
|
|
27014
|
+
}
|
|
27015
|
+
}
|
|
27016
|
+
function whichOnPosix(cmd) {
|
|
27017
|
+
try {
|
|
27018
|
+
const p = execFileSync2("bash", ["-lc", `command -v ${cmd}`], {
|
|
27019
|
+
encoding: "utf8",
|
|
27020
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
27021
|
+
shell: false,
|
|
27022
|
+
timeout: CLI_DETECT_TIMEOUT_MS
|
|
27023
|
+
}).trim();
|
|
27024
|
+
if (p && existsSync(p)) return p;
|
|
27025
|
+
} catch {
|
|
27026
|
+
}
|
|
27027
|
+
try {
|
|
27028
|
+
const p = execFileSync2("zsh", ["-lc", `command -v ${cmd}`], {
|
|
27029
|
+
encoding: "utf8",
|
|
27030
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
27031
|
+
shell: false,
|
|
27032
|
+
timeout: CLI_DETECT_TIMEOUT_MS
|
|
27033
|
+
}).trim();
|
|
27034
|
+
if (p && existsSync(p)) return p;
|
|
27035
|
+
} catch {
|
|
27036
|
+
}
|
|
27037
|
+
try {
|
|
27038
|
+
const p = execFileSync2("which", [cmd], {
|
|
27039
|
+
encoding: "utf8",
|
|
27040
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
27041
|
+
shell: false,
|
|
27042
|
+
timeout: CLI_DETECT_TIMEOUT_MS
|
|
27043
|
+
}).trim();
|
|
27044
|
+
if (p && existsSync(p)) return p;
|
|
27045
|
+
} catch {
|
|
27046
|
+
}
|
|
27047
|
+
return null;
|
|
27048
|
+
}
|
|
27049
|
+
function resolveCliPath(cmd, platform = process.platform) {
|
|
27050
|
+
return platform === "win32" ? whereOnWindows(cmd) : whichOnPosix(cmd);
|
|
27051
|
+
}
|
|
26992
27052
|
function detectClaudeCli() {
|
|
26993
|
-
const
|
|
26994
|
-
|
|
26995
|
-
|
|
26996
|
-
const p = execFileSync2("bash", ["-lc", `command -v ${cmd}`], {
|
|
26997
|
-
encoding: "utf8",
|
|
26998
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
26999
|
-
shell: false,
|
|
27000
|
-
timeout: CLI_DETECT_TIMEOUT_MS
|
|
27001
|
-
}).trim();
|
|
27002
|
-
if (p && existsSync(p)) return { cmd, absPath: p };
|
|
27003
|
-
} catch {
|
|
27004
|
-
}
|
|
27005
|
-
try {
|
|
27006
|
-
const p = execFileSync2("zsh", ["-lc", `command -v ${cmd}`], {
|
|
27007
|
-
encoding: "utf8",
|
|
27008
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
27009
|
-
shell: false,
|
|
27010
|
-
timeout: CLI_DETECT_TIMEOUT_MS
|
|
27011
|
-
}).trim();
|
|
27012
|
-
if (p && existsSync(p)) return { cmd, absPath: p };
|
|
27013
|
-
} catch {
|
|
27014
|
-
}
|
|
27015
|
-
try {
|
|
27016
|
-
const p = execFileSync2("which", [cmd], {
|
|
27017
|
-
encoding: "utf8",
|
|
27018
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
27019
|
-
shell: false,
|
|
27020
|
-
timeout: CLI_DETECT_TIMEOUT_MS
|
|
27021
|
-
}).trim();
|
|
27022
|
-
if (p && existsSync(p)) return { cmd, absPath: p };
|
|
27023
|
-
} catch {
|
|
27024
|
-
}
|
|
27053
|
+
for (const cmd of ALLOWED_CLI_CANDIDATES) {
|
|
27054
|
+
const absPath = resolveCliPath(cmd);
|
|
27055
|
+
if (absPath !== null) return { cmd, absPath };
|
|
27025
27056
|
}
|
|
27026
27057
|
throw new Error(
|
|
27027
|
-
"AI CLI
|
|
27058
|
+
"AI CLI unavailable: install one of claude / claude-internal / codex / codex-internal / codebuddy / workbuddy / openclaw"
|
|
27028
27059
|
);
|
|
27029
27060
|
}
|
|
27030
27061
|
function buildCliArgs(cmd, prompt) {
|
|
@@ -27050,8 +27081,8 @@ async function callClaude(prompt, opts) {
|
|
|
27050
27081
|
}
|
|
27051
27082
|
log.debug(`[ai-client] calling ${_cliInfo.cmd}, timeout=${Math.round(timeoutMs / 1e3)}s, prompt=${prompt.slice(0, 60).replace(/\n/g, " ")}...`);
|
|
27052
27083
|
const child = spawn2(_cliInfo.absPath, buildCliArgs(_cliInfo.cmd, prompt), { stdio: ["ignore", "pipe", "pipe"] });
|
|
27053
|
-
child.stdout
|
|
27054
|
-
child.stderr
|
|
27084
|
+
child.stdout?.on("data", (chunk) => chunks.push(chunk));
|
|
27085
|
+
child.stderr?.on("data", (chunk) => errChunks.push(chunk));
|
|
27055
27086
|
const timer = setTimeout(() => {
|
|
27056
27087
|
child.kill();
|
|
27057
27088
|
const seconds = Math.round(timeoutMs / 1e3);
|
|
@@ -27132,7 +27163,7 @@ async function runWithConcurrency(tasks, concurrency) {
|
|
|
27132
27163
|
await Promise.all(promises);
|
|
27133
27164
|
return results;
|
|
27134
27165
|
}
|
|
27135
|
-
var ALLOWED_CLI_CANDIDATES, CLI_DETECT_TIMEOUT_MS, DEFAULT_TIMEOUT_MS, DEFAULT_CONCURRENCY, _cliInfo;
|
|
27166
|
+
var ALLOWED_CLI_CANDIDATES, CLI_DETECT_TIMEOUT_MS, WIN_EXEC_EXTENSIONS, DEFAULT_TIMEOUT_MS, DEFAULT_CONCURRENCY, _cliInfo;
|
|
27136
27167
|
var init_ai_client = __esm({
|
|
27137
27168
|
"src/utils/ai-client.ts"() {
|
|
27138
27169
|
"use strict";
|
|
@@ -27149,6 +27180,7 @@ var init_ai_client = __esm({
|
|
|
27149
27180
|
"openclaw"
|
|
27150
27181
|
];
|
|
27151
27182
|
CLI_DETECT_TIMEOUT_MS = 5e3;
|
|
27183
|
+
WIN_EXEC_EXTENSIONS = [".exe", ".cmd", ".bat"];
|
|
27152
27184
|
DEFAULT_TIMEOUT_MS = 6e5;
|
|
27153
27185
|
DEFAULT_CONCURRENCY = 3;
|
|
27154
27186
|
}
|
|
@@ -37913,7 +37945,7 @@ var deep_enrich_exports = {};
|
|
|
37913
37945
|
__export(deep_enrich_exports, {
|
|
37914
37946
|
deepEnrich: () => deepEnrich
|
|
37915
37947
|
});
|
|
37916
|
-
import { readFile as readFile11, writeFile as writeFile11, readdir as readdir4, mkdir as mkdir8 } from "fs/promises";
|
|
37948
|
+
import { readFile as readFile11, writeFile as writeFile11, readdir as readdir4, mkdir as mkdir8, unlink as unlink2 } from "fs/promises";
|
|
37917
37949
|
import path113 from "path";
|
|
37918
37950
|
async function readFileSafe4(filePath) {
|
|
37919
37951
|
try {
|
|
@@ -38370,6 +38402,10 @@ async function runPhaseAiGraph(opts, ctx, docsDir) {
|
|
|
38370
38402
|
const g6 = buildG6Content(project, ctx.manifest);
|
|
38371
38403
|
await writeFile11(path113.join(docsDir, "graph-g6-multihop.md"), g6, "utf-8");
|
|
38372
38404
|
log.debug(`deep-enrich[${project}]: G6 multi-hop analysis written`);
|
|
38405
|
+
const g5Path = path113.join(docsDir, "graph-g5-scenarios.md");
|
|
38406
|
+
if (await pathExists(g5Path)) {
|
|
38407
|
+
return { g5Generated: true, g6Generated: g6HasEdges };
|
|
38408
|
+
}
|
|
38373
38409
|
let g5Generated = false;
|
|
38374
38410
|
if (ctx.moduleDocs.size < 2) {
|
|
38375
38411
|
log.warn(`deep-enrich[${project}]: Insufficient modules (${ctx.moduleDocs.size} < 2), skipping G5`);
|
|
@@ -38448,6 +38484,47 @@ async function runPhaseIndexEnhance(opts, ctx, docsDir, graphFlags) {
|
|
|
38448
38484
|
}
|
|
38449
38485
|
log.debug(`deep-enrich[${project}]: Index incremental update complete`);
|
|
38450
38486
|
}
|
|
38487
|
+
function architectureDocPath(docsDir) {
|
|
38488
|
+
return path113.join(docsDir, "architecture.md");
|
|
38489
|
+
}
|
|
38490
|
+
async function existingComponentSlugs(docsDir, slugs) {
|
|
38491
|
+
const found = [];
|
|
38492
|
+
for (const slug of slugs) {
|
|
38493
|
+
try {
|
|
38494
|
+
assertSafeResourceName(slug);
|
|
38495
|
+
} catch {
|
|
38496
|
+
continue;
|
|
38497
|
+
}
|
|
38498
|
+
if (await pathExists(path113.join(docsDir, `${slug}.md`))) found.push(slug);
|
|
38499
|
+
}
|
|
38500
|
+
return found;
|
|
38501
|
+
}
|
|
38502
|
+
async function collectMissingAiDocs(docsDir, slugs) {
|
|
38503
|
+
const existing = new Set(await existingComponentSlugs(docsDir, slugs));
|
|
38504
|
+
return {
|
|
38505
|
+
missingComponents: slugs.filter((slug) => !existing.has(slug)),
|
|
38506
|
+
missingArchitecture: !await pathExists(architectureDocPath(docsDir))
|
|
38507
|
+
};
|
|
38508
|
+
}
|
|
38509
|
+
async function removeIfExists(filePath) {
|
|
38510
|
+
try {
|
|
38511
|
+
await unlink2(filePath);
|
|
38512
|
+
} catch (err) {
|
|
38513
|
+
if (err.code !== "ENOENT") throw err;
|
|
38514
|
+
}
|
|
38515
|
+
}
|
|
38516
|
+
async function invalidateStaleAiDocs(docsDir, slugs) {
|
|
38517
|
+
for (const slug of slugs) {
|
|
38518
|
+
try {
|
|
38519
|
+
assertSafeResourceName(slug);
|
|
38520
|
+
} catch {
|
|
38521
|
+
continue;
|
|
38522
|
+
}
|
|
38523
|
+
await removeIfExists(path113.join(docsDir, `${slug}.md`));
|
|
38524
|
+
}
|
|
38525
|
+
await removeIfExists(architectureDocPath(docsDir));
|
|
38526
|
+
await removeIfExists(path113.join(docsDir, "graph-g5-scenarios.md"));
|
|
38527
|
+
}
|
|
38451
38528
|
async function deepEnrich(opts) {
|
|
38452
38529
|
const { project, evidenceDir } = opts;
|
|
38453
38530
|
const docsDir = path113.join(evidenceDir, "docs");
|
|
@@ -38456,7 +38533,7 @@ async function deepEnrich(opts) {
|
|
|
38456
38533
|
let components = ctx.manifest.components ?? [];
|
|
38457
38534
|
if (components.length === 0) {
|
|
38458
38535
|
log.warn(`deep-enrich[${project}]: No components in _manifest.json, aborting`);
|
|
38459
|
-
return;
|
|
38536
|
+
return { project, complete: false, missingComponents: [], missingArchitecture: true };
|
|
38460
38537
|
}
|
|
38461
38538
|
if (opts.maxModules && components.length > opts.maxModules) {
|
|
38462
38539
|
log.info(`deep-enrich[${project}]: Limited to first ${opts.maxModules} components (${components.length} total)`);
|
|
@@ -38465,52 +38542,55 @@ async function deepEnrich(opts) {
|
|
|
38465
38542
|
}
|
|
38466
38543
|
const allSlugs = components.map((c) => c.slug);
|
|
38467
38544
|
const progress = await loadProgress(evidenceDir, project, allSlugs);
|
|
38468
|
-
|
|
38469
|
-
|
|
38470
|
-
|
|
38471
|
-
|
|
38472
|
-
|
|
38473
|
-
|
|
38474
|
-
|
|
38475
|
-
|
|
38476
|
-
|
|
38477
|
-
|
|
38478
|
-
|
|
38479
|
-
progress.componentsDone = [];
|
|
38480
|
-
progress.componentsPending = [...allSlugs];
|
|
38481
|
-
progress.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
38482
|
-
await saveProgress(evidenceDir, progress);
|
|
38483
|
-
}
|
|
38545
|
+
const manifestTime = ctx.manifest.generatedAt;
|
|
38546
|
+
if (manifestTime && progress.startedAt < manifestTime) {
|
|
38547
|
+
log.info(
|
|
38548
|
+
`deep-enrich[${project}]: stale progress detected (manifest newer than progress.startedAt), invalidating AI docs`
|
|
38549
|
+
);
|
|
38550
|
+
await invalidateStaleAiDocs(docsDir, allSlugs);
|
|
38551
|
+
progress.phase = "pending";
|
|
38552
|
+
progress.componentsDone = [];
|
|
38553
|
+
progress.componentsPending = [...allSlugs];
|
|
38554
|
+
progress.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
38555
|
+
await saveProgress(evidenceDir, progress);
|
|
38484
38556
|
}
|
|
38485
|
-
|
|
38557
|
+
const existing = await existingComponentSlugs(docsDir, allSlugs);
|
|
38558
|
+
progress.componentsDone = existing;
|
|
38559
|
+
progress.componentsPending = allSlugs.filter((slug) => !existing.includes(slug));
|
|
38560
|
+
if (progress.componentsPending.length > 0) {
|
|
38486
38561
|
progress.phase = "components";
|
|
38487
38562
|
await saveProgress(evidenceDir, progress);
|
|
38488
38563
|
await runPhaseComponents(opts, ctx, progress, docsDir);
|
|
38489
38564
|
}
|
|
38490
|
-
if (
|
|
38565
|
+
if (!await pathExists(architectureDocPath(docsDir))) {
|
|
38491
38566
|
progress.phase = "architecture";
|
|
38492
38567
|
await saveProgress(evidenceDir, progress);
|
|
38493
38568
|
await runPhaseArchitecture(opts, ctx, docsDir);
|
|
38494
38569
|
}
|
|
38495
|
-
|
|
38496
|
-
progress.phase = "graph";
|
|
38497
|
-
await saveProgress(evidenceDir, progress);
|
|
38498
|
-
await runPhaseGraph(opts, ctx, docsDir);
|
|
38499
|
-
}
|
|
38500
|
-
let graphFlags = { g5Generated: false, g6Generated: true };
|
|
38501
|
-
if (progress.phase === "graph" || progress.phase === "ai-graph") {
|
|
38502
|
-
progress.phase = "ai-graph";
|
|
38503
|
-
await saveProgress(evidenceDir, progress);
|
|
38504
|
-
graphFlags = await runPhaseAiGraph(opts, ctx, docsDir);
|
|
38505
|
-
}
|
|
38506
|
-
if (progress.phase === "ai-graph" || progress.phase === "index-enhance") {
|
|
38507
|
-
progress.phase = "index-enhance";
|
|
38508
|
-
await saveProgress(evidenceDir, progress);
|
|
38509
|
-
await runPhaseIndexEnhance(opts, ctx, docsDir, graphFlags);
|
|
38510
|
-
}
|
|
38511
|
-
progress.phase = "done";
|
|
38570
|
+
progress.phase = "graph";
|
|
38512
38571
|
await saveProgress(evidenceDir, progress);
|
|
38513
|
-
|
|
38572
|
+
await runPhaseGraph(opts, ctx, docsDir);
|
|
38573
|
+
progress.phase = "ai-graph";
|
|
38574
|
+
await saveProgress(evidenceDir, progress);
|
|
38575
|
+
const graphFlags = await runPhaseAiGraph(opts, ctx, docsDir);
|
|
38576
|
+
progress.phase = "index-enhance";
|
|
38577
|
+
await saveProgress(evidenceDir, progress);
|
|
38578
|
+
await runPhaseIndexEnhance(opts, ctx, docsDir, graphFlags);
|
|
38579
|
+
const missing = await collectMissingAiDocs(docsDir, allSlugs);
|
|
38580
|
+
const complete = missing.missingComponents.length === 0 && !missing.missingArchitecture;
|
|
38581
|
+
progress.componentsDone = allSlugs.filter((slug) => !missing.missingComponents.includes(slug));
|
|
38582
|
+
progress.componentsPending = missing.missingComponents;
|
|
38583
|
+
progress.phase = complete ? "done" : missing.missingComponents.length > 0 ? "components" : "architecture";
|
|
38584
|
+
await saveProgress(evidenceDir, progress);
|
|
38585
|
+
if (complete) {
|
|
38586
|
+
log.success(`deep-enrich[${project}]: Deep knowledge generation complete`);
|
|
38587
|
+
} else {
|
|
38588
|
+
const missingComps = missing.missingComponents.join(", ") || "(none)";
|
|
38589
|
+
log.warn(
|
|
38590
|
+
`deep-enrich[${project}]: incomplete \u2014 missing component docs: ${missingComps}, architecture: ${missing.missingArchitecture}`
|
|
38591
|
+
);
|
|
38592
|
+
}
|
|
38593
|
+
return { project, complete, ...missing };
|
|
38514
38594
|
}
|
|
38515
38595
|
var PROGRESS_PATH_SUBDIR, PROGRESS_FILENAME, VALID_PHASES;
|
|
38516
38596
|
var init_deep_enrich = __esm({
|
|
@@ -40670,12 +40750,26 @@ async function codebaseCmd(opts) {
|
|
|
40670
40750
|
return;
|
|
40671
40751
|
}
|
|
40672
40752
|
const { deepEnrich: deepEnrich2 } = await Promise.resolve().then(() => (init_deep_enrich(), deep_enrich_exports));
|
|
40673
|
-
await deepEnrich2({ project, evidenceDir, wikiRoot: teamwikiDir });
|
|
40753
|
+
const result = await deepEnrich2({ project, evidenceDir, wikiRoot: teamwikiDir });
|
|
40674
40754
|
if (opts.json) {
|
|
40675
|
-
console.log(JSON.stringify({
|
|
40676
|
-
|
|
40755
|
+
console.log(JSON.stringify({
|
|
40756
|
+
project,
|
|
40757
|
+
evidenceDir,
|
|
40758
|
+
complete: result.complete,
|
|
40759
|
+
missingComponents: result.missingComponents,
|
|
40760
|
+
missingArchitecture: result.missingArchitecture
|
|
40761
|
+
}, null, 2));
|
|
40762
|
+
} else if (result.complete) {
|
|
40677
40763
|
console.log(`Deep enrichment complete: project=${project}`);
|
|
40764
|
+
} else {
|
|
40765
|
+
const parts = [];
|
|
40766
|
+
if (result.missingComponents.length > 0) {
|
|
40767
|
+
parts.push(`missing component docs: ${result.missingComponents.join(", ")}`);
|
|
40768
|
+
}
|
|
40769
|
+
if (result.missingArchitecture) parts.push("missing architecture.md");
|
|
40770
|
+
console.log(`Deep enrichment incomplete: project=${project}, ${parts.join(", ")}`);
|
|
40678
40771
|
}
|
|
40772
|
+
if (!result.complete) process.exitCode = 1;
|
|
40679
40773
|
return;
|
|
40680
40774
|
}
|
|
40681
40775
|
if (opts.reconcile) {
|