teamai-cli 0.26.0-beta.5 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -0
- package/dist/index.js +576 -302
- package/package.json +3 -2
- package/skill-data/core/references/commands.md +1 -1
package/dist/index.js
CHANGED
|
@@ -66,8 +66,8 @@ function ensureLogDir() {
|
|
|
66
66
|
}
|
|
67
67
|
function maybeRotate() {
|
|
68
68
|
try {
|
|
69
|
-
const
|
|
70
|
-
if (
|
|
69
|
+
const stat10 = fs.statSync(getLogFilePath());
|
|
70
|
+
if (stat10.size >= MAX_LOG_BYTES) {
|
|
71
71
|
fs.renameSync(getLogFilePath(), getLogFilePath() + ".1");
|
|
72
72
|
}
|
|
73
73
|
} catch {
|
|
@@ -683,8 +683,8 @@ async function writeJsonAtomic(filePath, data, options) {
|
|
|
683
683
|
async function copyDir(src, dest) {
|
|
684
684
|
const destExpanded = expandHome(dest);
|
|
685
685
|
try {
|
|
686
|
-
const
|
|
687
|
-
if (
|
|
686
|
+
const stat10 = await fse.lstat(destExpanded);
|
|
687
|
+
if (stat10.isSymbolicLink()) {
|
|
688
688
|
await fse.remove(destExpanded);
|
|
689
689
|
}
|
|
690
690
|
} catch {
|
|
@@ -698,8 +698,8 @@ async function pruneEmptyDirs(target) {
|
|
|
698
698
|
const expanded = expandHome(target);
|
|
699
699
|
let entries;
|
|
700
700
|
try {
|
|
701
|
-
const
|
|
702
|
-
if (!
|
|
701
|
+
const stat10 = await fse.lstat(expanded);
|
|
702
|
+
if (!stat10.isDirectory() || stat10.isSymbolicLink()) return false;
|
|
703
703
|
entries = await fse.readdir(expanded, { withFileTypes: true });
|
|
704
704
|
} catch {
|
|
705
705
|
return false;
|
|
@@ -781,8 +781,8 @@ async function remove(p) {
|
|
|
781
781
|
}
|
|
782
782
|
async function getFileMtime(filePath) {
|
|
783
783
|
try {
|
|
784
|
-
const
|
|
785
|
-
return
|
|
784
|
+
const stat10 = await fse.stat(expandHome(filePath));
|
|
785
|
+
return stat10.mtimeMs;
|
|
786
786
|
} catch {
|
|
787
787
|
return 0;
|
|
788
788
|
}
|
|
@@ -796,8 +796,8 @@ async function getDirLatestMtime(dirPath) {
|
|
|
796
796
|
if (isIgnored(entry.name)) continue;
|
|
797
797
|
const fullPath = path5.join(expanded, entry.name);
|
|
798
798
|
if (entry.isFile()) {
|
|
799
|
-
const
|
|
800
|
-
if (
|
|
799
|
+
const stat10 = await fse.stat(fullPath);
|
|
800
|
+
if (stat10.mtimeMs > latest) latest = stat10.mtimeMs;
|
|
801
801
|
} else if (entry.isDirectory()) {
|
|
802
802
|
const sub = await getDirLatestMtime(fullPath);
|
|
803
803
|
if (sub > latest) latest = sub;
|
|
@@ -3765,11 +3765,12 @@ export const TeamaiHooks = async ({ directory, worktree }) => {
|
|
|
3765
3765
|
`;
|
|
3766
3766
|
}
|
|
3767
3767
|
async function injectOpencodeHooks(baseDir, scope) {
|
|
3768
|
-
const
|
|
3769
|
-
await
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3768
|
+
const file = path14.join(resolveOpencodePluginDir(baseDir, scope), OPENCODE_HOOK_FILE);
|
|
3769
|
+
if (await writeIfChanged(file, buildPluginSource())) {
|
|
3770
|
+
log.success(`Injected teamai OpenCode hook into ${file}`);
|
|
3771
|
+
} else {
|
|
3772
|
+
log.debug(`teamai OpenCode hook already up-to-date in ${file}`);
|
|
3773
|
+
}
|
|
3773
3774
|
}
|
|
3774
3775
|
async function removeOpencodeHooks(baseDir, scope) {
|
|
3775
3776
|
const dir = resolveOpencodePluginDir(baseDir, scope);
|
|
@@ -3935,11 +3936,12 @@ export default function teamaiHooks(pi) {
|
|
|
3935
3936
|
`;
|
|
3936
3937
|
}
|
|
3937
3938
|
async function injectOmpHooks() {
|
|
3938
|
-
const
|
|
3939
|
-
await
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3939
|
+
const file = path15.join(resolveOmpExtensionsDir(), OMP_HOOK_FILE);
|
|
3940
|
+
if (await writeIfChanged(file, buildOmpExtensionSource())) {
|
|
3941
|
+
log.success(`Injected teamai OMP hook into ${file}`);
|
|
3942
|
+
} else {
|
|
3943
|
+
log.debug(`teamai OMP hook already up-to-date in ${file}`);
|
|
3944
|
+
}
|
|
3943
3945
|
}
|
|
3944
3946
|
async function removeOmpHooks() {
|
|
3945
3947
|
const file = path15.join(resolveOmpExtensionsDir(), OMP_HOOK_FILE);
|
|
@@ -4062,9 +4064,11 @@ async function injectPiHooks() {
|
|
|
4062
4064
|
log.warn(`Skipping Pi hook injection: ${file} exists without the TeamAI marker`);
|
|
4063
4065
|
return;
|
|
4064
4066
|
}
|
|
4065
|
-
await
|
|
4066
|
-
|
|
4067
|
-
|
|
4067
|
+
if (await writeIfChanged(file, buildPiExtensionSource())) {
|
|
4068
|
+
log.success(`Injected teamai Pi hook into ${file}`);
|
|
4069
|
+
} else {
|
|
4070
|
+
log.debug(`teamai Pi hook already up-to-date in ${file}`);
|
|
4071
|
+
}
|
|
4068
4072
|
}
|
|
4069
4073
|
async function removePiHooks() {
|
|
4070
4074
|
const file = path16.join(resolvePiExtensionsDir(), PI_HOOK_FILE);
|
|
@@ -4236,10 +4240,13 @@ async function injectOpenClawHooks(workspacePath, tool = "openclaw") {
|
|
|
4236
4240
|
return;
|
|
4237
4241
|
}
|
|
4238
4242
|
const dir = path17.join(wsDir, "hooks", OPENCLAW_HOOK_DIR);
|
|
4239
|
-
await
|
|
4240
|
-
await
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
+
const hookMdChanged = await writeIfChanged(path17.join(dir, "HOOK.md"), buildHookMd(tool));
|
|
4244
|
+
const handlerChanged = await writeIfChanged(path17.join(dir, "handler.ts"), buildHandlerTs(tool));
|
|
4245
|
+
if (hookMdChanged || handlerChanged) {
|
|
4246
|
+
log.success(`Injected teamai OpenClaw hook into ${dir}`);
|
|
4247
|
+
} else {
|
|
4248
|
+
log.debug(`teamai OpenClaw hook already up-to-date in ${dir}`);
|
|
4249
|
+
}
|
|
4243
4250
|
await enableOpenClawInternalHooks(tool);
|
|
4244
4251
|
}
|
|
4245
4252
|
async function enableOpenClawInternalHooks(tool = "openclaw") {
|
|
@@ -4526,9 +4533,10 @@ async function upsertHermesHook(event, entry) {
|
|
|
4526
4533
|
if (entry.matcher !== void 0) cleanEntry.matcher = entry.matcher;
|
|
4527
4534
|
if (entry.timeout !== void 0) cleanEntry.timeout = entry.timeout;
|
|
4528
4535
|
const newArr = [...untouched, cleanEntry];
|
|
4529
|
-
if (JSON.stringify(arr) === JSON.stringify(newArr)) return;
|
|
4536
|
+
if (JSON.stringify(arr) === JSON.stringify(newArr)) return false;
|
|
4530
4537
|
doc.setIn(["hooks", event], newArr);
|
|
4531
4538
|
await writeConfigDoc(doc);
|
|
4539
|
+
return true;
|
|
4532
4540
|
}
|
|
4533
4541
|
async function removeHermesHookByCommand(command) {
|
|
4534
4542
|
const doc = await readConfigDoc();
|
|
@@ -4561,9 +4569,10 @@ async function addHermesAllowlist(event, command) {
|
|
|
4561
4569
|
const raw = await readJson(filePath);
|
|
4562
4570
|
const data = raw && Array.isArray(raw.approvals) ? raw : { approvals: [] };
|
|
4563
4571
|
const exists3 = data.approvals.some((a) => a.event === event && a.command === command);
|
|
4564
|
-
if (exists3) return;
|
|
4572
|
+
if (exists3) return false;
|
|
4565
4573
|
data.approvals.push({ event, command });
|
|
4566
4574
|
await writeJson(filePath, data);
|
|
4575
|
+
return true;
|
|
4567
4576
|
}
|
|
4568
4577
|
async function removeHermesAllowlist(event, command) {
|
|
4569
4578
|
const filePath = getHermesAllowlistPath();
|
|
@@ -4611,16 +4620,19 @@ function buildReportScript() {
|
|
|
4611
4620
|
}
|
|
4612
4621
|
async function injectHermesHooks() {
|
|
4613
4622
|
const scriptPath = getReportScriptPath();
|
|
4614
|
-
await
|
|
4615
|
-
await writeFile(scriptPath, buildReportScript());
|
|
4623
|
+
const scriptChanged = await writeIfChanged(scriptPath, buildReportScript());
|
|
4616
4624
|
try {
|
|
4617
4625
|
await chmod(scriptPath, 493);
|
|
4618
4626
|
} catch (e) {
|
|
4619
4627
|
log.warn(`Failed to chmod Hermes hook script: ${e.message}`);
|
|
4620
4628
|
}
|
|
4621
|
-
await upsertHermesHook(REPORT_EVENT, { command: scriptPath, timeout: 60 });
|
|
4622
|
-
await addHermesAllowlist(REPORT_EVENT, scriptPath);
|
|
4623
|
-
|
|
4629
|
+
const hookChanged = await upsertHermesHook(REPORT_EVENT, { command: scriptPath, timeout: 60 });
|
|
4630
|
+
const allowlistChanged = await addHermesAllowlist(REPORT_EVENT, scriptPath);
|
|
4631
|
+
if (scriptChanged || hookChanged || allowlistChanged) {
|
|
4632
|
+
log.success("Injected teamai Hermes hook into " + scriptPath);
|
|
4633
|
+
} else {
|
|
4634
|
+
log.debug(`teamai Hermes hook already up-to-date in ${scriptPath}`);
|
|
4635
|
+
}
|
|
4624
4636
|
}
|
|
4625
4637
|
async function removeHermesHooks() {
|
|
4626
4638
|
const scriptPath = getReportScriptPath();
|
|
@@ -6491,7 +6503,7 @@ searched: relay that warning rather than conclude the team has no knowledge ther
|
|
|
6491
6503
|
import crypto2 from "crypto";
|
|
6492
6504
|
import path25 from "path";
|
|
6493
6505
|
import fse2 from "fs-extra";
|
|
6494
|
-
async function syncTeamUpdatesToLocal(teamConfig, localConfig, baseRevs, placedRules
|
|
6506
|
+
async function syncTeamUpdatesToLocal(teamConfig, localConfig, baseRevs, placedRules) {
|
|
6495
6507
|
const bases = (typeof baseRevs === "string" ? [baseRevs] : baseRevs ?? []).filter((rev) => rev !== "");
|
|
6496
6508
|
if (bases.length === 0) {
|
|
6497
6509
|
log.debug("No base revision \u2014 skipping pre-push sync");
|
|
@@ -6640,12 +6652,12 @@ async function replaceSkillDir(teamSkillDir, localSkillDir) {
|
|
|
6640
6652
|
}
|
|
6641
6653
|
async function removeLeftover(dir) {
|
|
6642
6654
|
try {
|
|
6643
|
-
const
|
|
6655
|
+
const stat10 = await fse2.lstat(dir).catch((error) => {
|
|
6644
6656
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") return null;
|
|
6645
6657
|
throw error;
|
|
6646
6658
|
});
|
|
6647
|
-
if (
|
|
6648
|
-
if (
|
|
6659
|
+
if (stat10 === null) return;
|
|
6660
|
+
if (stat10.isDirectory()) await fse2.chmod(dir, 448);
|
|
6649
6661
|
await fse2.remove(dir);
|
|
6650
6662
|
} catch (error) {
|
|
6651
6663
|
log.warn(`Could not remove ${dir} (${error instanceof Error ? error.message : String(error)}). Delete it by hand.`);
|
|
@@ -6827,11 +6839,11 @@ function gfExec(args, options) {
|
|
|
6827
6839
|
}
|
|
6828
6840
|
function getGfPath() {
|
|
6829
6841
|
try {
|
|
6830
|
-
const
|
|
6842
|
+
const stat10 = execSync(`test -x "${gfBinPath()}" && echo ok`, {
|
|
6831
6843
|
encoding: "utf-8",
|
|
6832
6844
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6833
6845
|
});
|
|
6834
|
-
if (
|
|
6846
|
+
if (stat10.trim() === "ok") return gfBinPath();
|
|
6835
6847
|
} catch {
|
|
6836
6848
|
}
|
|
6837
6849
|
try {
|
|
@@ -10827,6 +10839,27 @@ var init_hook_cwd = __esm({
|
|
|
10827
10839
|
});
|
|
10828
10840
|
|
|
10829
10841
|
// src/utils/session-id.ts
|
|
10842
|
+
async function agentSessionIdFromEnv() {
|
|
10843
|
+
if (BRIDGE_AGENT_ENV.some((name) => process.env[name])) return void 0;
|
|
10844
|
+
const ids = [...new Set(AGENT_SESSION_ENV.map((name) => process.env[name]).filter((v) => !!v))];
|
|
10845
|
+
if (ids.length <= 1) return ids[0];
|
|
10846
|
+
const { readEvents: readEvents2 } = await Promise.resolve().then(() => (init_dashboard_collector(), dashboard_collector_exports));
|
|
10847
|
+
const firstEvent = /* @__PURE__ */ new Map();
|
|
10848
|
+
const lastStart = /* @__PURE__ */ new Map();
|
|
10849
|
+
for (const { sessionId, timestamp, type } of await readEvents2()) {
|
|
10850
|
+
if (!ids.includes(sessionId)) continue;
|
|
10851
|
+
const first = firstEvent.get(sessionId);
|
|
10852
|
+
if (!first || timestamp < first) firstEvent.set(sessionId, timestamp);
|
|
10853
|
+
const start = lastStart.get(sessionId);
|
|
10854
|
+
if (type === "session_start" && (!start || timestamp > start)) lastStart.set(sessionId, timestamp);
|
|
10855
|
+
}
|
|
10856
|
+
let latest;
|
|
10857
|
+
for (const [id, first] of firstEvent) {
|
|
10858
|
+
const runStart = lastStart.get(id) ?? first;
|
|
10859
|
+
if (!latest || runStart > latest.runStart) latest = { id, runStart };
|
|
10860
|
+
}
|
|
10861
|
+
return latest?.id ?? ids[0];
|
|
10862
|
+
}
|
|
10830
10863
|
function deriveSessionId(data, options = {}) {
|
|
10831
10864
|
if (typeof data.session_id === "string" && data.session_id) {
|
|
10832
10865
|
return data.session_id;
|
|
@@ -10847,11 +10880,27 @@ function deriveSessionId(data, options = {}) {
|
|
|
10847
10880
|
function deriveDispatchSessionId(data, tool) {
|
|
10848
10881
|
return deriveSessionId(data, { includeCwd: tool.toLowerCase() !== COPILOT_TOOL_ID });
|
|
10849
10882
|
}
|
|
10883
|
+
var AGENT_SESSION_ENV, BRIDGE_AGENT_ENV;
|
|
10850
10884
|
var init_session_id = __esm({
|
|
10851
10885
|
"src/utils/session-id.ts"() {
|
|
10852
10886
|
"use strict";
|
|
10853
10887
|
init_hook_cwd();
|
|
10854
10888
|
init_types();
|
|
10889
|
+
AGENT_SESSION_ENV = [
|
|
10890
|
+
"CLAUDE_CODE_SESSION_ID",
|
|
10891
|
+
// Claude Code
|
|
10892
|
+
"CODEX_SESSION_ID",
|
|
10893
|
+
// Codex >= 0.148
|
|
10894
|
+
"CODEBUDDY_SESSION_ID",
|
|
10895
|
+
// CodeBuddy
|
|
10896
|
+
"COPILOT_AGENT_SESSION_ID",
|
|
10897
|
+
// Copilot CLI >= 1.0.29
|
|
10898
|
+
"CURSOR_CONVERSATION_ID",
|
|
10899
|
+
// Cursor
|
|
10900
|
+
"CLAUDE_SESSION_ID"
|
|
10901
|
+
// CodeBuddy's alias and older setups
|
|
10902
|
+
];
|
|
10903
|
+
BRIDGE_AGENT_ENV = ["PI_SESSION_ID", "OPENCODE"];
|
|
10855
10904
|
}
|
|
10856
10905
|
});
|
|
10857
10906
|
|
|
@@ -11011,10 +11060,10 @@ function getParentPid(pid) {
|
|
|
11011
11060
|
return ppid !== void 0 && ppid > 0 ? ppid : void 0;
|
|
11012
11061
|
}
|
|
11013
11062
|
try {
|
|
11014
|
-
const
|
|
11015
|
-
const closeParen =
|
|
11063
|
+
const stat10 = fs13.readFileSync(`/proc/${pid}/stat`, "utf-8");
|
|
11064
|
+
const closeParen = stat10.lastIndexOf(") ");
|
|
11016
11065
|
if (closeParen === -1) return void 0;
|
|
11017
|
-
const fields =
|
|
11066
|
+
const fields = stat10.slice(closeParen + 2).split(" ");
|
|
11018
11067
|
const ppid = parseInt(fields[1], 10);
|
|
11019
11068
|
return ppid > 0 ? ppid : void 0;
|
|
11020
11069
|
} catch {
|
|
@@ -11323,8 +11372,9 @@ async function acquireReclaimSentinel(sentinel, owner) {
|
|
|
11323
11372
|
});
|
|
11324
11373
|
return exclusiveCreate(sentinel, payload);
|
|
11325
11374
|
}
|
|
11326
|
-
async function acquireLock(lockPath) {
|
|
11375
|
+
async function acquireLock(lockPath, options = {}) {
|
|
11327
11376
|
const resolved = lockPath ?? expandHome(getUpdateLockPath());
|
|
11377
|
+
if (options.dryRun) return await lockState(resolved) !== "live";
|
|
11328
11378
|
const owner = randomUUID();
|
|
11329
11379
|
const payload = JSON.stringify({
|
|
11330
11380
|
pid: process.pid,
|
|
@@ -11416,6 +11466,22 @@ async function doUpdate() {
|
|
|
11416
11466
|
log.debug(`Auto-update skipped: ${reason}`);
|
|
11417
11467
|
return;
|
|
11418
11468
|
}
|
|
11469
|
+
const pkgName = getCurrentPackageName();
|
|
11470
|
+
const registry = resolveRegistryForPackage(pkgName);
|
|
11471
|
+
const target = resolveInstallPrefix();
|
|
11472
|
+
if (!target) {
|
|
11473
|
+
const root = path35.dirname(path35.dirname(fileURLToPath3(import.meta.url)));
|
|
11474
|
+
const message = `Self-update skipped: teamai is running from ${root}, which is not an npm install it can update in place (for example an \`npm link\` checkout). For a checkout, pull and rebuild it; to switch to the published package, run "npm install -g ${pkgName} --registry=${registry}".`;
|
|
11475
|
+
log.warn(message);
|
|
11476
|
+
log.persist(message);
|
|
11477
|
+
return;
|
|
11478
|
+
}
|
|
11479
|
+
if (!target.global) {
|
|
11480
|
+
const message = `Self-update is not supported for the vendored install at ${target.prefix} (npm would relocate or prune the runtime tree) \u2014 update manually.`;
|
|
11481
|
+
log.warn(message);
|
|
11482
|
+
log.persist(message);
|
|
11483
|
+
return;
|
|
11484
|
+
}
|
|
11419
11485
|
if (policy === "prompt") {
|
|
11420
11486
|
if (!isInteractive()) {
|
|
11421
11487
|
log.info(`Update available: v${result.current} \u2192 v${result.latest}. Run "teamai update" to upgrade.`);
|
|
@@ -11435,16 +11501,7 @@ async function doUpdate() {
|
|
|
11435
11501
|
return;
|
|
11436
11502
|
}
|
|
11437
11503
|
try {
|
|
11438
|
-
const pkgName = getCurrentPackageName();
|
|
11439
|
-
const registry = resolveRegistryForPackage(pkgName);
|
|
11440
11504
|
const npm = resolveNpmCommand();
|
|
11441
|
-
const target = resolveInstallPrefix();
|
|
11442
|
-
if (target && !target.global) {
|
|
11443
|
-
log.warn(
|
|
11444
|
-
`Self-update is not supported for the vendored install at ${target.prefix} (npm would relocate or prune the runtime tree) \u2014 update manually.`
|
|
11445
|
-
);
|
|
11446
|
-
return;
|
|
11447
|
-
}
|
|
11448
11505
|
await execFileAsync(
|
|
11449
11506
|
npm.cmd,
|
|
11450
11507
|
[
|
|
@@ -11452,7 +11509,7 @@ async function doUpdate() {
|
|
|
11452
11509
|
"install",
|
|
11453
11510
|
"-g",
|
|
11454
11511
|
pkgName,
|
|
11455
|
-
|
|
11512
|
+
`--prefix=${target.prefix}`,
|
|
11456
11513
|
`--registry=${registry}`
|
|
11457
11514
|
],
|
|
11458
11515
|
{ timeout: INSTALL_TIMEOUT, windowsHide: true }
|
|
@@ -11603,8 +11660,8 @@ async function readStdin() {
|
|
|
11603
11660
|
}
|
|
11604
11661
|
async function readLastAssistantOutput(transcriptPath) {
|
|
11605
11662
|
try {
|
|
11606
|
-
const
|
|
11607
|
-
const fileSize =
|
|
11663
|
+
const stat10 = await fs15.promises.stat(transcriptPath);
|
|
11664
|
+
const fileSize = stat10.size;
|
|
11608
11665
|
if (fileSize === 0) return "";
|
|
11609
11666
|
const readSize = Math.min(fileSize, TRANSCRIPT_TAIL_BYTES);
|
|
11610
11667
|
const offset = Math.max(0, fileSize - readSize);
|
|
@@ -11676,12 +11733,12 @@ async function scanJsonlTranscriptOnce(transcriptPath, modelAliases) {
|
|
|
11676
11733
|
const countedUsageKeys = /* @__PURE__ */ new Set();
|
|
11677
11734
|
const historicalRequests = await readLocalRequestRecords();
|
|
11678
11735
|
try {
|
|
11679
|
-
const
|
|
11680
|
-
if (
|
|
11736
|
+
const stat10 = await fs15.promises.stat(transcriptPath);
|
|
11737
|
+
if (stat10.size === 0) {
|
|
11681
11738
|
return { result: { interrupt, toolReject, toolError, tokens, prompts }, codexSnapshot: null };
|
|
11682
11739
|
}
|
|
11683
|
-
if (
|
|
11684
|
-
log.warn(`dashboard: transcript too large to scan (${
|
|
11740
|
+
if (stat10.size > INTERVENTION_SCAN_MAX_BYTES) {
|
|
11741
|
+
log.warn(`dashboard: transcript too large to scan (${stat10.size} bytes)`);
|
|
11685
11742
|
return { result: { interrupt, toolReject, toolError, tokens, prompts }, codexSnapshot: null };
|
|
11686
11743
|
}
|
|
11687
11744
|
const rl = readline2.createInterface({
|
|
@@ -11823,10 +11880,10 @@ function preferCodexSnapshot(current, observed) {
|
|
|
11823
11880
|
}
|
|
11824
11881
|
async function readLatestCodexUsageFromTail(transcriptPath) {
|
|
11825
11882
|
try {
|
|
11826
|
-
const
|
|
11827
|
-
if (
|
|
11828
|
-
const readSize = Math.min(
|
|
11829
|
-
const offset =
|
|
11883
|
+
const stat10 = await fs15.promises.stat(transcriptPath);
|
|
11884
|
+
if (stat10.size === 0) return null;
|
|
11885
|
+
const readSize = Math.min(stat10.size, CODEX_USAGE_TAIL_BYTES);
|
|
11886
|
+
const offset = stat10.size - readSize;
|
|
11830
11887
|
const fh = await fs15.promises.open(transcriptPath, "r");
|
|
11831
11888
|
try {
|
|
11832
11889
|
const buffer = Buffer.alloc(readSize);
|
|
@@ -11899,10 +11956,10 @@ function parseCopilotShutdown(entry) {
|
|
|
11899
11956
|
}
|
|
11900
11957
|
async function readLatestCopilotUsageFromTail(transcriptPath) {
|
|
11901
11958
|
try {
|
|
11902
|
-
const
|
|
11903
|
-
if (
|
|
11904
|
-
const readSize = Math.min(
|
|
11905
|
-
const offset =
|
|
11959
|
+
const stat10 = await fs15.promises.stat(transcriptPath);
|
|
11960
|
+
if (stat10.size === 0) return { shutdownObserved: false };
|
|
11961
|
+
const readSize = Math.min(stat10.size, COPILOT_USAGE_TAIL_BYTES);
|
|
11962
|
+
const offset = stat10.size - readSize;
|
|
11906
11963
|
const fh = await fs15.promises.open(transcriptPath, "r");
|
|
11907
11964
|
try {
|
|
11908
11965
|
const buffer = Buffer.alloc(readSize);
|
|
@@ -11954,11 +12011,11 @@ async function waitForCopilotShutdownUsage(transcriptPath, initial) {
|
|
|
11954
12011
|
}
|
|
11955
12012
|
async function findCopilotRunMarkerAfter(transcriptPath, startOffset, startedAt, endedAt, useProviderTime) {
|
|
11956
12013
|
try {
|
|
11957
|
-
const
|
|
11958
|
-
if (startOffset >
|
|
12014
|
+
const stat10 = await fs15.promises.stat(transcriptPath);
|
|
12015
|
+
if (startOffset > stat10.size || stat10.size - startOffset > COPILOT_RUN_SCAN_BYTES) {
|
|
11959
12016
|
return void 0;
|
|
11960
12017
|
}
|
|
11961
|
-
const readSize =
|
|
12018
|
+
const readSize = stat10.size - startOffset;
|
|
11962
12019
|
const offset = startOffset;
|
|
11963
12020
|
const fh = await fs15.promises.open(transcriptPath, "r");
|
|
11964
12021
|
try {
|
|
@@ -11997,8 +12054,8 @@ async function readCopilotUsageForRun(transcriptPath, marker) {
|
|
|
11997
12054
|
const descendants = /* @__PURE__ */ new Set([marker.id]);
|
|
11998
12055
|
let latest = { shutdownObserved: false };
|
|
11999
12056
|
try {
|
|
12000
|
-
const
|
|
12001
|
-
if (
|
|
12057
|
+
const stat10 = await fs15.promises.stat(transcriptPath);
|
|
12058
|
+
if (stat10.size - marker.offset > COPILOT_RUN_SCAN_BYTES) return latest;
|
|
12002
12059
|
const stream = fs15.createReadStream(transcriptPath, {
|
|
12003
12060
|
start: marker.offset,
|
|
12004
12061
|
encoding: "utf-8"
|
|
@@ -12051,8 +12108,8 @@ function resolveCopilotUsageTranscript(sessionId) {
|
|
|
12051
12108
|
}
|
|
12052
12109
|
async function readCodebuddyIndexOnce(transcriptPath) {
|
|
12053
12110
|
try {
|
|
12054
|
-
const
|
|
12055
|
-
if (
|
|
12111
|
+
const stat10 = await fs15.promises.stat(transcriptPath);
|
|
12112
|
+
if (stat10.size === 0 || stat10.size > INTERVENTION_SCAN_MAX_BYTES) return null;
|
|
12056
12113
|
const content = await fs15.promises.readFile(transcriptPath, "utf-8");
|
|
12057
12114
|
const data = JSON.parse(content);
|
|
12058
12115
|
if (!data || !Array.isArray(data.requests)) return null;
|
|
@@ -12085,8 +12142,8 @@ async function scanCodebuddyBlobs(messagesDir) {
|
|
|
12085
12142
|
const erroredCallIds = /* @__PURE__ */ new Set();
|
|
12086
12143
|
for (const blobPath of blobPaths) {
|
|
12087
12144
|
try {
|
|
12088
|
-
const
|
|
12089
|
-
if (
|
|
12145
|
+
const stat10 = await fs15.promises.stat(blobPath);
|
|
12146
|
+
if (stat10.size === 0 || stat10.size > INTERVENTION_SCAN_MAX_BYTES) continue;
|
|
12090
12147
|
const raw = await fs15.promises.readFile(blobPath, "utf-8");
|
|
12091
12148
|
const blob = JSON.parse(raw);
|
|
12092
12149
|
if (blob.role === "assistant") {
|
|
@@ -13660,11 +13717,24 @@ function sameFile(a, b, platform = process.platform) {
|
|
|
13660
13717
|
function shellQuoteValue(value) {
|
|
13661
13718
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
13662
13719
|
}
|
|
13663
|
-
function
|
|
13664
|
-
const
|
|
13665
|
-
|
|
13666
|
-
|
|
13667
|
-
|
|
13720
|
+
function findEnvBlocks(profileContent) {
|
|
13721
|
+
const blocks = [];
|
|
13722
|
+
let from = 0;
|
|
13723
|
+
for (; ; ) {
|
|
13724
|
+
const start = profileContent.indexOf(TEAMAI_ENV_START, from);
|
|
13725
|
+
if (start === -1) return blocks;
|
|
13726
|
+
const endMarker = profileContent.indexOf(TEAMAI_ENV_END, start);
|
|
13727
|
+
if (endMarker === -1) {
|
|
13728
|
+
blocks.push({ text: profileContent.slice(start), start, end: null });
|
|
13729
|
+
return blocks;
|
|
13730
|
+
}
|
|
13731
|
+
const end = endMarker + TEAMAI_ENV_END.length;
|
|
13732
|
+
blocks.push({ text: profileContent.slice(start, endMarker), start, end });
|
|
13733
|
+
from = end;
|
|
13734
|
+
}
|
|
13735
|
+
}
|
|
13736
|
+
function findEnvBlockFor(profileContent, envShPath) {
|
|
13737
|
+
return findEnvBlocks(profileContent).find((block) => envBlockReferencesDataHome(block.text, envShPath)) ?? null;
|
|
13668
13738
|
}
|
|
13669
13739
|
function envBlockSourcesPath(block, envShPath) {
|
|
13670
13740
|
const posixPath = toGeneratedForm(envShPath);
|
|
@@ -13681,9 +13751,16 @@ function candidateSpellings(envShPath) {
|
|
|
13681
13751
|
}
|
|
13682
13752
|
return [...spellings];
|
|
13683
13753
|
}
|
|
13754
|
+
function includesAsPath(text, spelling) {
|
|
13755
|
+
for (let at = text.indexOf(spelling); at !== -1; at = text.indexOf(spelling, at + 1)) {
|
|
13756
|
+
const after = at + spelling.length;
|
|
13757
|
+
if ((at === 0 || /[\s'"]/.test(text[at - 1])) && (after === text.length || /[\s'";]/.test(text[after]))) return true;
|
|
13758
|
+
}
|
|
13759
|
+
return false;
|
|
13760
|
+
}
|
|
13684
13761
|
function envBlockReferencesDataHome(block, envShPath) {
|
|
13685
13762
|
for (const spelling of candidateSpellings(envShPath)) {
|
|
13686
|
-
if (block
|
|
13763
|
+
if (includesAsPath(block, spelling) || block.includes(shellQuoteValue(spelling)) || block.includes(`"${spelling}"`)) {
|
|
13687
13764
|
return true;
|
|
13688
13765
|
}
|
|
13689
13766
|
}
|
|
@@ -13763,14 +13840,15 @@ async function resolveActiveShellProfile(envShPath, platform = process.platform)
|
|
|
13763
13840
|
const activePick = await detectShellProfile(platform);
|
|
13764
13841
|
const visited = /* @__PURE__ */ new Set();
|
|
13765
13842
|
const queue = [activePick];
|
|
13843
|
+
let firstWithBlock = null;
|
|
13766
13844
|
while (queue.length > 0) {
|
|
13767
13845
|
const current = queue.shift();
|
|
13768
13846
|
if (visited.has(current)) continue;
|
|
13769
13847
|
visited.add(current);
|
|
13770
13848
|
const content = await readFileSafe(current);
|
|
13771
|
-
const block = content ? extractEnvBlock(content) : null;
|
|
13772
|
-
if (block && envBlockReferencesDataHome(block, envShPath)) return current;
|
|
13773
13849
|
if (!content) continue;
|
|
13850
|
+
if (findEnvBlockFor(content, envShPath)) return current;
|
|
13851
|
+
if (firstWithBlock === null && findEnvBlocks(content).length > 0) firstWithBlock = current;
|
|
13774
13852
|
for (const name of SHELL_PROFILE_CANDIDATE_NAMES) {
|
|
13775
13853
|
const candidate = path38.join(home, name);
|
|
13776
13854
|
if (candidate !== current && !visited.has(candidate) && await referencesCandidate(content, name)) {
|
|
@@ -13778,7 +13856,7 @@ async function resolveActiveShellProfile(envShPath, platform = process.platform)
|
|
|
13778
13856
|
}
|
|
13779
13857
|
}
|
|
13780
13858
|
}
|
|
13781
|
-
return activePick;
|
|
13859
|
+
return firstWithBlock ?? activePick;
|
|
13782
13860
|
}
|
|
13783
13861
|
var SHELL_PROFILE_CANDIDATE_NAMES;
|
|
13784
13862
|
var init_shell_profile = __esm({
|
|
@@ -13878,12 +13956,18 @@ function parseEnvYamlDocument(raw, label) {
|
|
|
13878
13956
|
function envPushItem(relativePath, sourcePath) {
|
|
13879
13957
|
return { name: relativePath.slice("env/".length), type: "env", sourcePath, relativePath };
|
|
13880
13958
|
}
|
|
13881
|
-
|
|
13959
|
+
async function userScopeEnvShPath() {
|
|
13960
|
+
const userConfig = await loadLocalConfigForScope("user");
|
|
13961
|
+
if (userConfig) return path39.join(getDataHome(userConfig), "env.sh");
|
|
13962
|
+
return await pathExists(getUserConfigPath()) ? path39.join(getTeamaiHome("user"), "env.sh") : null;
|
|
13963
|
+
}
|
|
13964
|
+
var EnvVariableSchema, EnvYamlSchema, envEntryReader, ENV_KEY_RE, SOURCES_ENV_SH, EnvHandler;
|
|
13882
13965
|
var init_env = __esm({
|
|
13883
13966
|
"src/resources/env.ts"() {
|
|
13884
13967
|
"use strict";
|
|
13885
13968
|
init_base();
|
|
13886
13969
|
init_types();
|
|
13970
|
+
init_config();
|
|
13887
13971
|
init_fs();
|
|
13888
13972
|
init_logger();
|
|
13889
13973
|
init_namespaced_entries();
|
|
@@ -13925,6 +14009,7 @@ var init_env = __esm({
|
|
|
13925
14009
|
scopeOf: (variable) => variable
|
|
13926
14010
|
};
|
|
13927
14011
|
ENV_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
14012
|
+
SOURCES_ENV_SH = /(?:^|[\s;&|])(?:source|\.)\s[^\n;&|]*env\.sh/m;
|
|
13928
14013
|
EnvHandler = class extends ResourceHandler {
|
|
13929
14014
|
type = "env";
|
|
13930
14015
|
/**
|
|
@@ -14014,7 +14099,7 @@ var init_env = __esm({
|
|
|
14014
14099
|
if (inject) {
|
|
14015
14100
|
const profilePath = teamConfig.sharing.env.shellProfilePath ? teamConfig.sharing.env.shellProfilePath : await this.detectShellProfile(envShPath);
|
|
14016
14101
|
const shellBlock = this.generateShellBlock(teamaiHome);
|
|
14017
|
-
await this.injectShellProfile(profilePath, shellBlock);
|
|
14102
|
+
await this.injectShellProfile(profilePath, shellBlock, envShPath, localConfig.scope);
|
|
14018
14103
|
}
|
|
14019
14104
|
return true;
|
|
14020
14105
|
}
|
|
@@ -14130,17 +14215,29 @@ var init_env = __esm({
|
|
|
14130
14215
|
return resolveActiveShellProfile(envShPath, platform);
|
|
14131
14216
|
}
|
|
14132
14217
|
/**
|
|
14133
|
-
* Inject
|
|
14218
|
+
* Inject this scope's shell block into the profile file (idempotent).
|
|
14219
|
+
*
|
|
14220
|
+
* The profile keeps the user scope's block plus at most one project block,
|
|
14221
|
+
* the user's first so a project value wins on a key both define (#876). A
|
|
14222
|
+
* scope replaces its own block. Otherwise a project takes over another
|
|
14223
|
+
* project's block, never the user scope's, which keeps the project block
|
|
14224
|
+
* last-wins; a user scope goes in right before the project block. Any other
|
|
14225
|
+
* blocks (a hand-edited profile) are left alone.
|
|
14134
14226
|
*/
|
|
14135
|
-
async injectShellProfile(profilePath, block) {
|
|
14227
|
+
async injectShellProfile(profilePath, block, envShPath, scope) {
|
|
14136
14228
|
const original = await readFileSafe(profilePath) ?? "";
|
|
14137
14229
|
let content = original;
|
|
14138
|
-
const
|
|
14139
|
-
|
|
14140
|
-
if (
|
|
14141
|
-
const
|
|
14142
|
-
const
|
|
14143
|
-
|
|
14230
|
+
const blocks = findEnvBlocks(content).filter((b) => b.end !== null);
|
|
14231
|
+
let target = blocks.find((b) => envBlockReferencesDataHome(b.text, envShPath));
|
|
14232
|
+
if (!target) {
|
|
14233
|
+
const userEnvShPath = scope === "user" ? envShPath : await userScopeEnvShPath();
|
|
14234
|
+
const isUserBlock = (b) => !SOURCES_ENV_SH.test(b.text) || userEnvShPath !== null && envBlockReferencesDataHome(b.text, userEnvShPath);
|
|
14235
|
+
target = blocks.find((b) => scope === "user" ? isUserBlock(b) : !isUserBlock(b));
|
|
14236
|
+
}
|
|
14237
|
+
if (target) {
|
|
14238
|
+
content = content.substring(0, target.start) + block + content.substring(target.end);
|
|
14239
|
+
} else if (scope === "user" && blocks.length > 0) {
|
|
14240
|
+
content = content.substring(0, blocks[0].start) + block + "\n\n" + content.substring(blocks[0].start);
|
|
14144
14241
|
} else {
|
|
14145
14242
|
if (content.length > 0 && !content.endsWith("\n")) {
|
|
14146
14243
|
content += "\n";
|
|
@@ -22572,8 +22669,8 @@ async function cleanupStaleSessions(dir, currentSessionId) {
|
|
|
22572
22669
|
if (name === currentBasename) continue;
|
|
22573
22670
|
const filePath = path57.join(dir, entry);
|
|
22574
22671
|
try {
|
|
22575
|
-
const
|
|
22576
|
-
if (now -
|
|
22672
|
+
const stat10 = await fs25.promises.stat(filePath);
|
|
22673
|
+
if (now - stat10.mtimeMs > STALE_SESSION_MS) {
|
|
22577
22674
|
await fs25.promises.unlink(filePath);
|
|
22578
22675
|
}
|
|
22579
22676
|
} catch {
|
|
@@ -25974,7 +26071,7 @@ async function contribute(options) {
|
|
|
25974
26071
|
log.debug(`contribute: post-publish index rebuild skipped: ${e.message}`);
|
|
25975
26072
|
}
|
|
25976
26073
|
}
|
|
25977
|
-
const sessionId = options.sessionId ||
|
|
26074
|
+
const sessionId = options.sessionId || await agentSessionIdFromEnv() || "";
|
|
25978
26075
|
if (sessionId) {
|
|
25979
26076
|
await markContributed(sessionId);
|
|
25980
26077
|
}
|
|
@@ -26000,6 +26097,7 @@ var init_contribute = __esm({
|
|
|
26000
26097
|
init_fs();
|
|
26001
26098
|
init_logger();
|
|
26002
26099
|
init_contribute_check();
|
|
26100
|
+
init_session_id();
|
|
26003
26101
|
init_pending_learnings();
|
|
26004
26102
|
init_learnings_publish();
|
|
26005
26103
|
init_learnings_roots();
|
|
@@ -27047,7 +27145,8 @@ async function capUsageEvents(config) {
|
|
|
27047
27145
|
}
|
|
27048
27146
|
async function withUsageLock(usagePath, wait, fn) {
|
|
27049
27147
|
const lockPath = `${usagePath}.lock`;
|
|
27050
|
-
|
|
27148
|
+
const deadline = Date.now() + wait.budgetMs;
|
|
27149
|
+
for (; ; ) {
|
|
27051
27150
|
if (await acquireLock(lockPath)) {
|
|
27052
27151
|
try {
|
|
27053
27152
|
await foldPendingEvents2(usagePath);
|
|
@@ -27057,9 +27156,10 @@ async function withUsageLock(usagePath, wait, fn) {
|
|
|
27057
27156
|
}
|
|
27058
27157
|
return true;
|
|
27059
27158
|
}
|
|
27060
|
-
|
|
27159
|
+
const remaining = deadline - Date.now();
|
|
27160
|
+
if (remaining <= 0) return false;
|
|
27161
|
+
await new Promise((r) => setTimeout(r, Math.min(wait.delayMs, remaining)));
|
|
27061
27162
|
}
|
|
27062
|
-
return false;
|
|
27063
27163
|
}
|
|
27064
27164
|
function pendingPrefix(usagePath) {
|
|
27065
27165
|
return `${path70.basename(usagePath, ".jsonl")}.pending-`;
|
|
@@ -27337,8 +27437,8 @@ var init_usage_tracker = __esm({
|
|
|
27337
27437
|
];
|
|
27338
27438
|
PROJECT_SKILL_DIRS = [...SKILL_DIRS, ".github/skills"];
|
|
27339
27439
|
USAGE_EVENT_CAP = 5e3;
|
|
27340
|
-
APPEND_LOCK_WAIT = {
|
|
27341
|
-
REWRITE_LOCK_WAIT = {
|
|
27440
|
+
APPEND_LOCK_WAIT = { budgetMs: 250, delayMs: 25 };
|
|
27441
|
+
REWRITE_LOCK_WAIT = { budgetMs: 5e3, delayMs: 50 };
|
|
27342
27442
|
USAGE_SIDE_FILE_PATTERNS = ["usage.jsonl.*", "usage.pending-*.jsonl"];
|
|
27343
27443
|
}
|
|
27344
27444
|
});
|
|
@@ -28916,15 +29016,15 @@ function mergeLocalAndReported(localStats, reported) {
|
|
|
28916
29016
|
});
|
|
28917
29017
|
}
|
|
28918
29018
|
}
|
|
28919
|
-
for (const
|
|
28920
|
-
const existing = map.get(
|
|
29019
|
+
for (const stat10 of localStats) {
|
|
29020
|
+
const existing = map.get(stat10.name);
|
|
28921
29021
|
if (existing) {
|
|
28922
|
-
existing.count +=
|
|
28923
|
-
if (
|
|
28924
|
-
existing.lastUsed =
|
|
29022
|
+
existing.count += stat10.count;
|
|
29023
|
+
if (stat10.lastUsed > existing.lastUsed) {
|
|
29024
|
+
existing.lastUsed = stat10.lastUsed;
|
|
28925
29025
|
}
|
|
28926
29026
|
} else {
|
|
28927
|
-
map.set(
|
|
29027
|
+
map.set(stat10.name, { ...stat10 });
|
|
28928
29028
|
}
|
|
28929
29029
|
}
|
|
28930
29030
|
return Array.from(map.values()).sort((a, b) => b.count - a.count);
|
|
@@ -29019,10 +29119,10 @@ async function showStats(options = {}) {
|
|
|
29019
29119
|
console.log("");
|
|
29020
29120
|
const maxNameLen = Math.max(...stats.map((s) => s.name.length), 4);
|
|
29021
29121
|
const maxCountLen = Math.max(...stats.map((s) => String(s.count).length), 4);
|
|
29022
|
-
for (const
|
|
29023
|
-
const name =
|
|
29024
|
-
const count = String(
|
|
29025
|
-
const recency = formatRelativeTime(
|
|
29122
|
+
for (const stat10 of stats) {
|
|
29123
|
+
const name = stat10.name.padEnd(maxNameLen);
|
|
29124
|
+
const count = String(stat10.count).padStart(maxCountLen);
|
|
29125
|
+
const recency = formatRelativeTime(stat10.lastUsed);
|
|
29026
29126
|
console.log(` ${name} ${count} uses last: ${recency}`);
|
|
29027
29127
|
}
|
|
29028
29128
|
const totalEvents = stats.reduce((sum, s) => sum + s.count, 0);
|
|
@@ -29259,16 +29359,16 @@ function mergeStats(existing, username, newEvents) {
|
|
|
29259
29359
|
skills[name] = { count: data.count, lastUsed: data.lastUsed };
|
|
29260
29360
|
}
|
|
29261
29361
|
}
|
|
29262
|
-
for (const
|
|
29263
|
-
const prev = skills[
|
|
29264
|
-
const newLastUsed =
|
|
29362
|
+
for (const stat10 of newEvents) {
|
|
29363
|
+
const prev = skills[stat10.name];
|
|
29364
|
+
const newLastUsed = stat10.lastUsed.toISOString();
|
|
29265
29365
|
if (prev) {
|
|
29266
|
-
prev.count +=
|
|
29366
|
+
prev.count += stat10.count;
|
|
29267
29367
|
if (newLastUsed > prev.lastUsed) {
|
|
29268
29368
|
prev.lastUsed = newLastUsed;
|
|
29269
29369
|
}
|
|
29270
29370
|
} else {
|
|
29271
|
-
skills[
|
|
29371
|
+
skills[stat10.name] = { count: stat10.count, lastUsed: newLastUsed };
|
|
29272
29372
|
}
|
|
29273
29373
|
}
|
|
29274
29374
|
return {
|
|
@@ -29304,7 +29404,7 @@ async function readSnapshot(name, config, split, persist = true) {
|
|
|
29304
29404
|
if (!persist) return seed;
|
|
29305
29405
|
try {
|
|
29306
29406
|
await writeJson(own, seed);
|
|
29307
|
-
const sharedTime = await fs35.promises.stat(sharedSnapshotPath(name)).then((
|
|
29407
|
+
const sharedTime = await fs35.promises.stat(sharedSnapshotPath(name)).then((stat10) => stat10.mtime, () => void 0);
|
|
29308
29408
|
if (sharedTime) await fs35.promises.utimes(own, sharedTime, sharedTime);
|
|
29309
29409
|
} catch (e) {
|
|
29310
29410
|
log.debug(`Could not seed ${own}: ${e.message}`);
|
|
@@ -29696,7 +29796,7 @@ function metricsAsOf(events, at) {
|
|
|
29696
29796
|
return at === void 0 ? void 0 : aggregateSessionMetrics(events.filter((e) => Date.parse(e.timestamp) <= at));
|
|
29697
29797
|
}
|
|
29698
29798
|
async function snapshotWrittenAt(config) {
|
|
29699
|
-
const mtime = (file) => fs35.promises.stat(file).then((
|
|
29799
|
+
const mtime = (file) => fs35.promises.stat(file).then((stat10) => stat10.mtimeMs, () => void 0);
|
|
29700
29800
|
const own = await mtime(scopeSnapshotPath("prompt-tokens", config));
|
|
29701
29801
|
if (own === void 0) return mtime(sharedSnapshotPath("prompt-tokens"));
|
|
29702
29802
|
const stats = config ? await mtime(path77.join(getReportsDir(config), "stats", `${config.username}.yaml`)) : void 0;
|
|
@@ -30357,10 +30457,10 @@ async function envDeliveryProblems(ctx) {
|
|
|
30357
30457
|
teamConfig?.sharing?.env?.shellProfilePath ?? await envHandler2.detectShellProfile(envShPath)
|
|
30358
30458
|
);
|
|
30359
30459
|
const profile = await readFileSafe(profilePath);
|
|
30360
|
-
const block = profile === null ? null :
|
|
30460
|
+
const block = profile === null ? null : findEnvBlockFor(profile, envShPath);
|
|
30361
30461
|
if (block === null) {
|
|
30362
|
-
problems.push(`${profilePath} carries no TeamAI env block`);
|
|
30363
|
-
} else if (envSh !== null && !envBlockSourcesPath(block, envShPath)) {
|
|
30462
|
+
problems.push(`${profilePath} carries no TeamAI env block for ${envShPath}`);
|
|
30463
|
+
} else if (envSh !== null && !envBlockSourcesPath(block.text, envShPath)) {
|
|
30364
30464
|
problems.push(
|
|
30365
30465
|
`the block in ${profilePath} does not load ${envShPath}: a POSIX shell reads an unquoted backslash as an escape, so the \`[ -f ... ]\` test fails and \`source\` never runs`
|
|
30366
30466
|
);
|
|
@@ -30371,10 +30471,7 @@ async function envDeliveryProblems(ctx) {
|
|
|
30371
30471
|
const candidate = path78.join(home, name);
|
|
30372
30472
|
if (sameFile(candidate, profilePath)) continue;
|
|
30373
30473
|
const content = await readFileSafe(candidate);
|
|
30374
|
-
|
|
30375
|
-
if (strayBlock && envBlockReferencesDataHome(strayBlock, envShPath)) {
|
|
30376
|
-
staleProfiles.push(candidate);
|
|
30377
|
-
}
|
|
30474
|
+
if (content && findEnvBlockFor(content, envShPath)) staleProfiles.push(candidate);
|
|
30378
30475
|
}
|
|
30379
30476
|
return { problems, staleProfiles };
|
|
30380
30477
|
}
|
|
@@ -32025,8 +32122,8 @@ async function pull(options, result) {
|
|
|
32025
32122
|
const lockScope = async (config) => {
|
|
32026
32123
|
if (config.repo.kind === "http") return true;
|
|
32027
32124
|
const lock = path81.join(getDataHome(config), SYNC_LOCK_FILENAME);
|
|
32028
|
-
if (await acquireLock(lock)) {
|
|
32029
|
-
heldLocks.set(config, lock);
|
|
32125
|
+
if (await acquireLock(lock, { dryRun: options.dryRun })) {
|
|
32126
|
+
if (!options.dryRun) heldLocks.set(config, lock);
|
|
32030
32127
|
return true;
|
|
32031
32128
|
}
|
|
32032
32129
|
log.info(`[${config.scope}] sync in progress elsewhere \u2014 skipped (another pull/push holds the lock)`);
|
|
@@ -32037,9 +32134,13 @@ async function pull(options, result) {
|
|
|
32037
32134
|
let projectConfig = null;
|
|
32038
32135
|
const unreadable = [];
|
|
32039
32136
|
try {
|
|
32040
|
-
projectConfig = await detectProjectConfig(
|
|
32041
|
-
|
|
32042
|
-
|
|
32137
|
+
projectConfig = await detectProjectConfig(
|
|
32138
|
+
void 0,
|
|
32139
|
+
(configPath, error) => {
|
|
32140
|
+
unreadable.push(`${configPath}: ${error}`);
|
|
32141
|
+
},
|
|
32142
|
+
{ dryRun: options.dryRun }
|
|
32143
|
+
);
|
|
32043
32144
|
} catch (e) {
|
|
32044
32145
|
log.warn(`Project-scope detection error: ${e.message}`);
|
|
32045
32146
|
}
|
|
@@ -32059,7 +32160,7 @@ async function pull(options, result) {
|
|
|
32059
32160
|
log.info("project scope detected, skipped user scope");
|
|
32060
32161
|
} else {
|
|
32061
32162
|
try {
|
|
32062
|
-
const loadedUserConfig = await loadLocalConfigForScope("user");
|
|
32163
|
+
const loadedUserConfig = await loadLocalConfigForScope("user", void 0, { dryRun: options.dryRun });
|
|
32063
32164
|
if (loadedUserConfig) {
|
|
32064
32165
|
if (inheritUserScope) {
|
|
32065
32166
|
inheritedUserConfig = loadedUserConfig;
|
|
@@ -32737,7 +32838,10 @@ var init_rules = __esm({
|
|
|
32737
32838
|
}
|
|
32738
32839
|
}
|
|
32739
32840
|
await this.activateOpencodeInstructions(teamConfig, localConfig, rules.length > 0);
|
|
32740
|
-
if (rules.length === 0)
|
|
32841
|
+
if (rules.length === 0) {
|
|
32842
|
+
await this.reclaimUnselectedTeamRules(teamConfig, localConfig);
|
|
32843
|
+
return;
|
|
32844
|
+
}
|
|
32741
32845
|
for (const rule of rules) {
|
|
32742
32846
|
await this.pullItem(rule, teamConfig, localConfig);
|
|
32743
32847
|
}
|
|
@@ -32865,6 +32969,33 @@ var init_rules = __esm({
|
|
|
32865
32969
|
/**
|
|
32866
32970
|
* Recursively remove empty subdirectories under a given directory.
|
|
32867
32971
|
*/
|
|
32972
|
+
/**
|
|
32973
|
+
* Remove the copies of team rules that no longer reach this directory when none
|
|
32974
|
+
* does — e.g. the last rule of a project the directory dropped, or of one an
|
|
32975
|
+
* admin removed. Only a file TeamAI provably wrote goes: it sits at a team rule's
|
|
32976
|
+
* delivery path and holds exactly what pull rendered for that tool, from the
|
|
32977
|
+
* rule as it is now or as it was at a revision this checkout last pulled (the
|
|
32978
|
+
* admin may have edited the rule before removing its project). That proof holds
|
|
32979
|
+
* in rule directories shared with user-authored rules too, so JoyCode, OMP, Pi
|
|
32980
|
+
* and Copilot are reclaimed like the rest. A personal rule, a locally edited
|
|
32981
|
+
* copy and the author's own copy of a rule they published stay.
|
|
32982
|
+
*/
|
|
32983
|
+
async reclaimUnselectedTeamRules(teamConfig, localConfig) {
|
|
32984
|
+
const teamRules = await this.scanTeamForPull(teamConfig, localConfig);
|
|
32985
|
+
if (teamRules.length === 0) return;
|
|
32986
|
+
const deliveredRevs = (await (await Promise.resolve().then(() => (init_pull(), pull_exports))).resolveCheckoutBases(localConfig, await loadStateForScope(localConfig))).revs;
|
|
32987
|
+
const touchedDirs = /* @__PURE__ */ new Set();
|
|
32988
|
+
for (const item of teamRules) {
|
|
32989
|
+
for (const { tool, dest, supersedes } of await this.deliveryTargets(teamConfig, localConfig, item)) {
|
|
32990
|
+
if (supersedes) continue;
|
|
32991
|
+
if (!await isDeliveredRender(tool, dest, item, localConfig.repo.localPath, deliveredRevs)) continue;
|
|
32992
|
+
await remove(dest);
|
|
32993
|
+
touchedDirs.add(path83.join(resolveToolBaseDir(tool, localConfig), scopedToolPaths(teamConfig, localConfig)[tool].rules));
|
|
32994
|
+
log.debug(`Removed unselected team rule ${item.name} from ${tool}`);
|
|
32995
|
+
}
|
|
32996
|
+
}
|
|
32997
|
+
for (const dir of touchedDirs) await this.removeEmptyDirs(dir);
|
|
32998
|
+
}
|
|
32868
32999
|
async removeEmptyDirs(dir) {
|
|
32869
33000
|
if (!await pathExists(dir)) return;
|
|
32870
33001
|
const subdirs = await listDirs(dir);
|
|
@@ -33273,6 +33404,7 @@ __export(push_exports, {
|
|
|
33273
33404
|
filterExistingTopLevelPaths: () => filterExistingTopLevelPaths,
|
|
33274
33405
|
push: () => push
|
|
33275
33406
|
});
|
|
33407
|
+
import { chmod as chmod2, readFile as readFile4, stat as stat3 } from "fs/promises";
|
|
33276
33408
|
import path86 from "path";
|
|
33277
33409
|
import YAML20 from "yaml";
|
|
33278
33410
|
async function filterExistingTopLevelPaths(repoPath, candidates) {
|
|
@@ -33453,13 +33585,22 @@ async function hasGitModeChange(git, filePath) {
|
|
|
33453
33585
|
return true;
|
|
33454
33586
|
}
|
|
33455
33587
|
}
|
|
33456
|
-
function
|
|
33588
|
+
async function readFileSnapshot(filePath) {
|
|
33589
|
+
try {
|
|
33590
|
+
const [content, stats] = await Promise.all([readFile4(filePath), stat3(filePath)]);
|
|
33591
|
+
return { content, mode: stats.mode & 511 };
|
|
33592
|
+
} catch {
|
|
33593
|
+
return null;
|
|
33594
|
+
}
|
|
33595
|
+
}
|
|
33596
|
+
function isTeamaiOwnedDirtyPath(filePath, pendingTeamConfig, modeChangedPaths, pendingEnvPaths) {
|
|
33457
33597
|
const normalized = filePath.replaceAll("\\", "/");
|
|
33458
33598
|
if (normalized === ".teamai/.sync-lock") return true;
|
|
33599
|
+
if (pendingEnvPaths.has(normalized)) return true;
|
|
33459
33600
|
return normalized === "teamai.yaml" && pendingTeamConfig !== null && !modeChangedPaths.has(normalized);
|
|
33460
33601
|
}
|
|
33461
|
-
function collectUnsafeDirtyPaths(status2, pendingTeamConfig, modeChangedPaths = /* @__PURE__ */ new Set()) {
|
|
33462
|
-
return collectDirtyPaths(status2).filter((filePath) => !isTeamaiOwnedDirtyPath(filePath, pendingTeamConfig, modeChangedPaths));
|
|
33602
|
+
function collectUnsafeDirtyPaths(status2, pendingTeamConfig, modeChangedPaths = /* @__PURE__ */ new Set(), pendingEnvPaths = /* @__PURE__ */ new Set()) {
|
|
33603
|
+
return collectDirtyPaths(status2).filter((filePath) => !isTeamaiOwnedDirtyPath(filePath, pendingTeamConfig, modeChangedPaths, pendingEnvPaths));
|
|
33463
33604
|
}
|
|
33464
33605
|
function collisionPaths(type, placedAt) {
|
|
33465
33606
|
if (type !== "agents") return [placedAt];
|
|
@@ -33579,7 +33720,7 @@ async function pushGroup(args) {
|
|
|
33579
33720
|
log.debug(`Marketplace refresh skipped: ${e.message}`);
|
|
33580
33721
|
}
|
|
33581
33722
|
}
|
|
33582
|
-
const sweeperCandidates = ["rules/", "
|
|
33723
|
+
const sweeperCandidates = ["rules/", ".codebuddy-plugin/"];
|
|
33583
33724
|
const existingSweepers = await filterExistingTopLevelPaths(
|
|
33584
33725
|
localConfig.repo.localPath,
|
|
33585
33726
|
sweeperCandidates
|
|
@@ -33655,7 +33796,7 @@ ${items.map((i) => `- [${i.type}] ${i.name}`).join("\n")}`
|
|
|
33655
33796
|
}
|
|
33656
33797
|
}
|
|
33657
33798
|
async function push(options, result) {
|
|
33658
|
-
const { localConfig, teamConfig } = await autoDetectInit();
|
|
33799
|
+
const { localConfig, teamConfig } = await autoDetectInit(void 0, { dryRun: options.dryRun });
|
|
33659
33800
|
assertNotReadOnly(localConfig, "teamai push");
|
|
33660
33801
|
if (options.project && options.role) {
|
|
33661
33802
|
log.error("Use either --role or --project, not both.");
|
|
@@ -33676,30 +33817,22 @@ async function push(options, result) {
|
|
|
33676
33817
|
}
|
|
33677
33818
|
if (localConfig.repo.kind === "self") {
|
|
33678
33819
|
const selfSyncLock = path86.join(getDataHome(localConfig), SYNC_LOCK_FILENAME);
|
|
33679
|
-
if (!await acquireLock(selfSyncLock)) {
|
|
33820
|
+
if (!await acquireLock(selfSyncLock, { dryRun: options.dryRun })) {
|
|
33680
33821
|
log.error("Another teamai pull/push/migration is in progress for this project. Re-run once it finishes.");
|
|
33681
33822
|
process.exitCode = 1;
|
|
33682
33823
|
return;
|
|
33683
33824
|
}
|
|
33684
33825
|
try {
|
|
33685
|
-
|
|
33686
|
-
|
|
33687
|
-
|
|
33688
|
-
|
|
33826
|
+
if (!options.dryRun) {
|
|
33827
|
+
try {
|
|
33828
|
+
const { migrateSelfModeGitignore: migrateSelfModeGitignore2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
33829
|
+
await migrateSelfModeGitignore2(localConfig);
|
|
33830
|
+
} catch {
|
|
33831
|
+
}
|
|
33689
33832
|
}
|
|
33690
33833
|
const { withKnowledgeWorktree: withKnowledgeWorktree2, EmptyRepoError: EmptyRepoError2 } = await Promise.resolve().then(() => (init_reports_branch(), reports_branch_exports));
|
|
33691
33834
|
try {
|
|
33692
|
-
const
|
|
33693
|
-
const activeConfig = await readFileSafe(activeConfigPath);
|
|
33694
|
-
const businessRoot = localConfig.repo.businessRepoRoot ?? localConfig.projectRoot;
|
|
33695
|
-
let pendingTeamConfig = null;
|
|
33696
|
-
if (activeConfig !== null && businessRoot) {
|
|
33697
|
-
const relativeConfigPath = path86.relative(businessRoot, activeConfigPath).split(path86.sep).join("/");
|
|
33698
|
-
const committed = await getFileContentAtRev(businessRoot, "HEAD", relativeConfigPath);
|
|
33699
|
-
if (committed === null || committed.toString() !== activeConfig) {
|
|
33700
|
-
pendingTeamConfig = activeConfig;
|
|
33701
|
-
}
|
|
33702
|
-
}
|
|
33835
|
+
const pendingTeamConfig = await pendingSelfTeamConfig(localConfig);
|
|
33703
33836
|
await withKnowledgeWorktree2(localConfig, async (wtConfig) => {
|
|
33704
33837
|
if (pendingTeamConfig !== null) {
|
|
33705
33838
|
await writeFile(path86.join(wtConfig.repo.localPath, "teamai.yaml"), pendingTeamConfig);
|
|
@@ -33720,7 +33853,7 @@ async function push(options, result) {
|
|
|
33720
33853
|
return;
|
|
33721
33854
|
}
|
|
33722
33855
|
const syncLock = path86.join(getDataHome(localConfig), SYNC_LOCK_FILENAME);
|
|
33723
|
-
const locked = await acquireLock(syncLock);
|
|
33856
|
+
const locked = await acquireLock(syncLock, { dryRun: options.dryRun });
|
|
33724
33857
|
if (!locked) {
|
|
33725
33858
|
log.error("Another teamai pull/push is in progress for this project. Re-run once it finishes.");
|
|
33726
33859
|
process.exitCode = 1;
|
|
@@ -33732,9 +33865,39 @@ async function push(options, result) {
|
|
|
33732
33865
|
await releaseLock(syncLock);
|
|
33733
33866
|
}
|
|
33734
33867
|
}
|
|
33868
|
+
async function pendingSelfTeamConfig(localConfig) {
|
|
33869
|
+
const activeConfigPath = path86.join(localConfig.repo.localPath, "teamai.yaml");
|
|
33870
|
+
const activeConfig = await readFileSafe(activeConfigPath);
|
|
33871
|
+
const businessRoot = localConfig.repo.businessRepoRoot ?? localConfig.projectRoot;
|
|
33872
|
+
if (activeConfig === null || !businessRoot) return null;
|
|
33873
|
+
const relativeConfigPath = path86.relative(businessRoot, activeConfigPath).split(path86.sep).join("/");
|
|
33874
|
+
const committed = await getFileContentAtRev(businessRoot, "HEAD", relativeConfigPath);
|
|
33875
|
+
return committed === null || committed.toString() !== activeConfig ? activeConfig : null;
|
|
33876
|
+
}
|
|
33735
33877
|
async function pushCore(localConfig, teamConfig, options, initialPendingTeamConfig = null, result) {
|
|
33736
33878
|
const selfMode = localConfig.repo.kind === "self";
|
|
33737
33879
|
let pendingTeamConfig = initialPendingTeamConfig;
|
|
33880
|
+
const pendingEnvFiles = /* @__PURE__ */ new Map();
|
|
33881
|
+
const restorePendingEnvFiles = async () => {
|
|
33882
|
+
const lost = [];
|
|
33883
|
+
for (const [relativePath, { content, mode }] of pendingEnvFiles) {
|
|
33884
|
+
const target = path86.join(localConfig.repo.localPath, ...relativePath.split("/"));
|
|
33885
|
+
try {
|
|
33886
|
+
await writeFile(target, content);
|
|
33887
|
+
await chmod2(target, mode);
|
|
33888
|
+
} catch (e) {
|
|
33889
|
+
lost.push(`${relativePath} (${e.message})`);
|
|
33890
|
+
}
|
|
33891
|
+
}
|
|
33892
|
+
return lost;
|
|
33893
|
+
};
|
|
33894
|
+
const reportLostEnvFiles = (lost) => {
|
|
33895
|
+
log.error(
|
|
33896
|
+
`Could not put back ${lost.join(", ")} after resetting the team repo, so that env edit is no longer in the clone. Nothing more was pushed. Run \`teamai env add\` again for the variables it held, then push.`
|
|
33897
|
+
);
|
|
33898
|
+
process.exitCode = 1;
|
|
33899
|
+
};
|
|
33900
|
+
let lostEnvFiles = [];
|
|
33738
33901
|
let teamRepoStale = false;
|
|
33739
33902
|
if (!selfMode) {
|
|
33740
33903
|
const pullSpin = spinner("Pulling latest changes...").start();
|
|
@@ -33758,10 +33921,26 @@ async function pushCore(localConfig, teamConfig, options, initialPendingTeamConf
|
|
|
33758
33921
|
if (pendingTeamConfig !== null && await hasGitModeChange(git, "teamai.yaml")) {
|
|
33759
33922
|
modeChangedPaths.add("teamai.yaml");
|
|
33760
33923
|
}
|
|
33924
|
+
const status2 = await git.status();
|
|
33925
|
+
const indexedPaths = new Set(collectDirtyPaths({
|
|
33926
|
+
staged: status2.staged,
|
|
33927
|
+
created: status2.created,
|
|
33928
|
+
conflicted: status2.conflicted,
|
|
33929
|
+
deleted: status2.deleted,
|
|
33930
|
+
renamed: status2.renamed
|
|
33931
|
+
}));
|
|
33932
|
+
for (const item of await getHandler("env").scanLocalForPush(teamConfig, localConfig)) {
|
|
33933
|
+
if (indexedPaths.has(item.relativePath)) continue;
|
|
33934
|
+
const snapshot = await readFileSnapshot(item.sourcePath);
|
|
33935
|
+
if (snapshot !== null && !await hasGitModeChange(git, item.relativePath)) {
|
|
33936
|
+
pendingEnvFiles.set(item.relativePath, snapshot);
|
|
33937
|
+
}
|
|
33938
|
+
}
|
|
33761
33939
|
const unsafeDirtyPaths = collectUnsafeDirtyPaths(
|
|
33762
|
-
|
|
33940
|
+
status2,
|
|
33763
33941
|
pendingTeamConfig,
|
|
33764
|
-
modeChangedPaths
|
|
33942
|
+
modeChangedPaths,
|
|
33943
|
+
new Set(pendingEnvFiles.keys())
|
|
33765
33944
|
);
|
|
33766
33945
|
if (unsafeDirtyPaths.length > 0) {
|
|
33767
33946
|
pullSpin.fail(
|
|
@@ -33770,8 +33949,12 @@ async function pushCore(localConfig, teamConfig, options, initialPendingTeamConf
|
|
|
33770
33949
|
process.exitCode = 1;
|
|
33771
33950
|
return;
|
|
33772
33951
|
}
|
|
33773
|
-
|
|
33774
|
-
|
|
33952
|
+
try {
|
|
33953
|
+
await resetToCleanMaster(git, repoPath);
|
|
33954
|
+
await pullRepo(repoPath);
|
|
33955
|
+
} finally {
|
|
33956
|
+
lostEnvFiles = await restorePendingEnvFiles();
|
|
33957
|
+
}
|
|
33775
33958
|
if (pendingTeamConfig !== null) {
|
|
33776
33959
|
await writeFile(yamlPath, pendingTeamConfig);
|
|
33777
33960
|
}
|
|
@@ -33780,6 +33963,10 @@ async function pushCore(localConfig, teamConfig, options, initialPendingTeamConf
|
|
|
33780
33963
|
teamRepoStale = true;
|
|
33781
33964
|
pullSpin.warn(`Pull failed: ${e.message}`);
|
|
33782
33965
|
}
|
|
33966
|
+
if (lostEnvFiles.length > 0) {
|
|
33967
|
+
reportLostEnvFiles(lostEnvFiles);
|
|
33968
|
+
return;
|
|
33969
|
+
}
|
|
33783
33970
|
}
|
|
33784
33971
|
const brokenCatalogs = await brokenTeamProfileFiles(localConfig.repo.localPath);
|
|
33785
33972
|
if (brokenCatalogs.length > 0) {
|
|
@@ -34206,11 +34393,20 @@ async function pushCore(localConfig, teamConfig, options, initialPendingTeamConf
|
|
|
34206
34393
|
includeTeamConfig: groupIndex === configGroupIndex,
|
|
34207
34394
|
branch: options.branch
|
|
34208
34395
|
});
|
|
34209
|
-
|
|
34396
|
+
const configPending = pendingTeamConfig !== null && groupIndex <= configGroupIndex;
|
|
34397
|
+
if (outcome === "failed" && (configPending || pendingEnvFiles.size > 0)) {
|
|
34398
|
+
await checkoutMaster(localConfig.repo.localPath);
|
|
34399
|
+
}
|
|
34400
|
+
if (pendingTeamConfig !== null && configPending && (groupIndex < configGroupIndex || outcome === "failed")) {
|
|
34210
34401
|
await writeFile(path86.join(localConfig.repo.localPath, "teamai.yaml"), pendingTeamConfig);
|
|
34211
34402
|
}
|
|
34212
|
-
if (outcome === "failed") {
|
|
34403
|
+
if (outcome === "pushed" || outcome === "pr-failed") {
|
|
34404
|
+
for (const item of group.items) pendingEnvFiles.delete(item.relativePath);
|
|
34405
|
+
}
|
|
34406
|
+
const lost = await restorePendingEnvFiles();
|
|
34407
|
+
if (outcome === "failed" || lost.length > 0) {
|
|
34213
34408
|
await saveStateForScope(pushState, localConfig);
|
|
34409
|
+
if (lost.length > 0) reportLostEnvFiles(lost);
|
|
34214
34410
|
process.exitCode = 1;
|
|
34215
34411
|
return;
|
|
34216
34412
|
}
|
|
@@ -34238,6 +34434,8 @@ async function pushCore(localConfig, teamConfig, options, initialPendingTeamConf
|
|
|
34238
34434
|
options,
|
|
34239
34435
|
anyPrFailed ? void 0 : result
|
|
34240
34436
|
);
|
|
34437
|
+
const lost = await restorePendingEnvFiles();
|
|
34438
|
+
if (lost.length > 0) reportLostEnvFiles(lost);
|
|
34241
34439
|
return;
|
|
34242
34440
|
}
|
|
34243
34441
|
if (result && anyPushed && !anyPrFailed) result.completed = true;
|
|
@@ -34334,6 +34532,7 @@ __export(git_exports, {
|
|
|
34334
34532
|
getHeadRev: () => getHeadRev,
|
|
34335
34533
|
getRemoteUrl: () => getRemoteUrl,
|
|
34336
34534
|
getRepoStatus: () => getRepoStatus,
|
|
34535
|
+
gitBinary: () => gitBinary,
|
|
34337
34536
|
hasCommits: () => hasCommits,
|
|
34338
34537
|
hashObject: () => hashObject,
|
|
34339
34538
|
initRepo: () => initRepo,
|
|
@@ -34356,16 +34555,53 @@ __export(git_exports, {
|
|
|
34356
34555
|
resetToCleanMaster: () => resetToCleanMaster,
|
|
34357
34556
|
resolveAnchors: () => resolveAnchors
|
|
34358
34557
|
});
|
|
34558
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
34359
34559
|
import fs38 from "fs";
|
|
34360
34560
|
import { realpath } from "fs/promises";
|
|
34361
34561
|
import path87 from "path";
|
|
34362
34562
|
import fse13 from "fs-extra";
|
|
34363
34563
|
import simpleGit from "simple-git";
|
|
34564
|
+
function gitBinary(options = {}) {
|
|
34565
|
+
if ((options.platform ?? process.platform) === "win32") return "git";
|
|
34566
|
+
const pathEnv = options.pathEnv ?? process.env.PATH ?? "";
|
|
34567
|
+
if (resolvedGit?.pathEnv !== pathEnv || !stillExecutable(resolvedGit.binary)) {
|
|
34568
|
+
resolvedGit = { pathEnv, binary: lookUpGit(pathEnv) };
|
|
34569
|
+
}
|
|
34570
|
+
return resolvedGit.binary;
|
|
34571
|
+
}
|
|
34572
|
+
function stillExecutable(binary) {
|
|
34573
|
+
if (binary === "git") return true;
|
|
34574
|
+
try {
|
|
34575
|
+
fs38.accessSync(binary, fs38.constants.X_OK);
|
|
34576
|
+
return true;
|
|
34577
|
+
} catch {
|
|
34578
|
+
return false;
|
|
34579
|
+
}
|
|
34580
|
+
}
|
|
34581
|
+
function lookUpGit(pathEnv) {
|
|
34582
|
+
for (const dir of pathEnv.split(path87.delimiter)) {
|
|
34583
|
+
if (!path87.isAbsolute(dir)) return "git";
|
|
34584
|
+
const candidate = path87.join(dir, "git");
|
|
34585
|
+
let stat10;
|
|
34586
|
+
try {
|
|
34587
|
+
stat10 = fs38.statSync(candidate);
|
|
34588
|
+
} catch (e) {
|
|
34589
|
+
if (typeof e === "object" && e !== null && "code" in e && (e.code === "ENOENT" || e.code === "ENOTDIR")) continue;
|
|
34590
|
+
return "git";
|
|
34591
|
+
}
|
|
34592
|
+
if (!stat10.isFile() || !SIMPLE_GIT_SAFE_BINARY.test(candidate)) return "git";
|
|
34593
|
+
return spawnsByPath(candidate) ? candidate : "git";
|
|
34594
|
+
}
|
|
34595
|
+
return "git";
|
|
34596
|
+
}
|
|
34597
|
+
function spawnsByPath(candidate) {
|
|
34598
|
+
return spawnSync7(candidate, ["--version"], { stdio: "ignore", timeout: 1e4 }).error === void 0;
|
|
34599
|
+
}
|
|
34364
34600
|
function createGit(basePath) {
|
|
34365
34601
|
if (basePath) {
|
|
34366
|
-
return simpleGit({ baseDir: basePath });
|
|
34602
|
+
return simpleGit({ baseDir: basePath, binary: gitBinary() });
|
|
34367
34603
|
}
|
|
34368
|
-
return simpleGit();
|
|
34604
|
+
return simpleGit({ binary: gitBinary() });
|
|
34369
34605
|
}
|
|
34370
34606
|
function commitSkippingHooks(git, message, paths) {
|
|
34371
34607
|
if (paths === void 0) return git.commit(message, { "--no-verify": null });
|
|
@@ -34379,7 +34615,7 @@ async function isGitRepo(localPath) {
|
|
|
34379
34615
|
}
|
|
34380
34616
|
async function initRepo(remote, localPath) {
|
|
34381
34617
|
await fse13.ensureDir(localPath);
|
|
34382
|
-
const git =
|
|
34618
|
+
const git = createGit(localPath);
|
|
34383
34619
|
await git.init();
|
|
34384
34620
|
await git.addRemote("origin", remote);
|
|
34385
34621
|
}
|
|
@@ -34861,11 +35097,12 @@ async function getRepoStatus(localPath) {
|
|
|
34861
35097
|
modified: [...status2.modified, ...status2.not_added, ...status2.created]
|
|
34862
35098
|
};
|
|
34863
35099
|
}
|
|
34864
|
-
var defaultBranchCache, anchorsByDir;
|
|
35100
|
+
var SIMPLE_GIT_SAFE_BINARY, resolvedGit, defaultBranchCache, anchorsByDir;
|
|
34865
35101
|
var init_git2 = __esm({
|
|
34866
35102
|
"src/utils/git.ts"() {
|
|
34867
35103
|
"use strict";
|
|
34868
35104
|
init_logger();
|
|
35105
|
+
SIMPLE_GIT_SAFE_BINARY = /^([a-z]:)?([a-z0-9/.\\_~-]+)$/i;
|
|
34869
35106
|
defaultBranchCache = /* @__PURE__ */ new Map();
|
|
34870
35107
|
anchorsByDir = /* @__PURE__ */ new Map();
|
|
34871
35108
|
}
|
|
@@ -37027,7 +37264,7 @@ async function status(options) {
|
|
|
37027
37264
|
await statusAll();
|
|
37028
37265
|
return;
|
|
37029
37266
|
}
|
|
37030
|
-
const { localConfig, teamConfig } = await autoDetectInit();
|
|
37267
|
+
const { localConfig, teamConfig } = await autoDetectInit(void 0, { dryRun: true });
|
|
37031
37268
|
const scopeLabel = localConfig.scope;
|
|
37032
37269
|
console.log("");
|
|
37033
37270
|
log.info(`Scope: ${scopeLabel}${scopeLabel === "project" && localConfig.projectRoot ? ` (${localConfig.projectRoot})` : ""}`);
|
|
@@ -37157,7 +37394,7 @@ async function statusAll() {
|
|
|
37157
37394
|
}
|
|
37158
37395
|
}
|
|
37159
37396
|
async function list(type, options) {
|
|
37160
|
-
const { localConfig, teamConfig } = await autoDetectInit();
|
|
37397
|
+
const { localConfig, teamConfig } = await autoDetectInit(void 0, { dryRun: true });
|
|
37161
37398
|
const repoPath = localConfig.repo.localPath;
|
|
37162
37399
|
const source = options.source ?? "all";
|
|
37163
37400
|
if (!["repo", "local", "all"].includes(source)) {
|
|
@@ -37707,13 +37944,21 @@ async function removeCore(type, names, options, localConfig, teamConfig) {
|
|
|
37707
37944
|
}
|
|
37708
37945
|
if (type === "mcp" && !name.includes("/")) {
|
|
37709
37946
|
const target = await mcpRemovalTarget(name, teamItems.filter((item) => item.name === name).map(qualified), localConfig, options);
|
|
37710
|
-
|
|
37711
|
-
ambiguous
|
|
37712
|
-
|
|
37713
|
-
|
|
37714
|
-
|
|
37715
|
-
|
|
37716
|
-
|
|
37947
|
+
switch (target.kind) {
|
|
37948
|
+
case "ambiguous":
|
|
37949
|
+
ambiguous = true;
|
|
37950
|
+
break;
|
|
37951
|
+
case "not-found":
|
|
37952
|
+
notFound2.push(name);
|
|
37953
|
+
break;
|
|
37954
|
+
case "target":
|
|
37955
|
+
if (target.name !== name) log.info(`${name} is ${target.name}`);
|
|
37956
|
+
found.push(target.name);
|
|
37957
|
+
break;
|
|
37958
|
+
default: {
|
|
37959
|
+
const unhandled = target;
|
|
37960
|
+
throw new Error(`Unhandled MCP removal target: ${JSON.stringify(unhandled)}`);
|
|
37961
|
+
}
|
|
37717
37962
|
}
|
|
37718
37963
|
continue;
|
|
37719
37964
|
}
|
|
@@ -37842,33 +38087,34 @@ async function mcpRemovalTarget(name, candidates, localConfig, options) {
|
|
|
37842
38087
|
const target = await entryNamespaceFromFlags2(localConfig.repo.localPath, "mcp", options);
|
|
37843
38088
|
if (!target.ok) {
|
|
37844
38089
|
log.error(target.message);
|
|
37845
|
-
return "ambiguous";
|
|
38090
|
+
return { kind: "ambiguous" };
|
|
37846
38091
|
}
|
|
37847
38092
|
const wanted = target.namespace === null ? name : `${target.namespace}/${name}`;
|
|
37848
|
-
if (candidates.includes(wanted)) return wanted;
|
|
38093
|
+
if (candidates.includes(wanted)) return { kind: "target", name: wanted };
|
|
37849
38094
|
const file = entryFilePath2("mcp", target.namespace);
|
|
37850
38095
|
if ((await unreadableMcpFiles(localConfig.repo.localPath)).includes(file)) {
|
|
37851
38096
|
log.error(`${file} does not parse, so "${name}" cannot be found in it. Fix it in the team repo, then retry.`);
|
|
37852
|
-
return "ambiguous";
|
|
38097
|
+
return { kind: "ambiguous" };
|
|
37853
38098
|
}
|
|
37854
|
-
return
|
|
38099
|
+
return { kind: "not-found" };
|
|
37855
38100
|
}
|
|
37856
|
-
if (candidates.includes(name)) return name;
|
|
38101
|
+
if (candidates.includes(name)) return { kind: "target", name };
|
|
37857
38102
|
const unreadable = await unreadableMcpFiles(localConfig.repo.localPath);
|
|
37858
38103
|
if (unreadable.length > 0) {
|
|
37859
38104
|
log.error(
|
|
37860
38105
|
`Cannot tell which MCP file defines "${name}": ${unreadable.join(", ")} does not parse. Fix it in the team repo, or pass --role <ns> or --project <id> to name the file.`
|
|
37861
38106
|
);
|
|
37862
|
-
return "ambiguous";
|
|
38107
|
+
return { kind: "ambiguous" };
|
|
37863
38108
|
}
|
|
37864
38109
|
if (candidates.length > 1) {
|
|
37865
38110
|
const files = candidates.map((candidate) => `mcp/${candidate.slice(0, candidate.lastIndexOf("/"))}/mcp.yaml`);
|
|
37866
38111
|
log.error(
|
|
37867
38112
|
`MCP server "${name}" is not in mcp/mcp.yaml but is defined in several namespace files (${files.join(", ")}), and each reaches different members. Pass --role <ns> or --project <id> to remove it from one of them.`
|
|
37868
38113
|
);
|
|
37869
|
-
return "ambiguous";
|
|
38114
|
+
return { kind: "ambiguous" };
|
|
37870
38115
|
}
|
|
37871
|
-
|
|
38116
|
+
const only = candidates[0];
|
|
38117
|
+
return only === void 0 ? { kind: "not-found" } : { kind: "target", name: only };
|
|
37872
38118
|
}
|
|
37873
38119
|
async function unreadableMcpFiles(repoPath) {
|
|
37874
38120
|
const { listEntryFiles: listEntryFiles2 } = await Promise.resolve().then(() => (init_namespaced_entries(), namespaced_entries_exports));
|
|
@@ -39174,8 +39420,7 @@ async function buildRemovalPlan(localConfig, teamConfig, agentFilter) {
|
|
|
39174
39420
|
]));
|
|
39175
39421
|
for (const candidate of candidateProfilePaths) {
|
|
39176
39422
|
const profileContent = await readFileSafe(candidate);
|
|
39177
|
-
|
|
39178
|
-
if (block && envBlockReferencesDataHome(block, envShPath)) {
|
|
39423
|
+
if (profileContent && findEnvBlockFor(profileContent, envShPath)) {
|
|
39179
39424
|
plan.shellProfiles.push(candidate);
|
|
39180
39425
|
}
|
|
39181
39426
|
}
|
|
@@ -39437,15 +39682,15 @@ async function executeRemoval(plan) {
|
|
|
39437
39682
|
if (plan.agentFiles.length > 0) {
|
|
39438
39683
|
log.success(`Removed ${plan.agentFiles.length} agent files`);
|
|
39439
39684
|
}
|
|
39685
|
+
const envShPath = path97.join(plan.teamaiHome, "env.sh");
|
|
39440
39686
|
for (const profilePath of plan.shellProfiles) {
|
|
39441
39687
|
try {
|
|
39442
39688
|
const content = await readFileSafe(profilePath);
|
|
39443
39689
|
if (content) {
|
|
39444
|
-
const
|
|
39445
|
-
|
|
39446
|
-
|
|
39447
|
-
const
|
|
39448
|
-
const after = content.substring(endIdx + TEAMAI_ENV_END.length).replace(/^\n+/, "\n");
|
|
39690
|
+
const block = findEnvBlockFor(content, envShPath);
|
|
39691
|
+
if (block && block.end !== null) {
|
|
39692
|
+
const before = content.substring(0, block.start).replace(/\n+$/, "\n");
|
|
39693
|
+
const after = content.substring(block.end).replace(/^\n+/, "\n");
|
|
39449
39694
|
await writeFile(profilePath, before + after);
|
|
39450
39695
|
log.success(`Cleaned shell profile: ${profilePath}`);
|
|
39451
39696
|
}
|
|
@@ -40307,7 +40552,7 @@ async function saveSession(options) {
|
|
|
40307
40552
|
log.info("No local session data yet \u2014 nothing to save.");
|
|
40308
40553
|
return;
|
|
40309
40554
|
}
|
|
40310
|
-
const sessionId = options.sessionId ||
|
|
40555
|
+
const sessionId = options.sessionId || await agentSessionIdFromEnv() || mostRecentSessionId(events);
|
|
40311
40556
|
if (!sessionId) {
|
|
40312
40557
|
log.error("Could not determine a session id. Pass --session-id <id>.");
|
|
40313
40558
|
return;
|
|
@@ -40431,6 +40676,7 @@ var init_save_session = __esm({
|
|
|
40431
40676
|
init_session_collector();
|
|
40432
40677
|
init_logger();
|
|
40433
40678
|
init_async();
|
|
40679
|
+
init_session_id();
|
|
40434
40680
|
init_types();
|
|
40435
40681
|
}
|
|
40436
40682
|
});
|
|
@@ -40463,7 +40709,7 @@ async function dashboardWorkspaces(events) {
|
|
|
40463
40709
|
const seen = /* @__PURE__ */ new Set();
|
|
40464
40710
|
async function add(root, dataHome) {
|
|
40465
40711
|
if (seen.has(root)) return;
|
|
40466
|
-
if (!await fs41.stat(root).then((
|
|
40712
|
+
if (!await fs41.stat(root).then((stat10) => stat10.isDirectory()).catch(() => false)) return;
|
|
40467
40713
|
const config = await readConfigFrom(dataHome, root);
|
|
40468
40714
|
if (!config) return;
|
|
40469
40715
|
seen.add(root);
|
|
@@ -41748,16 +41994,16 @@ function renderOverviewCards(data) {
|
|
|
41748
41994
|
return `<section id="overview"><h2 data-i18n>Overview</h2><div class="cards">${cardHtml}</div></section>`;
|
|
41749
41995
|
}
|
|
41750
41996
|
function renderCoverageSection(coverage) {
|
|
41751
|
-
const rows = coverage.map((
|
|
41752
|
-
const label = escapeHtml(
|
|
41753
|
-
const pct =
|
|
41997
|
+
const rows = coverage.map((stat10) => {
|
|
41998
|
+
const label = escapeHtml(stat10.type);
|
|
41999
|
+
const pct = stat10.coveragePct;
|
|
41754
42000
|
return [
|
|
41755
42001
|
`<div class="cov-row">`,
|
|
41756
42002
|
` <div class="cov-label">${label}</div>`,
|
|
41757
42003
|
` <div class="cov-bar-wrap">`,
|
|
41758
42004
|
` <div class="cov-bar" style="width:${pct}%"></div>`,
|
|
41759
42005
|
` </div>`,
|
|
41760
|
-
` <div class="cov-stat">${
|
|
42006
|
+
` <div class="cov-stat">${stat10.covered}/${stat10.total} (${pct}%)</div>`,
|
|
41761
42007
|
`</div>`
|
|
41762
42008
|
].join("");
|
|
41763
42009
|
}).join("");
|
|
@@ -42513,7 +42759,7 @@ async function getVizSummary(opts = {}) {
|
|
|
42513
42759
|
return {
|
|
42514
42760
|
totalEntries: data.totalEntries,
|
|
42515
42761
|
overallCoveragePct: data.overallCoveragePct,
|
|
42516
|
-
coverage: (data.coverage ?? []).map((
|
|
42762
|
+
coverage: (data.coverage ?? []).map((stat10) => ({ type: stat10.type, coveragePct: stat10.coveragePct })),
|
|
42517
42763
|
source: data.source
|
|
42518
42764
|
};
|
|
42519
42765
|
}
|
|
@@ -42582,27 +42828,30 @@ async function startDashboard(port) {
|
|
|
42582
42828
|
const contextRequests = /* @__PURE__ */ new Map();
|
|
42583
42829
|
const contextCaches = /* @__PURE__ */ new Map();
|
|
42584
42830
|
const clients = /* @__PURE__ */ new Set();
|
|
42585
|
-
|
|
42586
|
-
|
|
42587
|
-
|
|
42588
|
-
|
|
42589
|
-
|
|
42590
|
-
|
|
42591
|
-
|
|
42592
|
-
const
|
|
42593
|
-
|
|
42594
|
-
for (const client of clients) {
|
|
42595
|
-
const workspace = workspaces.find((w) => w.id === clientScopes.get(client));
|
|
42596
|
-
client.write(`data: ${workspace ? JSON.stringify(rebuildSessions(workspaceEvents(events, workspace, workspaces))) : data}
|
|
42831
|
+
const pushSessions = async () => {
|
|
42832
|
+
try {
|
|
42833
|
+
const events = await readEvents(eventsPath);
|
|
42834
|
+
const workspaces = await getWorkspaces(events);
|
|
42835
|
+
const sessions = rebuildSessions(events);
|
|
42836
|
+
const data = JSON.stringify(sessions);
|
|
42837
|
+
for (const client of clients) {
|
|
42838
|
+
const workspace = workspaces.find((w) => w.id === clientScopes.get(client));
|
|
42839
|
+
client.write(`data: ${workspace ? JSON.stringify(rebuildSessions(workspaceEvents(events, workspace, workspaces))) : data}
|
|
42597
42840
|
|
|
42598
42841
|
`);
|
|
42599
|
-
}
|
|
42600
|
-
} catch (e) {
|
|
42601
|
-
log.debug(`dashboard: SSE push error: ${e.message}`);
|
|
42602
42842
|
}
|
|
42603
|
-
}
|
|
42843
|
+
} catch (e) {
|
|
42844
|
+
log.debug(`dashboard: SSE push error: ${e.message}`);
|
|
42845
|
+
}
|
|
42846
|
+
};
|
|
42847
|
+
let watchDebounce = null;
|
|
42848
|
+
const eventsFileName = path107.basename(eventsPath);
|
|
42849
|
+
const watcher = fs43.watch(path107.dirname(eventsPath), (_eventType, filename) => {
|
|
42850
|
+
if (filename && filename.toString() !== eventsFileName) return;
|
|
42851
|
+
if (watchDebounce) clearTimeout(watchDebounce);
|
|
42852
|
+
watchDebounce = setTimeout(() => void pushSessions(), 200);
|
|
42604
42853
|
});
|
|
42605
|
-
const
|
|
42854
|
+
const checkPids = async () => {
|
|
42606
42855
|
try {
|
|
42607
42856
|
const events = await readEvents(eventsPath);
|
|
42608
42857
|
const sessions = rebuildSessions(events);
|
|
@@ -42631,8 +42880,9 @@ async function startDashboard(port) {
|
|
|
42631
42880
|
} catch (e) {
|
|
42632
42881
|
log.debug(`dashboard: PID check error: ${e.message}`);
|
|
42633
42882
|
}
|
|
42634
|
-
}
|
|
42635
|
-
const
|
|
42883
|
+
};
|
|
42884
|
+
const pidCheckInterval = setInterval(() => void checkPids(), DASHBOARD_PID_CHECK_INTERVAL_MS);
|
|
42885
|
+
const handleRequest = async (req, res) => {
|
|
42636
42886
|
const url = new URL(req.url ?? "/", `http://localhost:${serverPort}`);
|
|
42637
42887
|
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
42638
42888
|
if (url.pathname === "/" || url.pathname === "/index.html") {
|
|
@@ -42761,6 +43011,15 @@ async function startDashboard(port) {
|
|
|
42761
43011
|
}
|
|
42762
43012
|
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
42763
43013
|
res.end("Not Found");
|
|
43014
|
+
};
|
|
43015
|
+
const server = http.createServer((req, res) => {
|
|
43016
|
+
handleRequest(req, res).catch((e) => {
|
|
43017
|
+
log.debug(`dashboard: request error: ${e instanceof Error ? e.message : String(e)}`);
|
|
43018
|
+
if (!res.headersSent) {
|
|
43019
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
43020
|
+
res.end(JSON.stringify({ error: "Internal error" }));
|
|
43021
|
+
}
|
|
43022
|
+
});
|
|
42764
43023
|
});
|
|
42765
43024
|
server.on("error", (err) => {
|
|
42766
43025
|
if (err.code === "EADDRINUSE") {
|
|
@@ -43144,8 +43403,8 @@ async function parseTranscriptForVotes(transcriptPath) {
|
|
|
43144
43403
|
let finalAssistantText = "";
|
|
43145
43404
|
let finalAssistantId;
|
|
43146
43405
|
try {
|
|
43147
|
-
const
|
|
43148
|
-
if (
|
|
43406
|
+
const stat10 = await fs45.promises.stat(transcriptPath);
|
|
43407
|
+
if (stat10.size === 0) return emptyResult();
|
|
43149
43408
|
} catch {
|
|
43150
43409
|
return emptyResult();
|
|
43151
43410
|
}
|
|
@@ -43837,7 +44096,7 @@ __export(mr_hint_exports, {
|
|
|
43837
44096
|
mrHint: () => mrHint,
|
|
43838
44097
|
parseRemoteToRepo: () => parseRemoteToRepo
|
|
43839
44098
|
});
|
|
43840
|
-
import { spawnSync as
|
|
44099
|
+
import { spawnSync as spawnSync8 } from "child_process";
|
|
43841
44100
|
import fs48 from "fs";
|
|
43842
44101
|
import path113 from "path";
|
|
43843
44102
|
function repoSlug(owner, repo) {
|
|
@@ -43875,7 +44134,7 @@ function saveCache(owner, repo, cache) {
|
|
|
43875
44134
|
}
|
|
43876
44135
|
function getGitRemote(cwd) {
|
|
43877
44136
|
try {
|
|
43878
|
-
const result =
|
|
44137
|
+
const result = spawnSync8("git", ["remote", "get-url", "origin"], {
|
|
43879
44138
|
cwd,
|
|
43880
44139
|
encoding: "utf-8",
|
|
43881
44140
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -43962,7 +44221,7 @@ async function listGitHubMergedMRsViaREST(owner, repo, since) {
|
|
|
43962
44221
|
}
|
|
43963
44222
|
async function listGitHubMergedMRs(owner, repo, since) {
|
|
43964
44223
|
try {
|
|
43965
|
-
const result =
|
|
44224
|
+
const result = spawnSync8(
|
|
43966
44225
|
"gh",
|
|
43967
44226
|
[
|
|
43968
44227
|
"pr",
|
|
@@ -44558,6 +44817,7 @@ __export(hook_dispatch_cli_exports, {
|
|
|
44558
44817
|
deriveDispatchSessionId: () => deriveDispatchSessionId,
|
|
44559
44818
|
hookDispatchCli: () => hookDispatchCli,
|
|
44560
44819
|
parseStdin: () => parseStdin,
|
|
44820
|
+
readStdin: () => readStdin4,
|
|
44561
44821
|
salvageStdinFields: () => salvageStdinFields,
|
|
44562
44822
|
trySpawnDetachedViaWmi: () => trySpawnDetachedViaWmi
|
|
44563
44823
|
});
|
|
@@ -44565,17 +44825,23 @@ import { spawn as spawn4 } from "child_process";
|
|
|
44565
44825
|
import fs49 from "fs";
|
|
44566
44826
|
import os10 from "os";
|
|
44567
44827
|
import path115 from "path";
|
|
44568
|
-
async function readStdin4() {
|
|
44569
|
-
if (
|
|
44828
|
+
async function readStdin4(stream = process.stdin, timeoutMs = STDIN_READ_TIMEOUT_MS) {
|
|
44829
|
+
if (stream.isTTY) return "";
|
|
44570
44830
|
const chunks = [];
|
|
44571
44831
|
const readAll = (async () => {
|
|
44572
|
-
for await (const chunk of
|
|
44832
|
+
for await (const chunk of stream) {
|
|
44573
44833
|
chunks.push(chunk);
|
|
44574
44834
|
}
|
|
44575
44835
|
})();
|
|
44836
|
+
void readAll.catch(() => {
|
|
44837
|
+
});
|
|
44576
44838
|
let timer;
|
|
44839
|
+
let timedOut = false;
|
|
44577
44840
|
const deadline = new Promise((resolve) => {
|
|
44578
|
-
timer = setTimeout(
|
|
44841
|
+
timer = setTimeout(() => {
|
|
44842
|
+
timedOut = true;
|
|
44843
|
+
resolve();
|
|
44844
|
+
}, timeoutMs);
|
|
44579
44845
|
timer.unref();
|
|
44580
44846
|
});
|
|
44581
44847
|
try {
|
|
@@ -44583,8 +44849,7 @@ async function readStdin4() {
|
|
|
44583
44849
|
} finally {
|
|
44584
44850
|
if (timer) clearTimeout(timer);
|
|
44585
44851
|
}
|
|
44586
|
-
|
|
44587
|
-
});
|
|
44852
|
+
if (timedOut && !stream.destroyed) stream.destroy();
|
|
44588
44853
|
return Buffer.concat(chunks).toString("utf-8");
|
|
44589
44854
|
}
|
|
44590
44855
|
async function spawnBackground(event, tool, matcher, raw, cwd) {
|
|
@@ -44888,7 +45153,7 @@ __export(graph_index_schema_exports, {
|
|
|
44888
45153
|
toPageSlug: () => toPageSlug,
|
|
44889
45154
|
validateGraph: () => validateGraph
|
|
44890
45155
|
});
|
|
44891
|
-
import { readFile as
|
|
45156
|
+
import { readFile as readFile5, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
|
|
44892
45157
|
import path117 from "path";
|
|
44893
45158
|
import { z as z11 } from "zod";
|
|
44894
45159
|
function toPageSlug(relativePath) {
|
|
@@ -45076,7 +45341,7 @@ function computeGraphHealth(graph) {
|
|
|
45076
45341
|
async function loadGraphIndex(wikiRoot) {
|
|
45077
45342
|
const graphPath = path117.join(wikiRoot, ".indices", "graph-index.json");
|
|
45078
45343
|
try {
|
|
45079
|
-
const raw = await
|
|
45344
|
+
const raw = await readFile5(graphPath, "utf8");
|
|
45080
45345
|
const parsed = JSON.parse(raw);
|
|
45081
45346
|
return parseGraphIndex(parsed);
|
|
45082
45347
|
} catch {
|
|
@@ -45227,7 +45492,7 @@ var init_graph_index_schema = __esm({
|
|
|
45227
45492
|
});
|
|
45228
45493
|
|
|
45229
45494
|
// src/code-knowledge-recall.ts
|
|
45230
|
-
import { readFile as
|
|
45495
|
+
import { readFile as readFile6, readdir as readdir2 } from "fs/promises";
|
|
45231
45496
|
import path118 from "path";
|
|
45232
45497
|
import matter9 from "gray-matter";
|
|
45233
45498
|
function countOccurrences(text, token) {
|
|
@@ -45365,7 +45630,7 @@ async function loadWikiPages(wikiRoot, depth) {
|
|
|
45365
45630
|
if (depth === "route") {
|
|
45366
45631
|
const routerPath = path118.join(wikiRoot, "router.md");
|
|
45367
45632
|
try {
|
|
45368
|
-
const content = await
|
|
45633
|
+
const content = await readFile6(routerPath, "utf-8");
|
|
45369
45634
|
const titleMatch = content.match(/^title:\s*(.+)$/m);
|
|
45370
45635
|
const title = titleMatch ? titleMatch[1].trim() : "Team Wiki Router";
|
|
45371
45636
|
pages.push({
|
|
@@ -45477,7 +45742,7 @@ async function loadPagesRecursive(dir, relativePath, pages, depth, currentDepth
|
|
|
45477
45742
|
if (depthFromProject <= 1 && NAVIGATION_FILES.has(entry.name)) continue;
|
|
45478
45743
|
}
|
|
45479
45744
|
try {
|
|
45480
|
-
const content = await
|
|
45745
|
+
const content = await readFile6(fullPath, "utf-8");
|
|
45481
45746
|
let title;
|
|
45482
45747
|
let sources;
|
|
45483
45748
|
try {
|
|
@@ -45632,6 +45897,7 @@ __export(recall_exports, {
|
|
|
45632
45897
|
computeIdfBaseline: () => computeIdfBaseline,
|
|
45633
45898
|
formatResults: () => formatResults,
|
|
45634
45899
|
isRelevantScore: () => isRelevantScore,
|
|
45900
|
+
normalizeLearningsScoreForRanking: () => normalizeLearningsScoreForRanking,
|
|
45635
45901
|
recall: () => recall
|
|
45636
45902
|
});
|
|
45637
45903
|
import path119 from "path";
|
|
@@ -45650,6 +45916,12 @@ function computeIdfBaseline(indexes) {
|
|
|
45650
45916
|
if (maxEntries === 0) return 1;
|
|
45651
45917
|
return Math.log((maxEntries + 1) / 2) + 1;
|
|
45652
45918
|
}
|
|
45919
|
+
function normalizeLearningsScoreForRanking(score, idfBaseline) {
|
|
45920
|
+
if (score <= 0) return 0;
|
|
45921
|
+
const baseline = idfBaseline > 0 ? idfBaseline : 1;
|
|
45922
|
+
const threshold = Math.max(baseline * LEARNINGS_RELEVANCE_RATIO, LEARNINGS_ABSOLUTE_FLOOR);
|
|
45923
|
+
return Math.min(10, Math.max(0, 4 + 2 * Math.log2(score / threshold)));
|
|
45924
|
+
}
|
|
45653
45925
|
function resolveReadablePath(indexedPath, filename, learningsBase) {
|
|
45654
45926
|
if (indexedPath && existsSync3(indexedPath)) return indexedPath;
|
|
45655
45927
|
const underBase = learningsBase ? path119.join(learningsBase, filename) : null;
|
|
@@ -45919,13 +46191,14 @@ async function recall(query, options) {
|
|
|
45919
46191
|
);
|
|
45920
46192
|
const idfBaseline = computeIdfBaseline(scopeIndexes.map((s) => s.index));
|
|
45921
46193
|
for (const { index, scope, learningsBase } of scopeIndexes) {
|
|
46194
|
+
const scopeIdfBaseline = computeIdfBaseline([index]);
|
|
45922
46195
|
const results = search(query, index);
|
|
45923
46196
|
for (const r of results) {
|
|
45924
46197
|
const entryKey = `${r.entry.type}:${r.entry.filename}`;
|
|
45925
46198
|
if (scope === "user" && projectEntryKeys.has(entryKey)) continue;
|
|
45926
46199
|
if (!seenEntries.has(entryKey)) {
|
|
45927
46200
|
seenEntries.add(entryKey);
|
|
45928
|
-
allResults.push({ ...r, scope, learningsBase });
|
|
46201
|
+
allResults.push({ ...r, scope, learningsBase, idfBaseline: scopeIdfBaseline });
|
|
45929
46202
|
}
|
|
45930
46203
|
}
|
|
45931
46204
|
}
|
|
@@ -45957,18 +46230,20 @@ async function recall(query, options) {
|
|
|
45957
46230
|
} catch {
|
|
45958
46231
|
log.warn("recall: code graph retrieval unavailable, run teamai codebase --lint to diagnose");
|
|
45959
46232
|
}
|
|
46233
|
+
const rankingScore = (result) => result.fromCodebase ? result.score : normalizeLearningsScoreForRanking(result.score, result.idfBaseline ?? idfBaseline);
|
|
45960
46234
|
allResults.sort((a, b) => {
|
|
45961
|
-
|
|
46235
|
+
const scoreDelta = rankingScore(b) - rankingScore(a);
|
|
46236
|
+
if (scoreDelta !== 0) return scoreDelta;
|
|
45962
46237
|
return (b.entry.date || "").localeCompare(a.entry.date || "");
|
|
45963
46238
|
});
|
|
45964
46239
|
if (options.check) {
|
|
45965
46240
|
const top = allResults.length > 0 ? allResults[0] : void 0;
|
|
45966
|
-
emitCheckVerdict(top?.score ?? 0, top?.fromCodebase ?? false, idfBaseline, top);
|
|
46241
|
+
emitCheckVerdict(top?.score ?? 0, top?.fromCodebase ?? false, top?.idfBaseline ?? idfBaseline, top);
|
|
45967
46242
|
return;
|
|
45968
46243
|
}
|
|
45969
46244
|
const topResults = allResults.slice(0, 5);
|
|
45970
46245
|
if (process.env.TEAMAI_RECALL_DISABLED !== "1") {
|
|
45971
|
-
recordRecallQuality(deriveSessionId({}), topResults);
|
|
46246
|
+
recordRecallQuality(await agentSessionIdFromEnv() ?? deriveSessionId({}), topResults);
|
|
45972
46247
|
}
|
|
45973
46248
|
if (topResults.length === 0) {
|
|
45974
46249
|
log.info(`No matching learnings found for "${query}".`);
|
|
@@ -46158,8 +46433,8 @@ function keyToAbsPath(key) {
|
|
|
46158
46433
|
async function loadCacheIndex() {
|
|
46159
46434
|
const indexPath = path121.join(getCacheRoot(), INDEX_FILENAME);
|
|
46160
46435
|
try {
|
|
46161
|
-
const
|
|
46162
|
-
if (
|
|
46436
|
+
const stat10 = await fs50.stat(indexPath);
|
|
46437
|
+
if (stat10.size > MAX_CONFIG_FILE_BYTES) {
|
|
46163
46438
|
throw new Error(`${indexPath} exceeds max allowed size 10MB`);
|
|
46164
46439
|
}
|
|
46165
46440
|
const raw = await fs50.readFile(indexPath, "utf8");
|
|
@@ -46188,20 +46463,20 @@ function emptyIndex() {
|
|
|
46188
46463
|
}
|
|
46189
46464
|
async function statDirSize(absPath) {
|
|
46190
46465
|
let total = 0;
|
|
46191
|
-
let
|
|
46466
|
+
let stat10;
|
|
46192
46467
|
try {
|
|
46193
|
-
|
|
46468
|
+
stat10 = await fs50.lstat(absPath);
|
|
46194
46469
|
} catch (err) {
|
|
46195
46470
|
log.debug(`[cache-index] statDirSize: lstat failed, skipping ${absPath}: ${String(err)}`);
|
|
46196
46471
|
return 0;
|
|
46197
46472
|
}
|
|
46198
|
-
if (
|
|
46473
|
+
if (stat10.isSymbolicLink()) {
|
|
46199
46474
|
return 0;
|
|
46200
46475
|
}
|
|
46201
|
-
if (
|
|
46202
|
-
return
|
|
46476
|
+
if (stat10.isFile()) {
|
|
46477
|
+
return stat10.size;
|
|
46203
46478
|
}
|
|
46204
|
-
if (!
|
|
46479
|
+
if (!stat10.isDirectory()) {
|
|
46205
46480
|
return 0;
|
|
46206
46481
|
}
|
|
46207
46482
|
let entries;
|
|
@@ -46623,8 +46898,8 @@ async function scanCandidates(opts) {
|
|
|
46623
46898
|
if (ext !== ".md" && ext !== ".txt") continue;
|
|
46624
46899
|
const absPath = path122.join(expandedDir, relPath);
|
|
46625
46900
|
try {
|
|
46626
|
-
const
|
|
46627
|
-
if (
|
|
46901
|
+
const stat10 = fs51.statSync(absPath);
|
|
46902
|
+
if (stat10.size > MAX_FILE_SIZE_BYTES) continue;
|
|
46628
46903
|
} catch (statErr) {
|
|
46629
46904
|
log.warn(`cannot stat file, skipped: ${absPath} (${String(statErr)})`);
|
|
46630
46905
|
continue;
|
|
@@ -46649,8 +46924,8 @@ async function scanCandidates(opts) {
|
|
|
46649
46924
|
if (path122.extname(relPath).toLowerCase() !== ".md") continue;
|
|
46650
46925
|
const absPath = path122.join(baseDir, relPath);
|
|
46651
46926
|
try {
|
|
46652
|
-
const
|
|
46653
|
-
if (
|
|
46927
|
+
const stat10 = fs51.statSync(absPath);
|
|
46928
|
+
if (stat10.size > MAX_FILE_SIZE_BYTES) continue;
|
|
46654
46929
|
} catch (statErr) {
|
|
46655
46930
|
log.warn(`cannot stat file, skipped: ${absPath} (${String(statErr)})`);
|
|
46656
46931
|
continue;
|
|
@@ -47095,7 +47370,7 @@ var init_iwiki_client = __esm({
|
|
|
47095
47370
|
|
|
47096
47371
|
// src/import-iwiki.ts
|
|
47097
47372
|
import path123 from "path";
|
|
47098
|
-
import { readFile as
|
|
47373
|
+
import { readFile as readFile7, writeFile as writeFile4 } from "fs/promises";
|
|
47099
47374
|
function parseIWikiInput(input) {
|
|
47100
47375
|
const trimmed = input.trim();
|
|
47101
47376
|
if (/^\d+$/.test(trimmed)) {
|
|
@@ -47204,7 +47479,7 @@ async function importFromIWiki(opts) {
|
|
|
47204
47479
|
}
|
|
47205
47480
|
async function reconcileIwikiWithCodebase(documents, teamwikiRoot) {
|
|
47206
47481
|
const graphPath = path123.join(teamwikiRoot, ".indices", "graph-index.json");
|
|
47207
|
-
const graphRaw = await
|
|
47482
|
+
const graphRaw = await readFile7(graphPath, "utf-8");
|
|
47208
47483
|
const graph = JSON.parse(graphRaw);
|
|
47209
47484
|
const codeLabels = /* @__PURE__ */ new Map();
|
|
47210
47485
|
for (const node of graph.nodes) {
|
|
@@ -47222,7 +47497,7 @@ async function reconcileIwikiWithCodebase(documents, teamwikiRoot) {
|
|
|
47222
47497
|
const files = await readdir10(projectDir).catch(() => []);
|
|
47223
47498
|
for (const file of files) {
|
|
47224
47499
|
if (!file.endsWith(".md")) continue;
|
|
47225
|
-
const content = await
|
|
47500
|
+
const content = await readFile7(path123.join(projectDir, file), "utf-8").catch(() => "");
|
|
47226
47501
|
codePageContents.set(`evidence/code/${project}/${file}`, content);
|
|
47227
47502
|
}
|
|
47228
47503
|
}
|
|
@@ -47519,8 +47794,8 @@ async function resolveDocDate(filePath, filename) {
|
|
|
47519
47794
|
return parsed;
|
|
47520
47795
|
}
|
|
47521
47796
|
}
|
|
47522
|
-
const
|
|
47523
|
-
return
|
|
47797
|
+
const stat10 = await fs52.stat(filePath);
|
|
47798
|
+
return stat10.mtime;
|
|
47524
47799
|
}
|
|
47525
47800
|
async function listComparableLearnings(learningsDirs, namespaces) {
|
|
47526
47801
|
const out = await listLearningFiles(learningsDirs);
|
|
@@ -48127,7 +48402,7 @@ var init_codebase = __esm({
|
|
|
48127
48402
|
// src/wiki-engine/code-knowledge/code-collector.ts
|
|
48128
48403
|
import { createHash as createHash11 } from "crypto";
|
|
48129
48404
|
import { execFile as execFile3 } from "child_process";
|
|
48130
|
-
import { readFile as
|
|
48405
|
+
import { readFile as readFile8, readdir as readdir3, stat as stat4 } from "fs/promises";
|
|
48131
48406
|
import path127 from "path";
|
|
48132
48407
|
import { promisify as promisify3 } from "util";
|
|
48133
48408
|
function isKeyFile(relativePath, language) {
|
|
@@ -48162,7 +48437,7 @@ async function collectCode(options) {
|
|
|
48162
48437
|
const limited = filtered.slice(0, options.maxFiles ?? 200);
|
|
48163
48438
|
const files = [];
|
|
48164
48439
|
for (const filePath of limited) {
|
|
48165
|
-
const content = await
|
|
48440
|
+
const content = await readFile8(filePath, "utf8");
|
|
48166
48441
|
const relativePath = toPosix(path127.relative(root, filePath));
|
|
48167
48442
|
const language = languageFor(filePath);
|
|
48168
48443
|
files.push({
|
|
@@ -48196,7 +48471,7 @@ async function walk(directory, results, includeTests) {
|
|
|
48196
48471
|
}
|
|
48197
48472
|
if (entry.isDirectory()) {
|
|
48198
48473
|
await walk(fullPath, results, includeTests);
|
|
48199
|
-
} else if (entry.isFile() && isCodeFile(fullPath) && (await
|
|
48474
|
+
} else if (entry.isFile() && isCodeFile(fullPath) && (await stat4(fullPath)).size < 256e3) {
|
|
48200
48475
|
results.push(fullPath);
|
|
48201
48476
|
}
|
|
48202
48477
|
}
|
|
@@ -49182,7 +49457,7 @@ var init_call_resolver = __esm({
|
|
|
49182
49457
|
});
|
|
49183
49458
|
|
|
49184
49459
|
// src/wiki-engine/code-knowledge/ast/import-resolver.ts
|
|
49185
|
-
import { readFile as
|
|
49460
|
+
import { readFile as readFile9, stat as stat5 } from "fs/promises";
|
|
49186
49461
|
import path129 from "path";
|
|
49187
49462
|
async function loadTsconfigPaths(repoRoot, fromRelativeFile) {
|
|
49188
49463
|
const absDir = path129.dirname(path129.resolve(repoRoot, fromRelativeFile));
|
|
@@ -49194,7 +49469,7 @@ async function loadTsconfigPaths(repoRoot, fromRelativeFile) {
|
|
|
49194
49469
|
}
|
|
49195
49470
|
const configPath = path129.join(dir, "tsconfig.json");
|
|
49196
49471
|
try {
|
|
49197
|
-
const raw = JSON.parse(await
|
|
49472
|
+
const raw = JSON.parse(await readFile9(configPath, "utf8"));
|
|
49198
49473
|
const configDir = path129.dirname(configPath);
|
|
49199
49474
|
const baseUrl = raw.compilerOptions?.baseUrl ?? ".";
|
|
49200
49475
|
const pathsMap = raw.compilerOptions?.paths ?? {};
|
|
@@ -49345,7 +49620,7 @@ async function buildFileExistenceChecker(repoRoot, knownFiles) {
|
|
|
49345
49620
|
if (knownFiles.has(relativePath)) return true;
|
|
49346
49621
|
try {
|
|
49347
49622
|
const abs = path129.resolve(repoRoot, relativePath);
|
|
49348
|
-
const s = await
|
|
49623
|
+
const s = await stat5(abs);
|
|
49349
49624
|
return s.isFile();
|
|
49350
49625
|
} catch {
|
|
49351
49626
|
return false;
|
|
@@ -50107,10 +50382,10 @@ var init_ast = __esm({
|
|
|
50107
50382
|
});
|
|
50108
50383
|
|
|
50109
50384
|
// src/wiki-engine/code-knowledge/code-incremental.ts
|
|
50110
|
-
import { readFile as
|
|
50385
|
+
import { readFile as readFile10, writeFile as writeFile5, stat as stat6, mkdir as mkdir3 } from "fs/promises";
|
|
50111
50386
|
import path131 from "path";
|
|
50112
50387
|
async function detectCodeIncrementalChanges(root, manifestPath2, project) {
|
|
50113
|
-
const previous = await exists(manifestPath2) ? JSON.parse(await
|
|
50388
|
+
const previous = await exists(manifestPath2) ? JSON.parse(await readFile10(manifestPath2, "utf8")) : { files: [] };
|
|
50114
50389
|
const oldSha = previous.headSha;
|
|
50115
50390
|
const newSha = await gitCommit(root);
|
|
50116
50391
|
if (oldSha && newSha && await isWorkingTreeClean(root)) {
|
|
@@ -50148,7 +50423,7 @@ function affectedPages(project, files) {
|
|
|
50148
50423
|
}
|
|
50149
50424
|
async function exists(filePath) {
|
|
50150
50425
|
try {
|
|
50151
|
-
await
|
|
50426
|
+
await stat6(path131.resolve(filePath));
|
|
50152
50427
|
return true;
|
|
50153
50428
|
} catch {
|
|
50154
50429
|
return false;
|
|
@@ -50157,7 +50432,7 @@ async function exists(filePath) {
|
|
|
50157
50432
|
async function loadFactsCache(indicesDir) {
|
|
50158
50433
|
const cachePath = path131.join(indicesDir, FACTS_CACHE_FILENAME);
|
|
50159
50434
|
try {
|
|
50160
|
-
const raw = await
|
|
50435
|
+
const raw = await readFile10(cachePath, "utf-8");
|
|
50161
50436
|
const parsed = JSON.parse(raw);
|
|
50162
50437
|
return Array.isArray(parsed) ? parsed : [];
|
|
50163
50438
|
} catch {
|
|
@@ -50171,7 +50446,7 @@ async function saveFactsCache(indicesDir, facts) {
|
|
|
50171
50446
|
async function loadInterfacesCache(indicesDir) {
|
|
50172
50447
|
const cachePath = path131.join(indicesDir, INTERFACES_CACHE_FILENAME);
|
|
50173
50448
|
try {
|
|
50174
|
-
const raw = await
|
|
50449
|
+
const raw = await readFile10(cachePath, "utf-8");
|
|
50175
50450
|
const parsed = JSON.parse(raw);
|
|
50176
50451
|
return parsed?.entries ? parsed : { entries: [], scannedAt: "" };
|
|
50177
50452
|
} catch {
|
|
@@ -50560,7 +50835,7 @@ var init_reconciler_v2_types = __esm({
|
|
|
50560
50835
|
});
|
|
50561
50836
|
|
|
50562
50837
|
// src/wiki-engine/knowledge-reconciler.ts
|
|
50563
|
-
import { readFile as
|
|
50838
|
+
import { readFile as readFile11, readdir as readdir4, stat as stat7 } from "fs/promises";
|
|
50564
50839
|
import path133 from "path";
|
|
50565
50840
|
function isExpectedSelfLoop(edge) {
|
|
50566
50841
|
return edge.from === edge.to && edge.relation === "IMPLEMENTS" && !!edge.source && REPAIRABLE_CODE_EDGE_SOURCES.has(edge.source);
|
|
@@ -50607,7 +50882,7 @@ async function loadReconciliationBase(wikiRoot) {
|
|
|
50607
50882
|
}
|
|
50608
50883
|
async function exists2(p) {
|
|
50609
50884
|
try {
|
|
50610
|
-
await
|
|
50885
|
+
await stat7(p);
|
|
50611
50886
|
return true;
|
|
50612
50887
|
} catch (error) {
|
|
50613
50888
|
if (error.code === MISSING_PATH_ERROR_CODE) return false;
|
|
@@ -50623,7 +50898,7 @@ async function readPages(dirPath) {
|
|
|
50623
50898
|
if (entry.isDirectory()) {
|
|
50624
50899
|
pages.push(...await readPages(full));
|
|
50625
50900
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
50626
|
-
const text = await
|
|
50901
|
+
const text = await readFile11(full, "utf8");
|
|
50627
50902
|
const headingMatch = text.match(/^#\s+(.+)/m);
|
|
50628
50903
|
const title = headingMatch ? headingMatch[1].trim() : entry.name.replace(/\.md$/, "");
|
|
50629
50904
|
const updatedMatch = text.match(/updated[:\s]+(\d{4}-\d{2}-\d{2})/i);
|
|
@@ -51418,7 +51693,7 @@ __export(codebase_extract_exports, {
|
|
|
51418
51693
|
extractCodebase: () => extractCodebase
|
|
51419
51694
|
});
|
|
51420
51695
|
import { statSync } from "fs";
|
|
51421
|
-
import { mkdir as mkdir5, writeFile as writeFile7, readFile as
|
|
51696
|
+
import { mkdir as mkdir5, writeFile as writeFile7, readFile as readFile12, realpath as realpath3 } from "fs/promises";
|
|
51422
51697
|
import path135 from "path";
|
|
51423
51698
|
import chalk4 from "chalk";
|
|
51424
51699
|
function detectKnowledgeGaps(facts, graph, files) {
|
|
@@ -51907,7 +52182,7 @@ async function extractCodebase(opts) {
|
|
|
51907
52182
|
if (changedFiles) {
|
|
51908
52183
|
let reused = false;
|
|
51909
52184
|
try {
|
|
51910
|
-
const existing = await
|
|
52185
|
+
const existing = await readFile12(depPathsFile, "utf-8");
|
|
51911
52186
|
if (existing.trim()) {
|
|
51912
52187
|
reused = true;
|
|
51913
52188
|
}
|
|
@@ -51925,7 +52200,7 @@ async function extractCodebase(opts) {
|
|
|
51925
52200
|
await mkdir5(evidenceDir, { recursive: true });
|
|
51926
52201
|
if (changedFiles && !pages.has("dependency-paths.md")) {
|
|
51927
52202
|
try {
|
|
51928
|
-
const existing = await
|
|
52203
|
+
const existing = await readFile12(depPathsFile, "utf-8");
|
|
51929
52204
|
pages.set("dependency-paths.md", existing);
|
|
51930
52205
|
} catch {
|
|
51931
52206
|
}
|
|
@@ -52044,7 +52319,7 @@ async function extractCodebase(opts) {
|
|
|
52044
52319
|
}));
|
|
52045
52320
|
if (changedFiles !== void 0 && changedFiles.length > 0) {
|
|
52046
52321
|
try {
|
|
52047
|
-
const oldManifestRaw = await
|
|
52322
|
+
const oldManifestRaw = await readFile12(manifestPath2, "utf-8");
|
|
52048
52323
|
const oldManifest = JSON.parse(oldManifestRaw);
|
|
52049
52324
|
const changedSet = /* @__PURE__ */ new Set([...changedFiles, ...deletedFiles]);
|
|
52050
52325
|
const unchanged = (oldManifest.files ?? []).filter((f) => !changedSet.has(f.relativePath)).map((f) => ({ relativePath: f.relativePath, sha256: f.sha256, language: f.language ?? "" }));
|
|
@@ -52059,7 +52334,7 @@ async function extractCodebase(opts) {
|
|
|
52059
52334
|
let prevBranch;
|
|
52060
52335
|
let prevIngestedMrs = [];
|
|
52061
52336
|
try {
|
|
52062
|
-
const prev = JSON.parse(await
|
|
52337
|
+
const prev = JSON.parse(await readFile12(manifestPath2, "utf-8"));
|
|
52063
52338
|
prevRepoUrl = prev.repoUrl;
|
|
52064
52339
|
prevBranch = prev.branch;
|
|
52065
52340
|
prevIngestedMrs = prev.ingestedMrs ?? [];
|
|
@@ -52396,11 +52671,11 @@ __export(deep_enrich_exports, {
|
|
|
52396
52671
|
deepEnrich: () => deepEnrich,
|
|
52397
52672
|
runHiddenDeepEnrich: () => runHiddenDeepEnrich
|
|
52398
52673
|
});
|
|
52399
|
-
import { readFile as
|
|
52674
|
+
import { readFile as readFile13, writeFile as writeFile8, readdir as readdir5, mkdir as mkdir6, unlink as unlink2 } from "fs/promises";
|
|
52400
52675
|
import path137 from "path";
|
|
52401
52676
|
async function readFileSafe2(filePath) {
|
|
52402
52677
|
try {
|
|
52403
|
-
return await
|
|
52678
|
+
return await readFile13(filePath, "utf-8");
|
|
52404
52679
|
} catch {
|
|
52405
52680
|
return "";
|
|
52406
52681
|
}
|
|
@@ -52446,7 +52721,7 @@ function isValidProgressState(v, project) {
|
|
|
52446
52721
|
async function loadProgress(evidenceDir, project, allComponents) {
|
|
52447
52722
|
const p = progressPath(evidenceDir);
|
|
52448
52723
|
try {
|
|
52449
|
-
const raw = await
|
|
52724
|
+
const raw = await readFile13(p, "utf-8");
|
|
52450
52725
|
const parsed = JSON.parse(raw);
|
|
52451
52726
|
if (isValidProgressState(parsed, project)) return parsed;
|
|
52452
52727
|
} catch {
|
|
@@ -53132,7 +53407,7 @@ var rebuild_wiki_index_exports = {};
|
|
|
53132
53407
|
__export(rebuild_wiki_index_exports, {
|
|
53133
53408
|
rebuildWikiIndex: () => rebuildWikiIndex
|
|
53134
53409
|
});
|
|
53135
|
-
import { readFile as
|
|
53410
|
+
import { readFile as readFile14, readdir as readdir7, stat as stat8, writeFile as writeFile9 } from "fs/promises";
|
|
53136
53411
|
import path139 from "path";
|
|
53137
53412
|
async function rebuildWikiIndex(teamwikiRoot) {
|
|
53138
53413
|
const evidenceCodeDir = path139.join(teamwikiRoot, "evidence", "code");
|
|
@@ -53144,7 +53419,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
|
|
|
53144
53419
|
const dirs = await readdir7(evidenceCodeDir);
|
|
53145
53420
|
for (const dir of dirs) {
|
|
53146
53421
|
const dirPath = path139.join(evidenceCodeDir, dir);
|
|
53147
|
-
const dirStat = await
|
|
53422
|
+
const dirStat = await stat8(dirPath).catch(() => null);
|
|
53148
53423
|
if (!dirStat?.isDirectory()) continue;
|
|
53149
53424
|
const info = {
|
|
53150
53425
|
slug: dir,
|
|
@@ -53158,7 +53433,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
|
|
|
53158
53433
|
};
|
|
53159
53434
|
const overviewPath = path139.join(dirPath, "overview.md");
|
|
53160
53435
|
if (await pathExists(overviewPath)) {
|
|
53161
|
-
const content = await
|
|
53436
|
+
const content = await readFile14(overviewPath, "utf-8");
|
|
53162
53437
|
const bodyStart = content.indexOf("\n\n", content.indexOf("---", 3));
|
|
53163
53438
|
if (bodyStart > 0) {
|
|
53164
53439
|
const body = content.slice(bodyStart).trim();
|
|
@@ -53171,7 +53446,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
|
|
|
53171
53446
|
}
|
|
53172
53447
|
const projectIndex = path139.join(dirPath, "index.md");
|
|
53173
53448
|
if (await pathExists(projectIndex)) {
|
|
53174
|
-
const content = await
|
|
53449
|
+
const content = await readFile14(projectIndex, "utf-8");
|
|
53175
53450
|
const factsMatch = content.match(/Facts:\s*(\d+)/);
|
|
53176
53451
|
if (factsMatch) info.facts = parseInt(factsMatch[1], 10);
|
|
53177
53452
|
const ifMatches = content.matchAll(/\|\s*(HTTP|MQ|RPC)\s*\|\s*(\d+)\s*\|/g);
|
|
@@ -53182,7 +53457,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
|
|
|
53182
53457
|
const manifestPath2 = path139.join(dirPath, "_manifest.json");
|
|
53183
53458
|
if (await pathExists(manifestPath2)) {
|
|
53184
53459
|
try {
|
|
53185
|
-
const raw = await
|
|
53460
|
+
const raw = await readFile14(manifestPath2, "utf-8");
|
|
53186
53461
|
const manifest = JSON.parse(raw);
|
|
53187
53462
|
for (const comp of manifest.components) {
|
|
53188
53463
|
if (comp.responsibilities) info.responsibilities.push(...comp.responsibilities);
|
|
@@ -53194,7 +53469,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
|
|
|
53194
53469
|
const domainsPath = path139.join(dirPath, "_domains.json");
|
|
53195
53470
|
if (await pathExists(domainsPath)) {
|
|
53196
53471
|
try {
|
|
53197
|
-
const raw = await
|
|
53472
|
+
const raw = await readFile14(domainsPath, "utf-8");
|
|
53198
53473
|
const domainMeta = JSON.parse(raw);
|
|
53199
53474
|
if (domainMeta.domain) {
|
|
53200
53475
|
info.domain = domainMeta.domain;
|
|
@@ -53210,7 +53485,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
|
|
|
53210
53485
|
}
|
|
53211
53486
|
const chainsPath = path139.join(dirPath, "dependency-paths.md");
|
|
53212
53487
|
if (await pathExists(chainsPath)) {
|
|
53213
|
-
const content = await
|
|
53488
|
+
const content = await readFile14(chainsPath, "utf-8");
|
|
53214
53489
|
const chainMatch = content.match(/(\d+)\s*call chain/);
|
|
53215
53490
|
if (chainMatch) info.callChains = parseInt(chainMatch[1], 10);
|
|
53216
53491
|
}
|
|
@@ -53227,7 +53502,7 @@ async function rebuildWikiIndex(teamwikiRoot) {
|
|
|
53227
53502
|
const graphPath = path139.join(teamwikiRoot, ".indices", "graph-index.json");
|
|
53228
53503
|
if (await pathExists(graphPath)) {
|
|
53229
53504
|
try {
|
|
53230
|
-
const raw = await
|
|
53505
|
+
const raw = await readFile14(graphPath, "utf-8");
|
|
53231
53506
|
const graph = JSON.parse(raw);
|
|
53232
53507
|
totalNodes = Array.isArray(graph.nodes) ? graph.nodes.length : 0;
|
|
53233
53508
|
totalEdges = Array.isArray(graph.edges) ? graph.edges.length : 0;
|
|
@@ -53777,8 +54052,8 @@ async function loadRepoList(filePath) {
|
|
|
53777
54052
|
if (!exists3) {
|
|
53778
54053
|
throw new Error(`Repo list not found: ${filePath}`);
|
|
53779
54054
|
}
|
|
53780
|
-
const
|
|
53781
|
-
if (
|
|
54055
|
+
const stat10 = await fs59.stat(filePath);
|
|
54056
|
+
if (stat10.size > MAX_CONFIG_FILE_BYTES2) {
|
|
53782
54057
|
throw new Error(`${filePath} exceeds max allowed size 10MB`);
|
|
53783
54058
|
}
|
|
53784
54059
|
const raw = await fs59.readFile(filePath, "utf8");
|
|
@@ -53866,7 +54141,7 @@ async function importFromRepoList(opts) {
|
|
|
53866
54141
|
failed.push({ url: entry.url, error: message });
|
|
53867
54142
|
}
|
|
53868
54143
|
}
|
|
53869
|
-
const inFlight =
|
|
54144
|
+
const inFlight = /* @__PURE__ */ new Set();
|
|
53870
54145
|
for (const entry of queue) {
|
|
53871
54146
|
while (semaphore.running >= concurrency) {
|
|
53872
54147
|
await Promise.race(inFlight);
|
|
@@ -53874,10 +54149,9 @@ async function importFromRepoList(opts) {
|
|
|
53874
54149
|
semaphore.running++;
|
|
53875
54150
|
const task = processEntry(entry).finally(() => {
|
|
53876
54151
|
semaphore.running--;
|
|
53877
|
-
|
|
53878
|
-
if (idx !== -1) inFlight.splice(idx, 1);
|
|
54152
|
+
inFlight.delete(task);
|
|
53879
54153
|
});
|
|
53880
|
-
inFlight.
|
|
54154
|
+
inFlight.add(task);
|
|
53881
54155
|
}
|
|
53882
54156
|
await Promise.all(inFlight);
|
|
53883
54157
|
if (!dryRun && succeeded.length > 0) {
|
|
@@ -54141,8 +54415,8 @@ async function loadPendingReview(cwd) {
|
|
|
54141
54415
|
if (!await fs62.pathExists(filePath)) {
|
|
54142
54416
|
return [];
|
|
54143
54417
|
}
|
|
54144
|
-
const
|
|
54145
|
-
if (
|
|
54418
|
+
const stat10 = await fs62.stat(filePath);
|
|
54419
|
+
if (stat10.size > MAX_CONFIG_FILE_BYTES3) {
|
|
54146
54420
|
throw new Error(`${filePath} exceeds max allowed size 10MB`);
|
|
54147
54421
|
}
|
|
54148
54422
|
const text = await fs62.readFile(filePath, "utf8");
|
|
@@ -54853,7 +55127,7 @@ var codebase_upgrade_wiki_exports = {};
|
|
|
54853
55127
|
__export(codebase_upgrade_wiki_exports, {
|
|
54854
55128
|
upgradeCodebaseWiki: () => upgradeCodebaseWiki
|
|
54855
55129
|
});
|
|
54856
|
-
import { readdir as readdir8, readFile as
|
|
55130
|
+
import { readdir as readdir8, readFile as readFile15 } from "fs/promises";
|
|
54857
55131
|
import path147 from "path";
|
|
54858
55132
|
import chalk6 from "chalk";
|
|
54859
55133
|
import matter13 from "gray-matter";
|
|
@@ -54885,7 +55159,7 @@ async function upgradeCodebaseWiki(opts) {
|
|
|
54885
55159
|
const slug = file.replace(".md", "");
|
|
54886
55160
|
const filePath = path147.join(teamCodebaseDir, file);
|
|
54887
55161
|
try {
|
|
54888
|
-
const content = await
|
|
55162
|
+
const content = await readFile15(filePath, "utf-8");
|
|
54889
55163
|
const parsed = matter13(content);
|
|
54890
55164
|
const source = parsed.data["source"] ?? parsed.data["repo_url"];
|
|
54891
55165
|
if (!source) {
|
|
@@ -54952,7 +55226,7 @@ __export(codebase_wiki_lint_exports, {
|
|
|
54952
55226
|
formatWikiLintReport: () => formatWikiLintReport,
|
|
54953
55227
|
lintTeamwiki: () => lintTeamwiki
|
|
54954
55228
|
});
|
|
54955
|
-
import { readFile as
|
|
55229
|
+
import { readFile as readFile16, readdir as readdir9, stat as stat9 } from "fs/promises";
|
|
54956
55230
|
import path148 from "path";
|
|
54957
55231
|
import chalk7 from "chalk";
|
|
54958
55232
|
async function lintTeamwiki(opts) {
|
|
@@ -54977,7 +55251,7 @@ async function lintTeamwiki(opts) {
|
|
|
54977
55251
|
});
|
|
54978
55252
|
} else {
|
|
54979
55253
|
try {
|
|
54980
|
-
const raw = await
|
|
55254
|
+
const raw = await readFile16(graphPath, "utf-8");
|
|
54981
55255
|
graph = JSON.parse(raw);
|
|
54982
55256
|
} catch {
|
|
54983
55257
|
addIssue({
|
|
@@ -55008,7 +55282,7 @@ async function lintTeamwiki(opts) {
|
|
|
55008
55282
|
}
|
|
55009
55283
|
for (const project of projects) {
|
|
55010
55284
|
const projectDir = path148.join(evidenceDir, project);
|
|
55011
|
-
const pStat = await
|
|
55285
|
+
const pStat = await stat9(projectDir).catch(() => null);
|
|
55012
55286
|
if (!pStat?.isDirectory()) {
|
|
55013
55287
|
if (!pStat) {
|
|
55014
55288
|
addIssue({ severity: "low", category: "stat-failed", location: `evidence/code/${project}`, message: "\u65E0\u6CD5\u8BFB\u53D6\u76EE\u5F55\u72B6\u6001" });
|
|
@@ -55046,7 +55320,7 @@ async function lintTeamwiki(opts) {
|
|
|
55046
55320
|
});
|
|
55047
55321
|
} else {
|
|
55048
55322
|
try {
|
|
55049
|
-
const raw = await
|
|
55323
|
+
const raw = await readFile16(manifestPath2, "utf-8");
|
|
55050
55324
|
const manifest = JSON.parse(raw);
|
|
55051
55325
|
if (manifest.lastScan) {
|
|
55052
55326
|
const daysSince = (Date.now() - new Date(manifest.lastScan).getTime()) / (1e3 * 60 * 60 * 24);
|
|
@@ -55152,7 +55426,7 @@ __export(codebase_cmd_exports, {
|
|
|
55152
55426
|
codebaseCmd: () => codebaseCmd
|
|
55153
55427
|
});
|
|
55154
55428
|
import path149 from "path";
|
|
55155
|
-
import { readFile as
|
|
55429
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
55156
55430
|
import chalk8 from "chalk";
|
|
55157
55431
|
async function codebaseCmd(opts) {
|
|
55158
55432
|
const cwd = process.cwd();
|
|
@@ -55233,7 +55507,7 @@ async function codebaseCmd(opts) {
|
|
|
55233
55507
|
}
|
|
55234
55508
|
let componentCount = 0;
|
|
55235
55509
|
try {
|
|
55236
|
-
const manifest = JSON.parse(await
|
|
55510
|
+
const manifest = JSON.parse(await readFile17(path149.join(evidenceDir, "_manifest.json"), "utf-8"));
|
|
55237
55511
|
componentCount = Array.isArray(manifest.components) ? manifest.components.length : 0;
|
|
55238
55512
|
} catch {
|
|
55239
55513
|
componentCount = 0;
|
|
@@ -55313,7 +55587,7 @@ async function printCodebaseStatus(opts) {
|
|
|
55313
55587
|
const manifestPath2 = path149.join(teamwikiDir, "source-manifest.json");
|
|
55314
55588
|
let manifest;
|
|
55315
55589
|
try {
|
|
55316
|
-
manifest = JSON.parse(await
|
|
55590
|
+
manifest = JSON.parse(await readFile17(manifestPath2, "utf-8"));
|
|
55317
55591
|
} catch {
|
|
55318
55592
|
if (opts.json) {
|
|
55319
55593
|
console.log(JSON.stringify({ error: "no-manifest", manifestPath: manifestPath2 }));
|
|
@@ -56914,7 +57188,7 @@ program.command("stats").description("Show local skill usage statistics").option
|
|
|
56914
57188
|
await showStats2({ byRepo: cmdOpts.byRepo, byTime: cmdOpts.byTime });
|
|
56915
57189
|
});
|
|
56916
57190
|
var sessionCmd = program.command("session").description("Record and inspect coding-session summaries");
|
|
56917
|
-
sessionCmd.command("save").description("Record a privacy-scrubbed summary of a coding session to a local monthly log").option("--session-id <id>", "Session to record (default:
|
|
57191
|
+
sessionCmd.command("save").description("Record a privacy-scrubbed summary of a coding session to a local monthly log").option("--session-id <id>", "Session to record (default: the agent's session, e.g. $CLAUDE_CODE_SESSION_ID, or the most recent)").option("--push", "Also push the summary to the team repo (feeds `teamai digest`)").option("--force", "Push even if the session is not flagged as valuable").option("--include-prompt", "Include the redacted first-prompt line in the pushed summary (default: off)").option("--scope <scope>", "Config scope for --push: user | project (default: auto-detect)").action(async (cmdOpts) => {
|
|
56918
57192
|
const globalOpts = program.opts();
|
|
56919
57193
|
const { saveSession: saveSession2 } = await Promise.resolve().then(() => (init_save_session(), save_session_exports));
|
|
56920
57194
|
await saveSession2({ ...globalOpts, ...cmdOpts });
|