teamai-cli 0.24.0-beta.8 → 0.24.0-beta.9
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 +2 -0
- package/README.md +2 -0
- package/README.zh-CN.md +2 -0
- package/dist/index.js +410 -153
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,8 @@ All notable changes to this project will be documented in this file. See [standa
|
|
|
11
11
|
|
|
12
12
|
### 🐛 Bug Fixes
|
|
13
13
|
|
|
14
|
+
- The GitHub and CNB providers resolve their CLI to a launchable absolute path and start it through cross-spawn, so on Windows they no longer answer "installed" while every call fails silently ([#520](https://github.com/Tencent/teamai-cli/pull/520)).
|
|
15
|
+
- `enabledAgents` now also gates CLI builtin deploy, CLAUDE.md-class injects, and last-pull skip-sync targets, so an already-installed tool outside the whitelist is not written to ([#510](https://github.com/Tencent/teamai-cli/issues/510)).
|
|
14
16
|
- `teamai status` counts rule files in subdirectories recursively ([#437](https://github.com/Tencent/teamai-cli/pull/437)).
|
|
15
17
|
- Codex Stop-phase contribution hints are deferred to the next prompt, so the host no longer rejects `additionalContext` ([#441](https://github.com/Tencent/teamai-cli/pull/441)).
|
|
16
18
|
|
package/README.md
CHANGED
|
@@ -199,6 +199,8 @@ teamai codebase --reconcile --output /path/to/repo # map product docs to code pa
|
|
|
199
199
|
teamai codebase --lint --output /path/to/repo # check the locally extracted graph
|
|
200
200
|
```
|
|
201
201
|
|
|
202
|
+
Extract writes `teamwiki/evidence/code/<project>/_manifest.json` even when AI enrichment is skipped or produces nothing, so `--deep-enrich` can start.
|
|
203
|
+
|
|
202
204
|
The graph stores components, interfaces, configs, and cross-repo import edges. `teamai recall` uses it for graph-boosted re-ranking.
|
|
203
205
|
When a recall hit comes from a codebase page, the result includes a `Sources:` line listing the relevant source file paths — giving agents a direct starting point for code changes instead of re-exploring the repo.
|
|
204
206
|
|
package/README.zh-CN.md
CHANGED
|
@@ -199,6 +199,8 @@ teamai codebase --reconcile --output /path/to/repo # 将产品文档映射到代
|
|
|
199
199
|
teamai codebase --lint --output /path/to/repo # 检查本地提取的图谱
|
|
200
200
|
```
|
|
201
201
|
|
|
202
|
+
只要 extract 发现了组件,就会写入 `teamwiki/evidence/code/<project>/_manifest.json`(包括跳过 AI 增强或增强没有产出的情况),因此 `--deep-enrich` 可以接着跑。
|
|
203
|
+
|
|
202
204
|
图谱存储组件、接口、配置和跨仓库依赖边。`teamai recall` 利用图谱进行增强排名。
|
|
203
205
|
当召回命中 codebase 页面时,结果会附带一行 `Sources:`,列出相关源文件路径,供 agent 直接作为代码改动的入口,无需重新探索代码库。
|
|
204
206
|
|
package/dist/index.js
CHANGED
|
@@ -171,6 +171,7 @@ __export(fs_exports, {
|
|
|
171
171
|
listFiles: () => listFiles,
|
|
172
172
|
listFilesRecursive: () => listFilesRecursive,
|
|
173
173
|
pathExists: () => pathExists,
|
|
174
|
+
pruneEmptyDirs: () => pruneEmptyDirs,
|
|
174
175
|
readFileSafe: () => readFileSafe,
|
|
175
176
|
readJson: () => readJson,
|
|
176
177
|
remove: () => remove,
|
|
@@ -255,6 +256,32 @@ async function copyDir(src, dest) {
|
|
|
255
256
|
filter: (srcPath) => !isIgnored(path2.basename(srcPath))
|
|
256
257
|
});
|
|
257
258
|
}
|
|
259
|
+
async function pruneEmptyDirs(target) {
|
|
260
|
+
const expanded = expandHome(target);
|
|
261
|
+
let entries;
|
|
262
|
+
try {
|
|
263
|
+
const stat8 = await fse.lstat(expanded);
|
|
264
|
+
if (!stat8.isDirectory() || stat8.isSymbolicLink()) return false;
|
|
265
|
+
entries = await fse.readdir(expanded, { withFileTypes: true });
|
|
266
|
+
} catch {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
let empty = true;
|
|
270
|
+
for (const entry of entries) {
|
|
271
|
+
if (entry.isDirectory()) {
|
|
272
|
+
if (!await pruneEmptyDirs(path2.join(expanded, entry.name))) empty = false;
|
|
273
|
+
} else {
|
|
274
|
+
empty = false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (!empty) return false;
|
|
278
|
+
try {
|
|
279
|
+
await fse.rmdir(expanded);
|
|
280
|
+
return true;
|
|
281
|
+
} catch {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
258
285
|
async function copyFile(src, dest) {
|
|
259
286
|
const destExpanded = expandHome(dest);
|
|
260
287
|
await fse.ensureDir(path2.dirname(destExpanded));
|
|
@@ -3215,7 +3242,7 @@ async function deployBuiltinRules(teamConfig, localConfig, options) {
|
|
|
3215
3242
|
log.debug(`Skipping built-in rules for ${tool}: tool not installed`);
|
|
3216
3243
|
continue;
|
|
3217
3244
|
}
|
|
3218
|
-
if (localConfig &&
|
|
3245
|
+
if (localConfig && isAgentExcluded(localConfig, tool)) continue;
|
|
3219
3246
|
const rulesDir = path14.join(baseDir, toolPath.rules);
|
|
3220
3247
|
if (!await pathExists(rulesDir)) continue;
|
|
3221
3248
|
try {
|
|
@@ -3454,7 +3481,7 @@ var init_pre_push_sync = __esm({
|
|
|
3454
3481
|
});
|
|
3455
3482
|
|
|
3456
3483
|
// src/providers/types.ts
|
|
3457
|
-
var RepoNotFoundError;
|
|
3484
|
+
var RepoNotFoundError, OrganizationNotFoundError, RepoCreatePermissionError;
|
|
3458
3485
|
var init_types2 = __esm({
|
|
3459
3486
|
"src/providers/types.ts"() {
|
|
3460
3487
|
"use strict";
|
|
@@ -3464,6 +3491,26 @@ var init_types2 = __esm({
|
|
|
3464
3491
|
this.name = "RepoNotFoundError";
|
|
3465
3492
|
}
|
|
3466
3493
|
};
|
|
3494
|
+
OrganizationNotFoundError = class extends Error {
|
|
3495
|
+
org;
|
|
3496
|
+
createUrl;
|
|
3497
|
+
constructor(org, createUrl) {
|
|
3498
|
+
super(`Organization "${org}" not found.`);
|
|
3499
|
+
this.name = "OrganizationNotFoundError";
|
|
3500
|
+
this.org = org;
|
|
3501
|
+
this.createUrl = createUrl;
|
|
3502
|
+
}
|
|
3503
|
+
};
|
|
3504
|
+
RepoCreatePermissionError = class extends Error {
|
|
3505
|
+
repo;
|
|
3506
|
+
createUrl;
|
|
3507
|
+
constructor(repo, createUrl) {
|
|
3508
|
+
super(`No permission to create repo "${repo}".`);
|
|
3509
|
+
this.name = "RepoCreatePermissionError";
|
|
3510
|
+
this.repo = repo;
|
|
3511
|
+
this.createUrl = createUrl;
|
|
3512
|
+
}
|
|
3513
|
+
};
|
|
3467
3514
|
}
|
|
3468
3515
|
});
|
|
3469
3516
|
|
|
@@ -4026,20 +4073,82 @@ var init_tgit = __esm({
|
|
|
4026
4073
|
}
|
|
4027
4074
|
});
|
|
4028
4075
|
|
|
4029
|
-
// src/
|
|
4030
|
-
import {
|
|
4031
|
-
|
|
4076
|
+
// src/utils/cli-path.ts
|
|
4077
|
+
import { execFileSync } from "child_process";
|
|
4078
|
+
import { existsSync } from "fs";
|
|
4079
|
+
function pickWindowsCommand(whereOutput) {
|
|
4080
|
+
const lines = whereOutput.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
4081
|
+
for (const ext of WIN_EXEC_EXTENSIONS) {
|
|
4082
|
+
const hit = lines.find((line) => line.toLowerCase().endsWith(ext));
|
|
4083
|
+
if (hit !== void 0) return hit;
|
|
4084
|
+
}
|
|
4085
|
+
return null;
|
|
4086
|
+
}
|
|
4087
|
+
function whereOnWindows(cmd) {
|
|
4032
4088
|
try {
|
|
4033
|
-
const
|
|
4034
|
-
encoding: "
|
|
4035
|
-
stdio: ["
|
|
4089
|
+
const out = execFileSync("where", [cmd], {
|
|
4090
|
+
encoding: "utf8",
|
|
4091
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4092
|
+
shell: false,
|
|
4093
|
+
timeout: CLI_DETECT_TIMEOUT_MS
|
|
4036
4094
|
});
|
|
4037
|
-
const
|
|
4038
|
-
return
|
|
4095
|
+
const p = pickWindowsCommand(out);
|
|
4096
|
+
return p !== null && existsSync(p) ? p : null;
|
|
4039
4097
|
} catch {
|
|
4040
4098
|
return null;
|
|
4041
4099
|
}
|
|
4042
4100
|
}
|
|
4101
|
+
function whichOnPosix(cmd) {
|
|
4102
|
+
try {
|
|
4103
|
+
const p = execFileSync("bash", ["-lc", `command -v ${cmd}`], {
|
|
4104
|
+
encoding: "utf8",
|
|
4105
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4106
|
+
shell: false,
|
|
4107
|
+
timeout: CLI_DETECT_TIMEOUT_MS
|
|
4108
|
+
}).trim();
|
|
4109
|
+
if (p && existsSync(p)) return p;
|
|
4110
|
+
} catch {
|
|
4111
|
+
}
|
|
4112
|
+
try {
|
|
4113
|
+
const p = execFileSync("zsh", ["-lc", `command -v ${cmd}`], {
|
|
4114
|
+
encoding: "utf8",
|
|
4115
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4116
|
+
shell: false,
|
|
4117
|
+
timeout: CLI_DETECT_TIMEOUT_MS
|
|
4118
|
+
}).trim();
|
|
4119
|
+
if (p && existsSync(p)) return p;
|
|
4120
|
+
} catch {
|
|
4121
|
+
}
|
|
4122
|
+
try {
|
|
4123
|
+
const p = execFileSync("which", [cmd], {
|
|
4124
|
+
encoding: "utf8",
|
|
4125
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
4126
|
+
shell: false,
|
|
4127
|
+
timeout: CLI_DETECT_TIMEOUT_MS
|
|
4128
|
+
}).trim();
|
|
4129
|
+
if (p && existsSync(p)) return p;
|
|
4130
|
+
} catch {
|
|
4131
|
+
}
|
|
4132
|
+
return null;
|
|
4133
|
+
}
|
|
4134
|
+
function resolveCliPath(cmd, platform = process.platform) {
|
|
4135
|
+
return platform === "win32" ? whereOnWindows(cmd) : whichOnPosix(cmd);
|
|
4136
|
+
}
|
|
4137
|
+
var CLI_DETECT_TIMEOUT_MS, WIN_EXEC_EXTENSIONS;
|
|
4138
|
+
var init_cli_path = __esm({
|
|
4139
|
+
"src/utils/cli-path.ts"() {
|
|
4140
|
+
"use strict";
|
|
4141
|
+
CLI_DETECT_TIMEOUT_MS = 5e3;
|
|
4142
|
+
WIN_EXEC_EXTENSIONS = [".exe", ".cmd", ".bat"];
|
|
4143
|
+
}
|
|
4144
|
+
});
|
|
4145
|
+
|
|
4146
|
+
// src/providers/github/gh-cli.ts
|
|
4147
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
4148
|
+
import crossSpawn from "cross-spawn";
|
|
4149
|
+
function getGhPath() {
|
|
4150
|
+
return resolveCliPath("gh");
|
|
4151
|
+
}
|
|
4043
4152
|
function isGhInstalled() {
|
|
4044
4153
|
return getGhPath() !== null;
|
|
4045
4154
|
}
|
|
@@ -4052,14 +4161,14 @@ function ghExec(args, options) {
|
|
|
4052
4161
|
}
|
|
4053
4162
|
log.debug(`gh exec: ${ghPath} ${args.join(" ")}`);
|
|
4054
4163
|
if (options?.inheritStdio) {
|
|
4055
|
-
const result2 =
|
|
4164
|
+
const result2 = crossSpawn.sync(ghPath, args, {
|
|
4056
4165
|
stdio: "inherit",
|
|
4057
4166
|
env: { ...process.env, ...options.env ?? {} },
|
|
4058
4167
|
cwd: options.cwd
|
|
4059
4168
|
});
|
|
4060
4169
|
return { stdout: "", stderr: "", status: result2.status ?? 1 };
|
|
4061
4170
|
}
|
|
4062
|
-
const result =
|
|
4171
|
+
const result = crossSpawn.sync(ghPath, args, {
|
|
4063
4172
|
env: { ...process.env, ...options?.env ?? {} },
|
|
4064
4173
|
encoding: "utf-8",
|
|
4065
4174
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -4307,6 +4416,7 @@ var init_gh_cli = __esm({
|
|
|
4307
4416
|
"src/providers/github/gh-cli.ts"() {
|
|
4308
4417
|
"use strict";
|
|
4309
4418
|
init_logger();
|
|
4419
|
+
init_cli_path();
|
|
4310
4420
|
GITHUB_API = "https://api.github.com";
|
|
4311
4421
|
RepoNotFoundError3 = class extends Error {
|
|
4312
4422
|
constructor(repo) {
|
|
@@ -4569,14 +4679,20 @@ var init_github = __esm({
|
|
|
4569
4679
|
});
|
|
4570
4680
|
|
|
4571
4681
|
// src/providers/cnb/cnb-cli.ts
|
|
4572
|
-
import { execSync as
|
|
4682
|
+
import { execSync as execSync2, spawnSync as spawnSync3 } from "child_process";
|
|
4683
|
+
import crossSpawn2 from "cross-spawn";
|
|
4573
4684
|
function cnbExec(args, options) {
|
|
4574
|
-
|
|
4685
|
+
const cnbPath = resolveCliPath("cnb");
|
|
4686
|
+
if (!cnbPath) {
|
|
4687
|
+
log.debug("cnb CLI not found on PATH");
|
|
4688
|
+
return { stdout: "", stderr: "cnb CLI not found on PATH", status: 127 };
|
|
4689
|
+
}
|
|
4690
|
+
log.debug(`cnb exec: ${cnbPath} ${args.join(" ")}`);
|
|
4575
4691
|
if (options?.inheritStdio) {
|
|
4576
|
-
const r2 =
|
|
4692
|
+
const r2 = crossSpawn2.sync(cnbPath, args, { stdio: "inherit", env: { ...process.env }, cwd: options.cwd });
|
|
4577
4693
|
return { stdout: "", stderr: "", status: r2.status ?? 1 };
|
|
4578
4694
|
}
|
|
4579
|
-
const r =
|
|
4695
|
+
const r = crossSpawn2.sync(cnbPath, args, {
|
|
4580
4696
|
env: { ...process.env },
|
|
4581
4697
|
encoding: "utf-8",
|
|
4582
4698
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -4598,12 +4714,7 @@ function assertCnbApiOk(out, action) {
|
|
|
4598
4714
|
}
|
|
4599
4715
|
}
|
|
4600
4716
|
function isCnbInstalled() {
|
|
4601
|
-
|
|
4602
|
-
execSync3("which cnb", { stdio: ["pipe", "pipe", "pipe"] });
|
|
4603
|
-
return true;
|
|
4604
|
-
} catch {
|
|
4605
|
-
return false;
|
|
4606
|
-
}
|
|
4717
|
+
return resolveCliPath("cnb") !== null;
|
|
4607
4718
|
}
|
|
4608
4719
|
async function ensureCnbInstalled() {
|
|
4609
4720
|
if (isCnbInstalled()) {
|
|
@@ -4612,7 +4723,7 @@ async function ensureCnbInstalled() {
|
|
|
4612
4723
|
}
|
|
4613
4724
|
const spin = spinner("Installing cnb CLI (@cnbcool/cnb-cli)...").start();
|
|
4614
4725
|
try {
|
|
4615
|
-
|
|
4726
|
+
execSync2("npm install -g @cnbcool/cnb-cli", { stdio: ["pipe", "pipe", "pipe"], timeout: 12e4 });
|
|
4616
4727
|
if (!isCnbInstalled()) throw new Error("cnb not found on PATH after install");
|
|
4617
4728
|
spin.succeed("cnb CLI installed");
|
|
4618
4729
|
} catch (e) {
|
|
@@ -4646,7 +4757,7 @@ function cnbWhoami() {
|
|
|
4646
4757
|
}
|
|
4647
4758
|
function cnbLogin() {
|
|
4648
4759
|
log.info("Starting cnb authentication (OAuth2 device flow)...");
|
|
4649
|
-
const r = cnbExec(["login"], { inheritStdio: true });
|
|
4760
|
+
const r = cnbExec(["login", "--host", CNB_HOST], { inheritStdio: true });
|
|
4650
4761
|
if (r.status !== 0) throw new Error("cnb login failed. Please try again.");
|
|
4651
4762
|
}
|
|
4652
4763
|
function ensureCnbAuthenticated() {
|
|
@@ -4693,13 +4804,47 @@ function cnbRepoClone(repo, localPath) {
|
|
|
4693
4804
|
const sanitized = out.replace(/cnb:[^@]+@/g, "cnb:***@").trim();
|
|
4694
4805
|
throw new Error(`git clone failed: ${sanitized}`);
|
|
4695
4806
|
}
|
|
4807
|
+
if (!token) {
|
|
4808
|
+
const cfg = spawnSync3("git", ["config", "--local", "credential.helper", "!cnb git-credential"], {
|
|
4809
|
+
encoding: "utf-8",
|
|
4810
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
4811
|
+
cwd: localPath
|
|
4812
|
+
});
|
|
4813
|
+
if (cfg.status !== 0) {
|
|
4814
|
+
log.warn(`Could not persist CNB credential helper: ${(cfg.stderr ?? "").trim()}. Push/pull may prompt for credentials.`);
|
|
4815
|
+
}
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4818
|
+
function cnbOrganizationCreateUrl() {
|
|
4819
|
+
return `https://${CNB_HOST}/new/groups`;
|
|
4820
|
+
}
|
|
4821
|
+
function cnbRepoCreateUrl() {
|
|
4822
|
+
return `https://${CNB_HOST}/new/repos`;
|
|
4823
|
+
}
|
|
4824
|
+
function cnbOrganizationExists(org) {
|
|
4825
|
+
const r = cnbExec(["organizations", "get-group", "--group", org]);
|
|
4826
|
+
const out = r.stdout || r.stderr;
|
|
4827
|
+
if (/(?:^|["\s])status["\s:]+\s*200\b/.test(out)) return true;
|
|
4828
|
+
if (/(?:^|["\s])status["\s:]+\s*404\b/.test(out) || /not found|不存在/i.test(out)) return false;
|
|
4829
|
+
throw new Error(`cnb get-group failed for "${org}": ${out || `exit ${r.status}`}`);
|
|
4696
4830
|
}
|
|
4697
4831
|
async function cnbCreateRepo(owner, repo) {
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4832
|
+
try {
|
|
4833
|
+
const r = cnbExec(["repositories", "create-repo", "--slug", owner, "--name", repo]);
|
|
4834
|
+
if (r.status !== 0) {
|
|
4835
|
+
throw new Error(`cnb create-repo failed: ${r.stderr || r.stdout}`);
|
|
4836
|
+
}
|
|
4837
|
+
assertCnbApiOk(r.stdout, "create-repo");
|
|
4838
|
+
} catch (e) {
|
|
4839
|
+
const msg = e.message;
|
|
4840
|
+
if (/HTTP 404|not found|不存在/i.test(msg)) {
|
|
4841
|
+
throw new OrganizationNotFoundError(owner, cnbOrganizationCreateUrl());
|
|
4842
|
+
}
|
|
4843
|
+
if (/HTTP 403|scope|permission|forbidden|权限/i.test(msg)) {
|
|
4844
|
+
throw new RepoCreatePermissionError(`${owner}/${repo}`, cnbRepoCreateUrl());
|
|
4845
|
+
}
|
|
4846
|
+
throw e;
|
|
4701
4847
|
}
|
|
4702
|
-
assertCnbApiOk(r.stdout, "create-repo");
|
|
4703
4848
|
}
|
|
4704
4849
|
function cnbPullCreate(opts) {
|
|
4705
4850
|
const args = [
|
|
@@ -4732,6 +4877,8 @@ var init_cnb_cli = __esm({
|
|
|
4732
4877
|
"src/providers/cnb/cnb-cli.ts"() {
|
|
4733
4878
|
"use strict";
|
|
4734
4879
|
init_logger();
|
|
4880
|
+
init_cli_path();
|
|
4881
|
+
init_types2();
|
|
4735
4882
|
CNB_HOST = process.env.TEAMAI_CNB_HOST?.trim() || "cnb.cool";
|
|
4736
4883
|
CnbRepoNotFoundError = class extends Error {
|
|
4737
4884
|
constructor(repo) {
|
|
@@ -4781,6 +4928,12 @@ var init_cnb = __esm({
|
|
|
4781
4928
|
async createRepo(owner, repo) {
|
|
4782
4929
|
await cnbCreateRepo(owner, repo);
|
|
4783
4930
|
}
|
|
4931
|
+
organizationExists(org) {
|
|
4932
|
+
return cnbOrganizationExists(org);
|
|
4933
|
+
}
|
|
4934
|
+
getOrganizationCreateUrl() {
|
|
4935
|
+
return cnbOrganizationCreateUrl();
|
|
4936
|
+
}
|
|
4784
4937
|
async createPullRequest(opts) {
|
|
4785
4938
|
return cnbPullCreate({
|
|
4786
4939
|
repo: opts.repo,
|
|
@@ -6320,6 +6473,8 @@ __export(providers_exports, {
|
|
|
6320
6473
|
GitCodeProvider: () => GitCodeProvider,
|
|
6321
6474
|
GitHubProvider: () => GitHubProvider,
|
|
6322
6475
|
GitLabProvider: () => GitLabProvider,
|
|
6476
|
+
OrganizationNotFoundError: () => OrganizationNotFoundError,
|
|
6477
|
+
RepoCreatePermissionError: () => RepoCreatePermissionError,
|
|
6323
6478
|
RepoNotFoundError: () => RepoNotFoundError,
|
|
6324
6479
|
TGitProvider: () => TGitProvider,
|
|
6325
6480
|
detectProvider: () => detectProvider,
|
|
@@ -6394,7 +6549,7 @@ async function deployBuiltinSkills(teamConfig, localConfig, options) {
|
|
|
6394
6549
|
log.debug(`Skipping built-in skill deployment for ${tool}: tool not installed`);
|
|
6395
6550
|
continue;
|
|
6396
6551
|
}
|
|
6397
|
-
if (localConfig &&
|
|
6552
|
+
if (localConfig && isAgentExcluded(localConfig, tool)) continue;
|
|
6398
6553
|
for (const skillName of skillNames) {
|
|
6399
6554
|
const srcDir = path18.join(builtinDir, skillName);
|
|
6400
6555
|
const destDir = await resolveSkillDestination(tool, toolPath.skills, baseDir, skillName, srcDir);
|
|
@@ -6740,7 +6895,7 @@ var init_session_id = __esm({
|
|
|
6740
6895
|
|
|
6741
6896
|
// src/pid-monitor.ts
|
|
6742
6897
|
import fs9 from "fs";
|
|
6743
|
-
import { execSync as
|
|
6898
|
+
import { execSync as execSync3 } from "child_process";
|
|
6744
6899
|
function getParentPid(pid) {
|
|
6745
6900
|
try {
|
|
6746
6901
|
const stat8 = fs9.readFileSync(`/proc/${pid}/stat`, "utf-8");
|
|
@@ -6752,7 +6907,7 @@ function getParentPid(pid) {
|
|
|
6752
6907
|
} catch {
|
|
6753
6908
|
}
|
|
6754
6909
|
try {
|
|
6755
|
-
const out =
|
|
6910
|
+
const out = execSync3(`ps -o ppid= -p ${pid}`, {
|
|
6756
6911
|
encoding: "utf-8",
|
|
6757
6912
|
timeout: 2e3,
|
|
6758
6913
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -6769,7 +6924,7 @@ function getProcessComm(pid) {
|
|
|
6769
6924
|
} catch {
|
|
6770
6925
|
}
|
|
6771
6926
|
try {
|
|
6772
|
-
const out =
|
|
6927
|
+
const out = execSync3(`ps -o comm= -p ${pid}`, {
|
|
6773
6928
|
encoding: "utf-8",
|
|
6774
6929
|
timeout: 2e3,
|
|
6775
6930
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -7878,7 +8033,7 @@ var init_agent_version = __esm({
|
|
|
7878
8033
|
// src/machine-id.ts
|
|
7879
8034
|
import crypto2 from "crypto";
|
|
7880
8035
|
import fs11 from "fs";
|
|
7881
|
-
import { execFileSync } from "child_process";
|
|
8036
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
7882
8037
|
import os6 from "os";
|
|
7883
8038
|
function getMachineId() {
|
|
7884
8039
|
if (cachedMachineId !== null) return cachedMachineId;
|
|
@@ -7904,7 +8059,7 @@ function detectMachineId(platform = process.platform) {
|
|
|
7904
8059
|
return id || os6.hostname() || "";
|
|
7905
8060
|
}
|
|
7906
8061
|
function readDarwinMachineId() {
|
|
7907
|
-
const out =
|
|
8062
|
+
const out = execFileSync2("ioreg", ["-rd1", "-c", "IOPlatformExpertDevice"], {
|
|
7908
8063
|
encoding: "utf-8",
|
|
7909
8064
|
timeout: 3e3
|
|
7910
8065
|
});
|
|
@@ -7912,7 +8067,7 @@ function readDarwinMachineId() {
|
|
|
7912
8067
|
return match ? match[1].trim() : "";
|
|
7913
8068
|
}
|
|
7914
8069
|
function readWindowsMachineId() {
|
|
7915
|
-
const out =
|
|
8070
|
+
const out = execFileSync2(
|
|
7916
8071
|
"reg",
|
|
7917
8072
|
["query", "HKLM\\SOFTWARE\\Microsoft\\Cryptography", "/v", "MachineGuid"],
|
|
7918
8073
|
{ encoding: "utf-8", timeout: 3e3 }
|
|
@@ -11765,9 +11920,16 @@ async function scanTeamRepoNamespaces(repoPath) {
|
|
|
11765
11920
|
for (const dir of topDirs) {
|
|
11766
11921
|
const dirPath = path29.join(teamSkillsDir, dir);
|
|
11767
11922
|
const hasSkillMd = await pathExists(path29.join(dirPath, "SKILL.md"));
|
|
11768
|
-
if (
|
|
11769
|
-
|
|
11923
|
+
if (hasSkillMd) continue;
|
|
11924
|
+
const subDirs = await listDirs(dirPath);
|
|
11925
|
+
let holdsSkill = false;
|
|
11926
|
+
for (const subDir of subDirs) {
|
|
11927
|
+
if (await pathExists(path29.join(dirPath, subDir, "SKILL.md"))) {
|
|
11928
|
+
holdsSkill = true;
|
|
11929
|
+
break;
|
|
11930
|
+
}
|
|
11770
11931
|
}
|
|
11932
|
+
if (holdsSkill) namespaces.push(dir);
|
|
11771
11933
|
}
|
|
11772
11934
|
return namespaces;
|
|
11773
11935
|
}
|
|
@@ -13182,7 +13344,7 @@ async function deployBuiltinAgents(teamConfig, localConfig, options) {
|
|
|
13182
13344
|
log.debug(`Skipping built-in agent deployment for ${tool}: tool not installed`);
|
|
13183
13345
|
continue;
|
|
13184
13346
|
}
|
|
13185
|
-
if (localConfig &&
|
|
13347
|
+
if (localConfig && isAgentExcluded(localConfig, tool)) continue;
|
|
13186
13348
|
if (!ALL_SUPPORTED_TOOLS.includes(tool)) {
|
|
13187
13349
|
log.warn(
|
|
13188
13350
|
`Skipping built-in agent deployment for ${tool}: unsupported agent format; disable this target or add a native renderer`
|
|
@@ -15700,6 +15862,15 @@ async function initSelfRepo(options) {
|
|
|
15700
15862
|
log.info(" 4. Push your business repo (e.g. `git push -u origin HEAD`) so teammates get the .teamai/ knowledge and are auto-initialized on clone.");
|
|
15701
15863
|
closePrompt();
|
|
15702
15864
|
}
|
|
15865
|
+
function guideWebCreation(kind, name, url) {
|
|
15866
|
+
log.info(`${kind} "${name}" can't be created from the CLI (insufficient token permission).`);
|
|
15867
|
+
if (url) {
|
|
15868
|
+
log.info(`Create it here, then re-run this command:`);
|
|
15869
|
+
log.info(` ${url}`);
|
|
15870
|
+
} else {
|
|
15871
|
+
log.info(`Create the ${kind} on the platform, then re-run this command.`);
|
|
15872
|
+
}
|
|
15873
|
+
}
|
|
15703
15874
|
async function init(options) {
|
|
15704
15875
|
if (options.http) {
|
|
15705
15876
|
return initHttp(options.http, options);
|
|
@@ -15851,6 +16022,23 @@ async function init(options) {
|
|
|
15851
16022
|
} catch (e) {
|
|
15852
16023
|
if (e instanceof RepoNotFoundError) {
|
|
15853
16024
|
cloneSpin.info(`Repo ${repoInfo.owner}/${repoInfo.repo} does not exist`);
|
|
16025
|
+
if (typeof provider.organizationExists === "function") {
|
|
16026
|
+
let orgMissing = false;
|
|
16027
|
+
try {
|
|
16028
|
+
orgMissing = !provider.organizationExists(repoInfo.owner);
|
|
16029
|
+
} catch (checkErr) {
|
|
16030
|
+
log.debug(`Organization check failed: ${checkErr.message}`);
|
|
16031
|
+
}
|
|
16032
|
+
if (orgMissing) {
|
|
16033
|
+
cloneSpin.info(`Organization "${repoInfo.owner}" does not exist`);
|
|
16034
|
+
guideWebCreation(
|
|
16035
|
+
"organization",
|
|
16036
|
+
repoInfo.owner,
|
|
16037
|
+
provider.getOrganizationCreateUrl?.() ?? null
|
|
16038
|
+
);
|
|
16039
|
+
process.exit(1);
|
|
16040
|
+
}
|
|
16041
|
+
}
|
|
15854
16042
|
const confirmed = await askConfirmation(
|
|
15855
16043
|
`Create repo ${repoInfo.owner}/${repoInfo.repo}? [Y/n] `,
|
|
15856
16044
|
true
|
|
@@ -15864,6 +16052,16 @@ async function init(options) {
|
|
|
15864
16052
|
await provider.createRepo(repoInfo.owner, repoInfo.repo);
|
|
15865
16053
|
createSpin.succeed(`Repo ${repoInfo.owner}/${repoInfo.repo} created`);
|
|
15866
16054
|
} catch (ce) {
|
|
16055
|
+
if (ce instanceof OrganizationNotFoundError) {
|
|
16056
|
+
createSpin.fail(`Organization "${ce.org}" does not exist`);
|
|
16057
|
+
guideWebCreation("organization", ce.org, ce.createUrl ?? null);
|
|
16058
|
+
process.exit(1);
|
|
16059
|
+
}
|
|
16060
|
+
if (ce instanceof RepoCreatePermissionError) {
|
|
16061
|
+
createSpin.fail(`No permission to create repo "${ce.repo}" from the CLI`);
|
|
16062
|
+
guideWebCreation("repo", ce.repo, ce.createUrl ?? null);
|
|
16063
|
+
process.exit(1);
|
|
16064
|
+
}
|
|
15867
16065
|
const msg = ce.message;
|
|
15868
16066
|
if (/already been taken|already exists/i.test(msg)) {
|
|
15869
16067
|
createSpin.info(`Repo ${repoInfo.owner}/${repoInfo.repo} already exists, retrying clone`);
|
|
@@ -16244,6 +16442,9 @@ ${items.map((i) => `- [${i.type}] ${i.name}`).join("\n")}`
|
|
|
16244
16442
|
items: toPendingItems(items)
|
|
16245
16443
|
});
|
|
16246
16444
|
await checkoutMaster(localConfig.repo.localPath);
|
|
16445
|
+
for (const rel of pushedFiles) {
|
|
16446
|
+
await pruneEmptyDirs(path45.resolve(localConfig.repo.localPath, rel));
|
|
16447
|
+
}
|
|
16247
16448
|
return true;
|
|
16248
16449
|
} catch (e) {
|
|
16249
16450
|
pushSpin.fail(`Push failed: ${e.message}`);
|
|
@@ -22024,7 +22225,7 @@ async function refreshTeamRepo(localConfig) {
|
|
|
22024
22225
|
if (!apiKey) {
|
|
22025
22226
|
throw new Error("No API key configured. Re-run `teamai init --http <url> --token <key>` or set TEAMAI_API_TOKEN.");
|
|
22026
22227
|
}
|
|
22027
|
-
return { label: "HTTP (report/sync delivery)", version: null, reportingOnly: true, submodulesFailed: false };
|
|
22228
|
+
return { label: "HTTP (report/sync delivery)", version: null, reportingOnly: true, submodulesFailed: false, submodulesChanged: false };
|
|
22028
22229
|
}
|
|
22029
22230
|
if (localConfig.repo.kind === "self") {
|
|
22030
22231
|
try {
|
|
@@ -22038,7 +22239,7 @@ async function refreshTeamRepo(localConfig) {
|
|
|
22038
22239
|
} catch {
|
|
22039
22240
|
version3 = null;
|
|
22040
22241
|
}
|
|
22041
|
-
return { label: "single-repo (knowledge on main)", version: version3, reportingOnly: false, submodulesFailed: false };
|
|
22242
|
+
return { label: "single-repo (knowledge on main)", version: version3, reportingOnly: false, submodulesFailed: false, submodulesChanged: false };
|
|
22042
22243
|
}
|
|
22043
22244
|
const result = await pullRepo(localConfig.repo.localPath);
|
|
22044
22245
|
try {
|
|
@@ -22054,17 +22255,34 @@ async function refreshTeamRepo(localConfig) {
|
|
|
22054
22255
|
version2 = null;
|
|
22055
22256
|
}
|
|
22056
22257
|
let submodulesFailed = false;
|
|
22258
|
+
let submodulesChanged = false;
|
|
22057
22259
|
try {
|
|
22058
22260
|
const teamConfig = await loadTeamConfig(localConfig.repo.localPath);
|
|
22059
22261
|
if (teamConfig?.submodules) {
|
|
22060
|
-
|
|
22061
|
-
|
|
22262
|
+
const git = createGit2(localConfig.repo.localPath);
|
|
22263
|
+
let before = null;
|
|
22264
|
+
try {
|
|
22265
|
+
before = await git.subModule(["status"]);
|
|
22266
|
+
} catch {
|
|
22267
|
+
before = null;
|
|
22268
|
+
}
|
|
22269
|
+
await git.submoduleUpdate(["--init"]);
|
|
22270
|
+
let after = null;
|
|
22271
|
+
try {
|
|
22272
|
+
after = await git.subModule(["status"]);
|
|
22273
|
+
} catch {
|
|
22274
|
+
after = null;
|
|
22275
|
+
}
|
|
22276
|
+
submodulesChanged = before === null || before !== after;
|
|
22277
|
+
log.debug(
|
|
22278
|
+
submodulesChanged ? "Submodules updated (tree changed \u2014 full sync this pull)" : "Submodules updated (no change)"
|
|
22279
|
+
);
|
|
22062
22280
|
}
|
|
22063
22281
|
} catch (e) {
|
|
22064
22282
|
submodulesFailed = true;
|
|
22065
22283
|
log.warn(`Submodule update failed for ${localConfig.repo.localPath}: ${e.message}`);
|
|
22066
22284
|
}
|
|
22067
|
-
return { label: result, version: version2, reportingOnly: false, submodulesFailed };
|
|
22285
|
+
return { label: result, version: version2, reportingOnly: false, submodulesFailed, submodulesChanged };
|
|
22068
22286
|
}
|
|
22069
22287
|
async function usageReportDisabled(repoPath) {
|
|
22070
22288
|
return (await loadTeamConfig(repoPath))?.usageReport === false;
|
|
@@ -22183,7 +22401,7 @@ async function skillSafeToRemove(deployedDir, source) {
|
|
|
22183
22401
|
async function cleanupInactiveNamespaceSkills(teamConfig, localConfig, retainedSkillNames, inactiveSkillNames, inactiveSkillSources) {
|
|
22184
22402
|
const baseDir = resolveBaseDir(localConfig);
|
|
22185
22403
|
for (const [tool, toolPath] of Object.entries(scopedToolPaths(teamConfig, localConfig))) {
|
|
22186
|
-
if (
|
|
22404
|
+
if (isAgentExcluded(localConfig, tool)) continue;
|
|
22187
22405
|
if (!toolPath.skills) continue;
|
|
22188
22406
|
if (!await ResourceHandler.isToolInstalled(toolPath.skills, baseDir)) continue;
|
|
22189
22407
|
if (!await pathExists(path65.join(baseDir, toolPath.skills))) continue;
|
|
@@ -22244,7 +22462,7 @@ async function getInstalledResourceTargets(teamConfig, localConfig) {
|
|
|
22244
22462
|
const baseDir = resolveBaseDir(localConfig);
|
|
22245
22463
|
const targets = [];
|
|
22246
22464
|
for (const [tool, toolPath] of Object.entries(scopedToolPaths(teamConfig, localConfig))) {
|
|
22247
|
-
if (
|
|
22465
|
+
if (isAgentExcluded(localConfig, tool)) continue;
|
|
22248
22466
|
const resourcePaths = [toolPath.skills, toolPath.rules, toolPath.agents].filter((resourcePath) => !!resourcePath);
|
|
22249
22467
|
for (const resourcePath of resourcePaths) {
|
|
22250
22468
|
if (await ResourceHandler.isToolInstalled(resourcePath, baseDir)) {
|
|
@@ -22268,18 +22486,20 @@ async function pullForScope(localConfig, options, policy = {}) {
|
|
|
22268
22486
|
let currentRev = null;
|
|
22269
22487
|
let reportingOnly = false;
|
|
22270
22488
|
let submodulesFailed = false;
|
|
22489
|
+
let submodulesChanged = false;
|
|
22271
22490
|
try {
|
|
22272
22491
|
const refresh = await refreshTeamRepo(localConfig);
|
|
22273
22492
|
currentRev = refresh.version;
|
|
22274
22493
|
reportingOnly = refresh.reportingOnly;
|
|
22275
22494
|
submodulesFailed = refresh.submodulesFailed;
|
|
22495
|
+
submodulesChanged = refresh.submodulesChanged;
|
|
22276
22496
|
pullSpin.succeed(`[${scopeLabel}] Team repo: ${refresh.label}`);
|
|
22277
22497
|
} catch (e) {
|
|
22278
22498
|
pullSpin.fail(`[${scopeLabel}] Pull failed: ${e.message}`);
|
|
22279
22499
|
return;
|
|
22280
22500
|
}
|
|
22281
22501
|
let currentTargets = null;
|
|
22282
|
-
if (!options.force && !options.dryRun) {
|
|
22502
|
+
if (!options.force && !options.dryRun && !submodulesChanged) {
|
|
22283
22503
|
try {
|
|
22284
22504
|
const state = await loadStateForScope(localConfig);
|
|
22285
22505
|
if (currentRev && state[revisionField] && state[revisionField] === currentRev) {
|
|
@@ -22460,7 +22680,7 @@ async function pullForScope(localConfig, options, policy = {}) {
|
|
|
22460
22680
|
const dir = toolPath[toolPathField];
|
|
22461
22681
|
if (!dir) continue;
|
|
22462
22682
|
if (!await ResourceHandler.isToolInstalled(dir, baseDir)) continue;
|
|
22463
|
-
if (
|
|
22683
|
+
if (isAgentExcluded(localConfig, tool)) continue;
|
|
22464
22684
|
const extensions = type === "rules" ? [.../* @__PURE__ */ new Set([ruleFileExtensionForTool(tool), ".md"])] : [ext];
|
|
22465
22685
|
for (const name of tombstones) {
|
|
22466
22686
|
for (const extension of extensions) {
|
|
@@ -22490,7 +22710,7 @@ async function pullForScope(localConfig, options, policy = {}) {
|
|
|
22490
22710
|
if (!options.dryRun && desiredSkillNames && knownRepoSkillNames) {
|
|
22491
22711
|
const baseDir = resolveBaseDir(localConfig);
|
|
22492
22712
|
for (const [tool, toolPath] of Object.entries(scopedToolPaths(freshConfig, localConfig))) {
|
|
22493
|
-
if (
|
|
22713
|
+
if (isAgentExcluded(localConfig, tool)) continue;
|
|
22494
22714
|
if (!toolPath.skills) continue;
|
|
22495
22715
|
if (!await ResourceHandler.isToolInstalled(toolPath.skills, baseDir)) continue;
|
|
22496
22716
|
const skillsDir = path65.join(baseDir, toolPath.skills);
|
|
@@ -22609,7 +22829,7 @@ async function pullForScope(localConfig, options, policy = {}) {
|
|
|
22609
22829
|
if (compiled) {
|
|
22610
22830
|
const baseDir = resolveBaseDir(localConfig);
|
|
22611
22831
|
for (const [tool, toolPath] of Object.entries(scopedToolPaths(freshConfig, localConfig))) {
|
|
22612
|
-
if (
|
|
22832
|
+
if (isAgentExcluded(localConfig, tool)) continue;
|
|
22613
22833
|
if (!toolPath.claudemd) continue;
|
|
22614
22834
|
if (toolPath.rules && !await ResourceHandler.isToolInstalled(toolPath.rules, baseDir)) continue;
|
|
22615
22835
|
const claudeMdPath = path65.join(baseDir, toolPath.claudemd);
|
|
@@ -22639,7 +22859,7 @@ async function pullForScope(localConfig, options, policy = {}) {
|
|
|
22639
22859
|
if (compiled) {
|
|
22640
22860
|
const baseDir = resolveBaseDir(localConfig);
|
|
22641
22861
|
for (const [tool, toolPath] of Object.entries(scopedToolPaths(freshConfig, localConfig))) {
|
|
22642
|
-
if (
|
|
22862
|
+
if (isAgentExcluded(localConfig, tool)) continue;
|
|
22643
22863
|
if (!toolPath.claudemd) continue;
|
|
22644
22864
|
if (toolPath.rules && !await ResourceHandler.isToolInstalled(toolPath.rules, baseDir)) continue;
|
|
22645
22865
|
const claudeMdPath = path65.join(baseDir, toolPath.claudemd);
|
|
@@ -22818,7 +23038,7 @@ async function injectRecallBlockIntoTools(config, localConfig, scopeLabel) {
|
|
|
22818
23038
|
const recallBlock = compileRecallRulesBlock();
|
|
22819
23039
|
let injected = 0;
|
|
22820
23040
|
for (const [tool, toolPath] of Object.entries(scopedToolPaths(config, localConfig))) {
|
|
22821
|
-
if (
|
|
23041
|
+
if (isAgentExcluded(localConfig, tool)) continue;
|
|
22822
23042
|
if (!toolPath.claudemd || !toolPath.agents) continue;
|
|
22823
23043
|
if (!await ResourceHandler.isToolInstalled(toolPath.agents, baseDir)) continue;
|
|
22824
23044
|
const claudeMdPath = path65.join(baseDir, toolPath.claudemd);
|
|
@@ -27057,67 +27277,7 @@ __export(ai_client_exports, {
|
|
|
27057
27277
|
pickWindowsCommand: () => pickWindowsCommand,
|
|
27058
27278
|
resolveCliPath: () => resolveCliPath
|
|
27059
27279
|
});
|
|
27060
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
27061
27280
|
import spawn2 from "cross-spawn";
|
|
27062
|
-
import { existsSync } from "fs";
|
|
27063
|
-
function pickWindowsCommand(whereOutput) {
|
|
27064
|
-
const lines = whereOutput.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
27065
|
-
for (const ext of WIN_EXEC_EXTENSIONS) {
|
|
27066
|
-
const hit = lines.find((line) => line.toLowerCase().endsWith(ext));
|
|
27067
|
-
if (hit !== void 0) return hit;
|
|
27068
|
-
}
|
|
27069
|
-
return null;
|
|
27070
|
-
}
|
|
27071
|
-
function whereOnWindows(cmd) {
|
|
27072
|
-
try {
|
|
27073
|
-
const out = execFileSync2("where", [cmd], {
|
|
27074
|
-
encoding: "utf8",
|
|
27075
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
27076
|
-
shell: false,
|
|
27077
|
-
timeout: CLI_DETECT_TIMEOUT_MS
|
|
27078
|
-
});
|
|
27079
|
-
const p = pickWindowsCommand(out);
|
|
27080
|
-
return p !== null && existsSync(p) ? p : null;
|
|
27081
|
-
} catch {
|
|
27082
|
-
return null;
|
|
27083
|
-
}
|
|
27084
|
-
}
|
|
27085
|
-
function whichOnPosix(cmd) {
|
|
27086
|
-
try {
|
|
27087
|
-
const p = execFileSync2("bash", ["-lc", `command -v ${cmd}`], {
|
|
27088
|
-
encoding: "utf8",
|
|
27089
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
27090
|
-
shell: false,
|
|
27091
|
-
timeout: CLI_DETECT_TIMEOUT_MS
|
|
27092
|
-
}).trim();
|
|
27093
|
-
if (p && existsSync(p)) return p;
|
|
27094
|
-
} catch {
|
|
27095
|
-
}
|
|
27096
|
-
try {
|
|
27097
|
-
const p = execFileSync2("zsh", ["-lc", `command -v ${cmd}`], {
|
|
27098
|
-
encoding: "utf8",
|
|
27099
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
27100
|
-
shell: false,
|
|
27101
|
-
timeout: CLI_DETECT_TIMEOUT_MS
|
|
27102
|
-
}).trim();
|
|
27103
|
-
if (p && existsSync(p)) return p;
|
|
27104
|
-
} catch {
|
|
27105
|
-
}
|
|
27106
|
-
try {
|
|
27107
|
-
const p = execFileSync2("which", [cmd], {
|
|
27108
|
-
encoding: "utf8",
|
|
27109
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
27110
|
-
shell: false,
|
|
27111
|
-
timeout: CLI_DETECT_TIMEOUT_MS
|
|
27112
|
-
}).trim();
|
|
27113
|
-
if (p && existsSync(p)) return p;
|
|
27114
|
-
} catch {
|
|
27115
|
-
}
|
|
27116
|
-
return null;
|
|
27117
|
-
}
|
|
27118
|
-
function resolveCliPath(cmd, platform = process.platform) {
|
|
27119
|
-
return platform === "win32" ? whereOnWindows(cmd) : whichOnPosix(cmd);
|
|
27120
|
-
}
|
|
27121
27281
|
function detectClaudeCli() {
|
|
27122
27282
|
for (const cmd of ALLOWED_CLI_CANDIDATES) {
|
|
27123
27283
|
const absPath = resolveCliPath(cmd);
|
|
@@ -27232,11 +27392,13 @@ async function runWithConcurrency(tasks, concurrency) {
|
|
|
27232
27392
|
await Promise.all(promises);
|
|
27233
27393
|
return results;
|
|
27234
27394
|
}
|
|
27235
|
-
var ALLOWED_CLI_CANDIDATES,
|
|
27395
|
+
var ALLOWED_CLI_CANDIDATES, DEFAULT_TIMEOUT_MS, DEFAULT_CONCURRENCY, _cliInfo;
|
|
27236
27396
|
var init_ai_client = __esm({
|
|
27237
27397
|
"src/utils/ai-client.ts"() {
|
|
27238
27398
|
"use strict";
|
|
27239
27399
|
init_logger();
|
|
27400
|
+
init_cli_path();
|
|
27401
|
+
init_cli_path();
|
|
27240
27402
|
ALLOWED_CLI_CANDIDATES = [
|
|
27241
27403
|
"claude",
|
|
27242
27404
|
"claude-internal",
|
|
@@ -27248,8 +27410,6 @@ var init_ai_client = __esm({
|
|
|
27248
27410
|
"workbuddy",
|
|
27249
27411
|
"openclaw"
|
|
27250
27412
|
];
|
|
27251
|
-
CLI_DETECT_TIMEOUT_MS = 5e3;
|
|
27252
|
-
WIN_EXEC_EXTENSIONS = [".exe", ".cmd", ".bat"];
|
|
27253
27413
|
DEFAULT_TIMEOUT_MS = 6e5;
|
|
27254
27414
|
DEFAULT_CONCURRENCY = 3;
|
|
27255
27415
|
}
|
|
@@ -33182,7 +33342,7 @@ var init_import_iwiki = __esm({
|
|
|
33182
33342
|
});
|
|
33183
33343
|
|
|
33184
33344
|
// src/providers/github/mr-fetch.ts
|
|
33185
|
-
import { execSync as
|
|
33345
|
+
import { execSync as execSync4 } from "child_process";
|
|
33186
33346
|
import https2 from "https";
|
|
33187
33347
|
function parseGitHubPRUrl(url) {
|
|
33188
33348
|
const match = url.match(/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/);
|
|
@@ -33269,12 +33429,12 @@ async function fetchGitHubPR(url) {
|
|
|
33269
33429
|
const repoArg = `${owner}/${repo}`;
|
|
33270
33430
|
log.debug(`fetchGitHubPR: ${repoArg}#${number}`);
|
|
33271
33431
|
try {
|
|
33272
|
-
const viewOutput =
|
|
33432
|
+
const viewOutput = execSync4(
|
|
33273
33433
|
`gh pr view ${number} --repo ${repoArg} --json title,body,author,mergedAt,commits`,
|
|
33274
33434
|
{ maxBuffer: 10 * 1024 * 1024, encoding: "utf8" }
|
|
33275
33435
|
);
|
|
33276
33436
|
const prView = JSON.parse(viewOutput);
|
|
33277
|
-
const rawDiff =
|
|
33437
|
+
const rawDiff = execSync4(
|
|
33278
33438
|
`gh pr diff ${number} --repo ${repoArg}`,
|
|
33279
33439
|
{ maxBuffer: 50 * 1024 * 1024, encoding: "utf8" }
|
|
33280
33440
|
);
|
|
@@ -33688,7 +33848,7 @@ var init_import_mr = __esm({
|
|
|
33688
33848
|
});
|
|
33689
33849
|
|
|
33690
33850
|
// src/codebase.ts
|
|
33691
|
-
import { execSync as
|
|
33851
|
+
import { execSync as execSync5 } from "child_process";
|
|
33692
33852
|
import fs36 from "fs";
|
|
33693
33853
|
import path103 from "path";
|
|
33694
33854
|
import matter12 from "gray-matter";
|
|
@@ -33704,7 +33864,7 @@ ${commitMessages}`);
|
|
|
33704
33864
|
log.debug(`gatherRepoContext: git log \u5931\u8D25 \u2014 ${String(err)}`);
|
|
33705
33865
|
}
|
|
33706
33866
|
try {
|
|
33707
|
-
const rawTree =
|
|
33867
|
+
const rawTree = execSync5(
|
|
33708
33868
|
'find . -maxdepth 4 -not -path "*/.git/*" -not -path "*/node_modules/*" -not -path "*/__pycache__/*" -not -path "*/dist/*" -not -path "*/.claude/worktrees/*" -not -name "*.js.map"',
|
|
33709
33869
|
{ cwd: repoPath, encoding: "utf-8" }
|
|
33710
33870
|
);
|
|
@@ -36853,9 +37013,12 @@ var init_templates = __esm({
|
|
|
36853
37013
|
// src/enrich-with-ai.ts
|
|
36854
37014
|
var enrich_with_ai_exports = {};
|
|
36855
37015
|
__export(enrich_with_ai_exports, {
|
|
37016
|
+
buildFallbackManifest: () => buildFallbackManifest,
|
|
37017
|
+
describeEvidenceManifest: () => describeEvidenceManifest,
|
|
36856
37018
|
edgeProvenanceRank: () => edgeProvenanceRank,
|
|
36857
37019
|
edgeReason: () => edgeReason,
|
|
36858
37020
|
enrichWithAI: () => enrichWithAI,
|
|
37021
|
+
groupFactsByModule: () => groupFactsByModule,
|
|
36859
37022
|
parseEdgeProvenance: () => parseEdgeProvenance,
|
|
36860
37023
|
writeManifest: () => writeManifest
|
|
36861
37024
|
});
|
|
@@ -36925,6 +37088,62 @@ function resolveImportToModule(importerFile, importPath) {
|
|
|
36925
37088
|
if (first.startsWith("@")) return void 0;
|
|
36926
37089
|
return first;
|
|
36927
37090
|
}
|
|
37091
|
+
function groupFactsByModule(facts) {
|
|
37092
|
+
const modules = /* @__PURE__ */ new Map();
|
|
37093
|
+
for (const fact of facts) {
|
|
37094
|
+
if (fact.kind === "relation") continue;
|
|
37095
|
+
const mod = fact.file.split("/")[0] || "_root";
|
|
37096
|
+
const existing = modules.get(mod) ?? [];
|
|
37097
|
+
existing.push(fact);
|
|
37098
|
+
modules.set(mod, existing);
|
|
37099
|
+
}
|
|
37100
|
+
return modules;
|
|
37101
|
+
}
|
|
37102
|
+
function isSafeSlug(name) {
|
|
37103
|
+
try {
|
|
37104
|
+
assertSafeResourceName(name);
|
|
37105
|
+
return true;
|
|
37106
|
+
} catch {
|
|
37107
|
+
return false;
|
|
37108
|
+
}
|
|
37109
|
+
}
|
|
37110
|
+
function buildFallbackManifest(ctx) {
|
|
37111
|
+
const moduleNames = [...ctx.modules.keys()].filter(isSafeSlug);
|
|
37112
|
+
const componentNames = [...new Set(
|
|
37113
|
+
ctx.facts.filter((f) => f.kind === "component").map((f) => f.name)
|
|
37114
|
+
)].filter(isSafeSlug);
|
|
37115
|
+
const slugs = moduleNames.length > 0 ? moduleNames : componentNames;
|
|
37116
|
+
if (slugs.length === 0) return null;
|
|
37117
|
+
const components = slugs.map((name) => {
|
|
37118
|
+
const moduleFacts = ctx.modules.get(name) ?? ctx.facts.filter((f) => f.name === name);
|
|
37119
|
+
const entrypoints = moduleFacts.filter((f) => f.kind === "component").filter((f) => /handler|route|controller|endpoint|main|server|app/i.test(f.name)).slice(0, 5).map((f) => `${f.name} (${f.file}:${f.lineStart})`);
|
|
37120
|
+
return {
|
|
37121
|
+
slug: name,
|
|
37122
|
+
docPath: `evidence/code/${ctx.project}/${name}.md`,
|
|
37123
|
+
title: name,
|
|
37124
|
+
category: "component",
|
|
37125
|
+
confidence: "EXTRACTED",
|
|
37126
|
+
...entrypoints.length > 0 ? { entrypoints } : {}
|
|
37127
|
+
};
|
|
37128
|
+
});
|
|
37129
|
+
return {
|
|
37130
|
+
schemaVersion: "team-wiki.codebase-output-manifest.v2",
|
|
37131
|
+
project: ctx.project,
|
|
37132
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
37133
|
+
components,
|
|
37134
|
+
edges: []
|
|
37135
|
+
};
|
|
37136
|
+
}
|
|
37137
|
+
function describeEvidenceManifest(source, componentCount) {
|
|
37138
|
+
if (source === "fallback") {
|
|
37139
|
+
const noun = componentCount === 1 ? "component" : "components";
|
|
37140
|
+
return `Wrote fallback _manifest.json (${componentCount} ${noun}, no AI enrich)`;
|
|
37141
|
+
}
|
|
37142
|
+
if (source === "none") {
|
|
37143
|
+
return "AI enrich produced no manifest; deep-enrich will have no components";
|
|
37144
|
+
}
|
|
37145
|
+
return void 0;
|
|
37146
|
+
}
|
|
36928
37147
|
async function enrichWithAI(ctx) {
|
|
36929
37148
|
const moduleEntries = [...ctx.modules.entries()].filter(([, facts]) => facts.length >= 5);
|
|
36930
37149
|
if (moduleEntries.length === 0) {
|
|
@@ -37063,6 +37282,7 @@ var init_enrich_with_ai = __esm({
|
|
|
37063
37282
|
"use strict";
|
|
37064
37283
|
init_ai_client();
|
|
37065
37284
|
init_logger();
|
|
37285
|
+
init_path_safety();
|
|
37066
37286
|
}
|
|
37067
37287
|
});
|
|
37068
37288
|
|
|
@@ -37584,39 +37804,53 @@ async function extractCodebase(opts) {
|
|
|
37584
37804
|
const repoGraph = mergeGraphs(graph, overlay);
|
|
37585
37805
|
await saveGraphIndex(wikiRoot, repoGraph);
|
|
37586
37806
|
let aiDomains = [];
|
|
37807
|
+
let manifestSource = "none";
|
|
37808
|
+
let manifestComponentCount = 0;
|
|
37809
|
+
const {
|
|
37810
|
+
enrichWithAI: enrichWithAI2,
|
|
37811
|
+
writeManifest: writeManifest2,
|
|
37812
|
+
buildFallbackManifest: buildFallbackManifest2,
|
|
37813
|
+
groupFactsByModule: groupFactsByModule2,
|
|
37814
|
+
describeEvidenceManifest: describeEvidenceManifest2
|
|
37815
|
+
} = await Promise.resolve().then(() => (init_enrich_with_ai(), enrich_with_ai_exports));
|
|
37816
|
+
const modules = groupFactsByModule2(facts);
|
|
37587
37817
|
if (opts.skipEnrich) {
|
|
37588
37818
|
if (!opts.json) console.log(chalk3.dim(" [AI enrich: skipped (--skip-enrich)]"));
|
|
37589
|
-
} else
|
|
37590
|
-
|
|
37591
|
-
|
|
37592
|
-
|
|
37593
|
-
|
|
37594
|
-
|
|
37595
|
-
|
|
37596
|
-
|
|
37597
|
-
|
|
37598
|
-
|
|
37599
|
-
|
|
37600
|
-
|
|
37601
|
-
|
|
37602
|
-
|
|
37603
|
-
|
|
37604
|
-
|
|
37605
|
-
|
|
37606
|
-
|
|
37607
|
-
|
|
37608
|
-
}
|
|
37609
|
-
|
|
37819
|
+
} else {
|
|
37820
|
+
try {
|
|
37821
|
+
const enrichResult = await enrichWithAI2({ project, facts, interfaceInventory, modules });
|
|
37822
|
+
if (enrichResult) {
|
|
37823
|
+
await writeManifest2(enrichResult.manifest, evidenceDir);
|
|
37824
|
+
manifestSource = "ai";
|
|
37825
|
+
manifestComponentCount = enrichResult.manifest.components.length;
|
|
37826
|
+
aiDomains = enrichResult.domains;
|
|
37827
|
+
const domainMeta = {
|
|
37828
|
+
domain: enrichResult.repoDomain || (enrichResult.domains[0]?.name ?? ""),
|
|
37829
|
+
description: enrichResult.repoDescription || "",
|
|
37830
|
+
keywords: enrichResult.repoKeywords || [],
|
|
37831
|
+
components: enrichResult.domains[0]?.components ?? []
|
|
37832
|
+
};
|
|
37833
|
+
await writeFile10(path112.join(evidenceDir, "_domains.json"), JSON.stringify(domainMeta, null, 2), "utf-8");
|
|
37834
|
+
if (!opts.json) {
|
|
37835
|
+
const domainLabel = domainMeta.domain || "uncategorized";
|
|
37836
|
+
console.log(` AI enrich: ${enrichResult.manifest.components.length} modules, domain=${domainLabel}`);
|
|
37837
|
+
}
|
|
37838
|
+
}
|
|
37839
|
+
} catch (e) {
|
|
37610
37840
|
if (!opts.json) {
|
|
37611
|
-
|
|
37612
|
-
console.log(` AI enrich: ${enrichResult.manifest.components.length} modules, domain=${domainLabel}`);
|
|
37841
|
+
console.log(chalk3.dim(` [AI enrich skipped: ${e.message}]`));
|
|
37613
37842
|
}
|
|
37614
37843
|
}
|
|
37615
|
-
}
|
|
37616
|
-
|
|
37617
|
-
|
|
37844
|
+
}
|
|
37845
|
+
if (manifestSource === "none") {
|
|
37846
|
+
const fallback = buildFallbackManifest2({ project, facts, modules });
|
|
37847
|
+
if (fallback && fallback.components.length > 0) {
|
|
37848
|
+
await writeManifest2(fallback, evidenceDir);
|
|
37849
|
+
manifestSource = "fallback";
|
|
37850
|
+
manifestComponentCount = fallback.components.length;
|
|
37618
37851
|
}
|
|
37619
37852
|
}
|
|
37853
|
+
const manifestNote = describeEvidenceManifest2(manifestSource, manifestComponentCount);
|
|
37620
37854
|
const moduleSummaries = buildModuleSummaries(facts, graph, project);
|
|
37621
37855
|
if (moduleSummaries.size > 0) {
|
|
37622
37856
|
const modulesDir = path112.join(evidenceDir, "modules");
|
|
@@ -37727,7 +37961,13 @@ async function extractCodebase(opts) {
|
|
|
37727
37961
|
facts: { total: facts.length, byKind },
|
|
37728
37962
|
graph: { nodes: repoGraph.nodes.length, edges: repoGraph.edges.length },
|
|
37729
37963
|
incremental: !!opts.incremental && !!changedFiles,
|
|
37730
|
-
outputDir: wikiRoot
|
|
37964
|
+
outputDir: wikiRoot,
|
|
37965
|
+
manifest: {
|
|
37966
|
+
written: manifestSource !== "none",
|
|
37967
|
+
source: manifestSource,
|
|
37968
|
+
components: manifestComponentCount,
|
|
37969
|
+
...manifestNote ? { note: manifestNote } : {}
|
|
37970
|
+
}
|
|
37731
37971
|
};
|
|
37732
37972
|
if (opts.json) {
|
|
37733
37973
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -37745,6 +37985,9 @@ async function extractCodebase(opts) {
|
|
|
37745
37985
|
console.log(` Call chains: ${callChains.length} chains (max depth ${Math.max(...callChains.map((c) => c.depth))})`);
|
|
37746
37986
|
}
|
|
37747
37987
|
console.log(` Output: ${wikiRoot}`);
|
|
37988
|
+
if (manifestNote) {
|
|
37989
|
+
console.log(` ${manifestNote}`);
|
|
37990
|
+
}
|
|
37748
37991
|
}
|
|
37749
37992
|
}
|
|
37750
37993
|
var init_codebase_extract = __esm({
|
|
@@ -38013,7 +38256,8 @@ var init_repo_cache = __esm({
|
|
|
38013
38256
|
// src/deep-enrich.ts
|
|
38014
38257
|
var deep_enrich_exports = {};
|
|
38015
38258
|
__export(deep_enrich_exports, {
|
|
38016
|
-
deepEnrich: () => deepEnrich
|
|
38259
|
+
deepEnrich: () => deepEnrich,
|
|
38260
|
+
runHiddenDeepEnrich: () => runHiddenDeepEnrich
|
|
38017
38261
|
});
|
|
38018
38262
|
import { readFile as readFile11, writeFile as writeFile11, readdir as readdir4, mkdir as mkdir8, unlink as unlink2 } from "fs/promises";
|
|
38019
38263
|
import path114 from "path";
|
|
@@ -38595,6 +38839,18 @@ async function invalidateStaleAiDocs(docsDir, slugs) {
|
|
|
38595
38839
|
await removeIfExists(architectureDocPath(docsDir));
|
|
38596
38840
|
await removeIfExists(path114.join(docsDir, "graph-g5-scenarios.md"));
|
|
38597
38841
|
}
|
|
38842
|
+
async function runHiddenDeepEnrich(opts) {
|
|
38843
|
+
const wikiRoot = opts.wikiRoot ?? path114.join(process.cwd(), ".teamai", "team-repo", "teamwiki");
|
|
38844
|
+
const evidenceDir = path114.join(wikiRoot, "evidence", "code", opts.project);
|
|
38845
|
+
const result = await deepEnrich({
|
|
38846
|
+
project: opts.project,
|
|
38847
|
+
evidenceDir,
|
|
38848
|
+
wikiRoot,
|
|
38849
|
+
maxModules: opts.maxModules
|
|
38850
|
+
});
|
|
38851
|
+
if (!result.complete) process.exitCode = 1;
|
|
38852
|
+
return result;
|
|
38853
|
+
}
|
|
38598
38854
|
async function deepEnrich(opts) {
|
|
38599
38855
|
const { project, evidenceDir } = opts;
|
|
38600
38856
|
const docsDir = path114.join(evidenceDir, "docs");
|
|
@@ -40807,7 +41063,7 @@ async function codebaseCmd(opts) {
|
|
|
40807
41063
|
componentCount = 0;
|
|
40808
41064
|
}
|
|
40809
41065
|
if (componentCount === 0) {
|
|
40810
|
-
console.log(`No components in
|
|
41066
|
+
console.log(`No components in _manifest.json for project "${project}".`);
|
|
40811
41067
|
process.exitCode = 1;
|
|
40812
41068
|
return;
|
|
40813
41069
|
}
|
|
@@ -42451,11 +42707,12 @@ ciCmd.command("extract-mr").description("Extract knowledge from MR/PR and post a
|
|
|
42451
42707
|
await ciExtractMr2({ ...globalOpts, ...cmdOpts });
|
|
42452
42708
|
});
|
|
42453
42709
|
program.command("deep-enrich", { hidden: true }).description("Run deep AI knowledge generation for an imported repo").requiredOption("--project <slug>", "Project slug (directory name in evidence/code/)").option("--wiki-root <path>", "Teamwiki root path").option("--max-modules <n>", "Max modules to process (cost control)", parseInt).action(async (cmdOpts) => {
|
|
42454
|
-
const
|
|
42455
|
-
|
|
42456
|
-
|
|
42457
|
-
|
|
42458
|
-
|
|
42710
|
+
const { runHiddenDeepEnrich: runHiddenDeepEnrich2 } = await Promise.resolve().then(() => (init_deep_enrich(), deep_enrich_exports));
|
|
42711
|
+
await runHiddenDeepEnrich2({
|
|
42712
|
+
project: cmdOpts.project,
|
|
42713
|
+
wikiRoot: cmdOpts.wikiRoot,
|
|
42714
|
+
maxModules: cmdOpts.maxModules
|
|
42715
|
+
});
|
|
42459
42716
|
});
|
|
42460
42717
|
recallCmd.command("feedback").description("Record manual feedback for a recalled document").option("--positive <docId>", "Upvote a document (marks as actually useful)").option("--negative <docId>", "Record negative signal for a document").action(async (cmdOpts) => {
|
|
42461
42718
|
const { recallFeedback: recallFeedback2 } = await Promise.resolve().then(() => (init_votes(), votes_exports));
|