@vibgrate/cli 2026.807.1 → 2026.814.1
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/NOTICE +7 -0
- package/dist/agent-2ZISYEQF.js +26 -0
- package/dist/{agent-K3J6EHLI.js.map → agent-2ZISYEQF.js.map} +1 -1
- package/dist/approvals-DOZ5RWJP.js +9 -0
- package/dist/approvals-DOZ5RWJP.js.map +1 -0
- package/dist/baseline-AGX42TZM.js +9 -0
- package/dist/{baseline-IAFWF2ET.js.map → baseline-AGX42TZM.js.map} +1 -1
- package/dist/{checkpoint-4WFZMKEO.js → checkpoint-UYMYMMQO.js} +6 -3
- package/dist/checkpoint-UYMYMMQO.js.map +1 -0
- package/dist/{chunk-N5EP5ZR2.js → chunk-2HNPL4OB.js} +5 -5
- package/dist/{chunk-N5EP5ZR2.js.map → chunk-2HNPL4OB.js.map} +1 -1
- package/dist/{chunk-2HAJCW7Z.js → chunk-2W3GBKSV.js} +5 -5
- package/dist/{chunk-2HAJCW7Z.js.map → chunk-2W3GBKSV.js.map} +1 -1
- package/dist/{chunk-XSHGXGVJ.js → chunk-5KY5UCDJ.js} +4 -4
- package/dist/{chunk-XSHGXGVJ.js.map → chunk-5KY5UCDJ.js.map} +1 -1
- package/dist/{chunk-NWX4VUFS.js → chunk-6OP5U55G.js} +8 -8
- package/dist/{chunk-NWX4VUFS.js.map → chunk-6OP5U55G.js.map} +1 -1
- package/dist/{chunk-KEIMMCS5.js → chunk-7CDW25VY.js} +4 -4
- package/dist/{chunk-KEIMMCS5.js.map → chunk-7CDW25VY.js.map} +1 -1
- package/dist/chunk-A4F7ESUR.js +174 -0
- package/dist/chunk-A4F7ESUR.js.map +1 -0
- package/dist/chunk-AJS4EL2C.js +266 -0
- package/dist/chunk-AJS4EL2C.js.map +1 -0
- package/dist/{chunk-5ESL76CM.js → chunk-BTYFZ3NM.js} +9 -8
- package/dist/chunk-BTYFZ3NM.js.map +1 -0
- package/dist/{chunk-JFCCJWXD.js → chunk-BXPWVTGS.js} +4 -3
- package/dist/chunk-BXPWVTGS.js.map +1 -0
- package/dist/{chunk-7CYFCSY2.js → chunk-EBUTRJHX.js} +24 -6
- package/dist/chunk-EBUTRJHX.js.map +1 -0
- package/dist/{chunk-EXOYSXH6.js → chunk-HYC3A2AT.js} +3 -3
- package/dist/{chunk-EXOYSXH6.js.map → chunk-HYC3A2AT.js.map} +1 -1
- package/dist/{chunk-G4KO5W6H.js → chunk-J3IDTLF4.js} +19 -9
- package/dist/chunk-J3IDTLF4.js.map +1 -0
- package/dist/chunk-JYY6G46J.js +64 -0
- package/dist/chunk-JYY6G46J.js.map +1 -0
- package/dist/{chunk-2M6B5V2U.js → chunk-K7MONYIM.js} +30 -66
- package/dist/chunk-K7MONYIM.js.map +1 -0
- package/dist/{chunk-ADP6ITGF.js → chunk-N3XFY3PX.js} +68 -11
- package/dist/chunk-N3XFY3PX.js.map +1 -0
- package/dist/{chunk-INIULQEP.js → chunk-N6ZMZ5AI.js} +11 -2
- package/dist/chunk-N6ZMZ5AI.js.map +1 -0
- package/dist/{chunk-QXIWQ2WG.js → chunk-NXDYWBZB.js} +4 -4
- package/dist/{chunk-QXIWQ2WG.js.map → chunk-NXDYWBZB.js.map} +1 -1
- package/dist/{chunk-GQ2EQXBG.js → chunk-QFCJY6FQ.js} +6 -5
- package/dist/chunk-QFCJY6FQ.js.map +1 -0
- package/dist/chunk-QIUQH2ND.js +6 -0
- package/dist/{chunk-ITY2NPIQ.js.map → chunk-QIUQH2ND.js.map} +1 -1
- package/dist/{chunk-FOKURZL3.js → chunk-RBBP4SP7.js} +12 -10
- package/dist/chunk-RBBP4SP7.js.map +1 -0
- package/dist/{chunk-VNAOFA6X.js → chunk-RNZP2XAL.js} +8 -8
- package/dist/chunk-RNZP2XAL.js.map +1 -0
- package/dist/{chunk-Y4JO6LYP.js → chunk-TFAXW7VM.js} +302 -11
- package/dist/chunk-TFAXW7VM.js.map +1 -0
- package/dist/{chunk-LS7GZBKP.js → chunk-UJAOCFQQ.js} +19 -9
- package/dist/chunk-UJAOCFQQ.js.map +1 -0
- package/dist/{chunk-PT2XQTSK.js → chunk-VD6TVFGA.js} +7 -6
- package/dist/chunk-VD6TVFGA.js.map +1 -0
- package/dist/{chunk-VQVLY63G.js → chunk-WN2AAB5O.js} +212 -59
- package/dist/chunk-WN2AAB5O.js.map +1 -0
- package/dist/{chunk-EJUMV5HU.js → chunk-XEI7UU6G.js} +3 -3
- package/dist/{chunk-EJUMV5HU.js.map → chunk-XEI7UU6G.js.map} +1 -1
- package/dist/cli.js +1179 -511
- package/dist/cli.js.map +1 -1
- package/dist/embed-worker-main.js +4 -3
- package/dist/embed-worker-main.js.map +1 -1
- package/dist/{ensure-map-TYMYYIJE.js → ensure-map-EURJPSAK.js} +15 -14
- package/dist/{ensure-map-TYMYYIJE.js.map → ensure-map-EURJPSAK.js.map} +1 -1
- package/dist/fastembed-S6YEJYQV.js +292 -0
- package/dist/fastembed-S6YEJYQV.js.map +1 -0
- package/dist/graph-backend-VUW2QWPI.js +14 -0
- package/dist/{graph-backend-5YOWNHHG.js.map → graph-backend-VUW2QWPI.js.map} +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +14 -13
- package/dist/index.js.map +1 -1
- package/dist/{interactive-YEMXHGBO.js → interactive-WRA477OW.js} +32 -30
- package/dist/interactive-WRA477OW.js.map +1 -0
- package/dist/load-XZV5LEGA.js +10 -0
- package/dist/{load-IIWVYUN7.js.map → load-XZV5LEGA.js.map} +1 -1
- package/dist/{run-outcome-2JO55QCC.js → run-outcome-X5KS6AUE.js} +6 -5
- package/dist/run-outcome-X5KS6AUE.js.map +1 -0
- package/dist/runtime-session-J7GDVRFU.js +21 -0
- package/dist/{runtime-session-HFKGKQV7.js.map → runtime-session-J7GDVRFU.js.map} +1 -1
- package/dist/session-3R34WA5S.js +16 -0
- package/dist/{session-5LFX4U2E.js.map → session-3R34WA5S.js.map} +1 -1
- package/dist/session-store-AQUORKWB.js +4 -0
- package/dist/{session-store-KGONLWHN.js.map → session-store-AQUORKWB.js.map} +1 -1
- package/dist/{stream-json-DUCFTBUF.js → stream-json-LKVPDKXP.js} +76 -24
- package/dist/stream-json-LKVPDKXP.js.map +1 -0
- package/dist/version-55TOTJTF.js +4 -0
- package/dist/version-55TOTJTF.js.map +1 -0
- package/dist/{vg-mcp-bridge-4KPDQUFN.js → vg-mcp-bridge-ZPJAHP7T.js} +11 -10
- package/dist/vg-mcp-bridge-ZPJAHP7T.js.map +1 -0
- package/dist/{vgd-LFHQ4GWC.js → vgd-UIFH5TTW.js} +12 -11
- package/dist/vgd-UIFH5TTW.js.map +1 -0
- package/dist/worktree-session-D575IQWM.js +82 -0
- package/dist/worktree-session-D575IQWM.js.map +1 -0
- package/package.json +9 -7
- package/dist/agent-K3J6EHLI.js +0 -24
- package/dist/baseline-IAFWF2ET.js +0 -9
- package/dist/checkpoint-4WFZMKEO.js.map +0 -1
- package/dist/chunk-2M6B5V2U.js.map +0 -1
- package/dist/chunk-5ESL76CM.js.map +0 -1
- package/dist/chunk-7CYFCSY2.js.map +0 -1
- package/dist/chunk-ADP6ITGF.js.map +0 -1
- package/dist/chunk-FOKURZL3.js.map +0 -1
- package/dist/chunk-G4KO5W6H.js.map +0 -1
- package/dist/chunk-GQ2EQXBG.js.map +0 -1
- package/dist/chunk-INIULQEP.js.map +0 -1
- package/dist/chunk-ITY2NPIQ.js +0 -6
- package/dist/chunk-JFCCJWXD.js.map +0 -1
- package/dist/chunk-LS7GZBKP.js.map +0 -1
- package/dist/chunk-PT2XQTSK.js.map +0 -1
- package/dist/chunk-VNAOFA6X.js.map +0 -1
- package/dist/chunk-VQVLY63G.js.map +0 -1
- package/dist/chunk-Y4JO6LYP.js.map +0 -1
- package/dist/chunk-Z7FM3ETS.js +0 -126
- package/dist/chunk-Z7FM3ETS.js.map +0 -1
- package/dist/graph-backend-5YOWNHHG.js +0 -13
- package/dist/interactive-YEMXHGBO.js.map +0 -1
- package/dist/load-IIWVYUN7.js +0 -9
- package/dist/run-outcome-2JO55QCC.js.map +0 -1
- package/dist/runtime-session-HFKGKQV7.js +0 -20
- package/dist/session-5LFX4U2E.js +0 -15
- package/dist/session-store-KGONLWHN.js +0 -4
- package/dist/stream-json-DUCFTBUF.js.map +0 -1
- package/dist/vg-mcp-bridge-4KPDQUFN.js.map +0 -1
- package/dist/vgd-LFHQ4GWC.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/code/ensure-map.ts"],"names":[],"mappings":";;;;AA4BA,eAAsB,aAAA,CACpB,IAAA,EACA,MAAA,EACA,KAAA,EAC4B;AAC5B,EAAA,IAAI,SAAA,CAAU,IAAA,EAAM,MAAA,CAAO,KAAK,GAAG,OAAO,OAAA;AAE1C,EAAA,KAAA,EAAO,UAAA,GAAa;AAAA,IAClB,KAAA,EAAO,OAAA;AAAA,IACP,OAAA,EAAS,+DAAA;AAAA,IACT,GAAA,EAAK;AAAA,GACN,CAAA;AAED,EAAA,IAAI;AACF,IAAA,MAAM,QAAA;AAAA,MACJ,CAAC,IAAI,CAAA;AAAA,MACL,EAAE,IAAA,EAAM,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAM;AAAA,MAC7B,EAAE,GAAG,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAK;AAAA,MACzB;AAAA,QACE,eAAA,EAAiB,CAAC,IAAA,EAAM,KAAA,KAAU;AAChC,UAAA,MAAM,GAAA,GAAM,KAAA,GAAQ,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,IAAA,CAAK,KAAA,CAAO,IAAA,GAAO,KAAA,GAAS,GAAG,CAAC,CAAA,GAAI,KAAA,CAAA;AACzE,UAAA,KAAA,EAAO,UAAA,GAAa;AAAA,YAClB,KAAA,EAAO,UAAA;AAAA,YACP,OAAA,EACE,KAAA,GAAQ,CAAA,GACJ,CAAA,oBAAA,EAAkB,IAAA,CAAK,cAAA,EAAgB,CAAA,CAAA,EAAI,KAAA,CAAM,cAAA,EAAgB,CAAA,CAAA,GACjE,qBAAA;AAAA,YACN;AAAA,WACD,CAAA;AAAA,QACH;AAAA;AACF,KACF;AACA,IAAA,KAAA,EAAO,UAAA,GAAa,EAAE,KAAA,EAAO,MAAA,EAAQ,SAAS,gDAAA,EAAwC,GAAA,EAAK,KAAK,CAAA;AAChG,IAAA,OAAO,OAAA;AAAA,EACT,SAAS,CAAA,EAAG;AACV,IAAA,MAAM,UAAU,CAAA,YAAa,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,OAAO,CAAC,CAAA;AACzD,IAAA,KAAA,EAAO,UAAA,GAAa;AAAA,MAClB,KAAA,EAAO,OAAA;AAAA,MACP,OAAA,EAAS,iCAAiC,OAAO,CAAA;AAAA,KAClD,CAAA;AACD,IAAA,MAAM,CAAA;AAAA,EACR;AACF","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/code/ensure-map.ts"],"names":[],"mappings":";;;;AA4BA,eAAsB,aAAA,CACpB,IAAA,EACA,MAAA,EACA,KAAA,EAC4B;AAC5B,EAAA,IAAI,SAAA,CAAU,IAAA,EAAM,MAAA,CAAO,KAAK,GAAG,OAAO,OAAA;AAE1C,EAAA,KAAA,EAAO,UAAA,GAAa;AAAA,IAClB,KAAA,EAAO,OAAA;AAAA,IACP,OAAA,EAAS,+DAAA;AAAA,IACT,GAAA,EAAK;AAAA,GACN,CAAA;AAED,EAAA,IAAI;AACF,IAAA,MAAM,QAAA;AAAA,MACJ,CAAC,IAAI,CAAA;AAAA,MACL,EAAE,IAAA,EAAM,KAAA,EAAO,MAAA,EAAQ,KAAA,EAAM;AAAA,MAC7B,EAAE,GAAG,MAAA,EAAQ,KAAA,EAAO,IAAA,EAAK;AAAA,MACzB;AAAA,QACE,eAAA,EAAiB,CAAC,IAAA,EAAM,KAAA,KAAU;AAChC,UAAA,MAAM,GAAA,GAAM,KAAA,GAAQ,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,IAAA,CAAK,KAAA,CAAO,IAAA,GAAO,KAAA,GAAS,GAAG,CAAC,CAAA,GAAI,KAAA,CAAA;AACzE,UAAA,KAAA,EAAO,UAAA,GAAa;AAAA,YAClB,KAAA,EAAO,UAAA;AAAA,YACP,OAAA,EACE,KAAA,GAAQ,CAAA,GACJ,CAAA,oBAAA,EAAkB,IAAA,CAAK,cAAA,EAAgB,CAAA,CAAA,EAAI,KAAA,CAAM,cAAA,EAAgB,CAAA,CAAA,GACjE,qBAAA;AAAA,YACN;AAAA,WACD,CAAA;AAAA,QACH;AAAA;AACF,KACF;AACA,IAAA,KAAA,EAAO,UAAA,GAAa,EAAE,KAAA,EAAO,MAAA,EAAQ,SAAS,gDAAA,EAAwC,GAAA,EAAK,KAAK,CAAA;AAChG,IAAA,OAAO,OAAA;AAAA,EACT,SAAS,CAAA,EAAG;AACV,IAAA,MAAM,UAAU,CAAA,YAAa,KAAA,GAAQ,CAAA,CAAE,OAAA,GAAU,OAAO,CAAC,CAAA;AACzD,IAAA,KAAA,EAAO,UAAA,GAAa;AAAA,MAClB,KAAA,EAAO,OAAA;AAAA,MACP,OAAA,EAAS,iCAAiC,OAAO,CAAA;AAAA,KAClD,CAAA;AACD,IAAA,MAAM,CAAA;AAAA,EACR;AACF","file":"chunk-7CDW25VY.js","sourcesContent":["/**\n * Ensure a code map exists before VG Code (or any host) starts work.\n *\n * Interactive CLI, `vg code --stream-json`, and VS Code all share this path so\n * the first turn never fails with \"no map found\" — we build once (with progress\n * callbacks for host UIs) and then proceed.\n */\n\nimport { loadGraph } from '../engine/load.js';\nimport { runBuild } from '../commands/build.js';\nimport type { GlobalOpts } from '../cli-options.js';\n\nexport type EnsureMapPhase = 'start' | 'progress' | 'done' | 'error';\n\nexport interface EnsureMapProgress {\n phase: EnsureMapPhase;\n message: string;\n /** 0–100 when known (parse phase); omitted when indeterminate. */\n pct?: number;\n}\n\nexport interface EnsureMapHooks {\n onProgress?: (p: EnsureMapProgress) => void;\n}\n\n/**\n * @returns `ready` when a map already existed; `built` when this call built one.\n */\nexport async function ensureCodeMap(\n root: string,\n global: GlobalOpts,\n hooks?: EnsureMapHooks,\n): Promise<'ready' | 'built'> {\n if (loadGraph(root, global.graph)) return 'ready';\n\n hooks?.onProgress?.({\n phase: 'start',\n message: 'Building the code map for this project… (first run only)',\n pct: 0,\n });\n\n try {\n await runBuild(\n [root],\n { html: false, report: false },\n { ...global, quiet: true },\n {\n onParseProgress: (done, total) => {\n const pct = total > 0 ? Math.min(99, Math.round((done / total) * 100)) : undefined;\n hooks?.onProgress?.({\n phase: 'progress',\n message:\n total > 0\n ? `Parsing files… ${done.toLocaleString()}/${total.toLocaleString()}`\n : 'Parsing files…',\n pct,\n });\n },\n },\n );\n hooks?.onProgress?.({ phase: 'done', message: 'Code map ready — starting your task…', pct: 100 });\n return 'built';\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n hooks?.onProgress?.({\n phase: 'error',\n message: `Could not build the code map: ${message}`,\n });\n throw e;\n }\n}\n"]}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { ensureVibgrateGitignore } from './chunk-K7MONYIM.js';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
function approvalsPath(root) {
|
|
6
|
+
return path.join(root, ".vibgrate", "code-approvals.json");
|
|
7
|
+
}
|
|
8
|
+
function ruleKey(rule) {
|
|
9
|
+
if (rule.kind === "run") return `run:${rule.prefix}`;
|
|
10
|
+
if (rule.kind === "tool") return `tool:${rule.tool}`;
|
|
11
|
+
return `${rule.kind}:${rule.glob}`;
|
|
12
|
+
}
|
|
13
|
+
function ruleLabel(rule) {
|
|
14
|
+
if (rule.kind === "run") return `run \`${rule.prefix} \u2026\``;
|
|
15
|
+
if (rule.kind === "tool") return `call ${rule.tool}`;
|
|
16
|
+
return `${rule.kind} ${rule.glob}`;
|
|
17
|
+
}
|
|
18
|
+
function loadApprovalRules(root) {
|
|
19
|
+
let raw;
|
|
20
|
+
try {
|
|
21
|
+
raw = JSON.parse(fs.readFileSync(approvalsPath(root), "utf8"));
|
|
22
|
+
} catch {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
const list = Array.isArray(raw) ? raw : raw && typeof raw === "object" && Array.isArray(raw.rules) ? raw.rules : [];
|
|
26
|
+
const out = [];
|
|
27
|
+
for (const item of list) {
|
|
28
|
+
const rule = sanitizeRule(item);
|
|
29
|
+
if (rule) out.push(rule);
|
|
30
|
+
}
|
|
31
|
+
return dedupe(out);
|
|
32
|
+
}
|
|
33
|
+
function sanitizeRule(item) {
|
|
34
|
+
if (!item || typeof item !== "object") return null;
|
|
35
|
+
const o = item;
|
|
36
|
+
if (o.kind === "run" && typeof o.prefix === "string" && o.prefix.trim()) {
|
|
37
|
+
return { kind: "run", prefix: normalizeCommandPrefix(o.prefix) };
|
|
38
|
+
}
|
|
39
|
+
if (o.kind === "tool" && typeof o.tool === "string" && o.tool.trim()) {
|
|
40
|
+
return { kind: "tool", tool: o.tool.trim() };
|
|
41
|
+
}
|
|
42
|
+
if ((o.kind === "edit" || o.kind === "create" || o.kind === "delete") && typeof o.glob === "string" && o.glob.trim()) {
|
|
43
|
+
return { kind: o.kind, glob: o.glob.trim() };
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
function dedupe(rules) {
|
|
48
|
+
const seen = /* @__PURE__ */ new Set();
|
|
49
|
+
const out = [];
|
|
50
|
+
for (const r of rules) {
|
|
51
|
+
const key = ruleKey(r);
|
|
52
|
+
if (seen.has(key)) continue;
|
|
53
|
+
seen.add(key);
|
|
54
|
+
out.push(r);
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
function saveApprovalRules(root, rules) {
|
|
59
|
+
try {
|
|
60
|
+
const dir = path.join(root, ".vibgrate");
|
|
61
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
62
|
+
ensureVibgrateGitignore(root);
|
|
63
|
+
const sorted = dedupe(rules).slice().sort((a, b) => ruleKey(a).localeCompare(ruleKey(b)));
|
|
64
|
+
fs.writeFileSync(approvalsPath(root), `${JSON.stringify({ rules: sorted }, null, 2)}
|
|
65
|
+
`);
|
|
66
|
+
return true;
|
|
67
|
+
} catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function addApprovalRule(root, rule) {
|
|
72
|
+
const rules = loadApprovalRules(root);
|
|
73
|
+
if (rules.some((r) => ruleKey(r) === ruleKey(rule))) return rule;
|
|
74
|
+
rules.push(rule);
|
|
75
|
+
return saveApprovalRules(root, rules) ? rule : null;
|
|
76
|
+
}
|
|
77
|
+
function removeApprovalRule(root, key) {
|
|
78
|
+
const rules = loadApprovalRules(root);
|
|
79
|
+
const kept = rules.filter((r) => ruleKey(r) !== key);
|
|
80
|
+
if (kept.length === rules.length) return false;
|
|
81
|
+
return saveApprovalRules(root, kept);
|
|
82
|
+
}
|
|
83
|
+
function normalizeCommandPrefix(command) {
|
|
84
|
+
const tokens = command.trim().split(/\s+/).filter(Boolean);
|
|
85
|
+
const head = tokens[0] ?? "";
|
|
86
|
+
const second = tokens[1];
|
|
87
|
+
if (!second) return head;
|
|
88
|
+
if (second.startsWith("-")) return tokens.join(" ");
|
|
89
|
+
if (tokens.slice(2).some((t) => !t.startsWith("-"))) return tokens.join(" ");
|
|
90
|
+
return `${head} ${second}`;
|
|
91
|
+
}
|
|
92
|
+
function extensiblePrefix(prefix) {
|
|
93
|
+
return !prefix.split(" ").some((t) => t.startsWith("-"));
|
|
94
|
+
}
|
|
95
|
+
var SHELL_METACHARS = /[><;&|`\n(){}$]/;
|
|
96
|
+
function ruleForAction(action) {
|
|
97
|
+
switch (action.kind) {
|
|
98
|
+
case "run":
|
|
99
|
+
return { kind: "run", prefix: normalizeCommandPrefix(action.command) };
|
|
100
|
+
case "tool":
|
|
101
|
+
return { kind: "tool", tool: action.name };
|
|
102
|
+
case "edit":
|
|
103
|
+
case "create":
|
|
104
|
+
case "delete":
|
|
105
|
+
return { kind: action.kind, glob: action.file };
|
|
106
|
+
case "patch":
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function globMatch(glob, file) {
|
|
111
|
+
let re = "";
|
|
112
|
+
for (let i = 0; i < glob.length; i++) {
|
|
113
|
+
const c = glob[i];
|
|
114
|
+
if (c === "*") {
|
|
115
|
+
if (glob[i + 1] === "*") {
|
|
116
|
+
re += ".*";
|
|
117
|
+
i += glob[i + 2] === "/" ? 2 : 1;
|
|
118
|
+
} else {
|
|
119
|
+
re += "[^/]*";
|
|
120
|
+
}
|
|
121
|
+
} else if (c === "?") {
|
|
122
|
+
re += "[^/]";
|
|
123
|
+
} else if ("\\^$.|+()[]{}".includes(c)) {
|
|
124
|
+
re += `\\${c}`;
|
|
125
|
+
} else {
|
|
126
|
+
re += c;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
return new RegExp(`^${re}$`).test(file);
|
|
131
|
+
} catch {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function fileRuleMatches(rules, op, file) {
|
|
136
|
+
for (const r of rules) {
|
|
137
|
+
if (r.kind === op && globMatch(r.glob, file)) return r;
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
function findMatchingRule(action, rules) {
|
|
142
|
+
if (!rules.length) return null;
|
|
143
|
+
switch (action.kind) {
|
|
144
|
+
case "run": {
|
|
145
|
+
const full = action.command.trim().split(/\s+/).filter(Boolean).join(" ");
|
|
146
|
+
for (const r of rules) {
|
|
147
|
+
if (r.kind !== "run") continue;
|
|
148
|
+
if (full === r.prefix) return r;
|
|
149
|
+
if (extensiblePrefix(r.prefix) && full.startsWith(`${r.prefix} `)) {
|
|
150
|
+
const tail = full.slice(r.prefix.length + 1);
|
|
151
|
+
if (SHELL_METACHARS.test(tail)) continue;
|
|
152
|
+
const extension = tail.split(" ");
|
|
153
|
+
if (extension.every((t) => t.startsWith("-"))) return r;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
case "tool":
|
|
159
|
+
return rules.find((r) => r.kind === "tool" && r.tool === action.name) ?? null;
|
|
160
|
+
case "edit":
|
|
161
|
+
case "create":
|
|
162
|
+
case "delete":
|
|
163
|
+
return fileRuleMatches(rules, action.kind, action.file);
|
|
164
|
+
case "patch": {
|
|
165
|
+
const covered = action.files.every((f) => fileRuleMatches(rules, f.op, f.file));
|
|
166
|
+
if (!covered) return null;
|
|
167
|
+
return fileRuleMatches(rules, action.files[0].op, action.files[0].file);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export { addApprovalRule, approvalsPath, findMatchingRule, globMatch, loadApprovalRules, normalizeCommandPrefix, removeApprovalRule, ruleForAction, ruleKey, ruleLabel, saveApprovalRules };
|
|
173
|
+
//# sourceMappingURL=chunk-A4F7ESUR.js.map
|
|
174
|
+
//# sourceMappingURL=chunk-A4F7ESUR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/code/approvals.ts"],"names":[],"mappings":"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","file":"chunk-A4F7ESUR.js","sourcesContent":["/**\n * Allow-always approval rules (P2 — permission parity).\n *\n * The approval gate exists to review state changes, but reviewing the *same*\n * change shape every time (`pnpm test`, edits to a scratch dir) is friction\n * without safety. A user can promote an approval to a standing rule\n * (\"Always allow\"); rules live in `.vibgrate/code-approvals.json` at the repo\n * root so the CLI and VS Code honour the same set, and are re-read on every\n * approval so a revocation (from the VG Code manage UI or by editing the\n * file) takes effect immediately — no session restart, no stale cache.\n *\n * Rules never widen safety: {@link dangerousCommand} still screens every rule\n * match, plan mode still denies everything engine-side before rules are\n * consulted, and a rule-approved action is still announced (notice event) and\n * checkpointed like any human-approved change.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { ensureVibgrateGitignore } from '../engine/artifacts.js';\nimport type { MutatingAction, PatchFileAction } from './tools.js';\n\n/** One standing approval. Discriminated by `kind`, mirroring MutatingAction. */\nexport type ApprovalRule =\n /** Matches `run` actions whose normalized command starts with these tokens. */\n | { kind: 'run'; prefix: string }\n /** Matches a single-file action of that op when the path matches the glob. */\n | { kind: 'edit' | 'create' | 'delete'; glob: string }\n /** Matches external `tool` calls by tool name. */\n | { kind: 'tool'; tool: string };\n\nexport function approvalsPath(root: string): string {\n return path.join(root, '.vibgrate', 'code-approvals.json');\n}\n\n/**\n * The stable identity of a rule — used for dedupe and for revocation from\n * hosts that only carry the display string.\n */\nexport function ruleKey(rule: ApprovalRule): string {\n if (rule.kind === 'run') return `run:${rule.prefix}`;\n if (rule.kind === 'tool') return `tool:${rule.tool}`;\n return `${rule.kind}:${rule.glob}`;\n}\n\n/** Human-readable label for notices and manage UIs. */\nexport function ruleLabel(rule: ApprovalRule): string {\n if (rule.kind === 'run') return `run \\`${rule.prefix} …\\``;\n if (rule.kind === 'tool') return `call ${rule.tool}`;\n return `${rule.kind} ${rule.glob}`;\n}\n\n/**\n * Load the rule set, tolerating absence and malformed JSON (an unreadable file\n * means \"no rules\", never a crash — same posture as `.vibgrate/code.json`).\n * Unknown shapes are dropped so an untrusted file cannot inject junk.\n */\nexport function loadApprovalRules(root: string): ApprovalRule[] {\n let raw: unknown;\n try {\n raw = JSON.parse(fs.readFileSync(approvalsPath(root), 'utf8'));\n } catch {\n return [];\n }\n const list = Array.isArray(raw)\n ? raw\n : raw && typeof raw === 'object' && Array.isArray((raw as { rules?: unknown }).rules)\n ? ((raw as { rules: unknown[] }).rules)\n : [];\n const out: ApprovalRule[] = [];\n for (const item of list) {\n const rule = sanitizeRule(item);\n if (rule) out.push(rule);\n }\n return dedupe(out);\n}\n\nfunction sanitizeRule(item: unknown): ApprovalRule | null {\n if (!item || typeof item !== 'object') return null;\n const o = item as Record<string, unknown>;\n if (o.kind === 'run' && typeof o.prefix === 'string' && o.prefix.trim()) {\n return { kind: 'run', prefix: normalizeCommandPrefix(o.prefix) };\n }\n if (o.kind === 'tool' && typeof o.tool === 'string' && o.tool.trim()) {\n return { kind: 'tool', tool: o.tool.trim() };\n }\n if ((o.kind === 'edit' || o.kind === 'create' || o.kind === 'delete') && typeof o.glob === 'string' && o.glob.trim()) {\n return { kind: o.kind, glob: o.glob.trim() };\n }\n return null;\n}\n\nfunction dedupe(rules: ApprovalRule[]): ApprovalRule[] {\n const seen = new Set<string>();\n const out: ApprovalRule[] = [];\n for (const r of rules) {\n const key = ruleKey(r);\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(r);\n }\n return out;\n}\n\n/**\n * Persist the rule set, deterministically sorted so identical sets produce an\n * identical file. Best-effort like other `.vibgrate/` writers — a read-only\n * checkout means no standing rules, never a thrown error mid-approval.\n */\nexport function saveApprovalRules(root: string, rules: ApprovalRule[]): boolean {\n try {\n const dir = path.join(root, '.vibgrate');\n fs.mkdirSync(dir, { recursive: true });\n // First writer into .vibgrate/ creates its .gitignore (create-once): one\n // developer's standing consents must not ride into the team's repo.\n ensureVibgrateGitignore(root);\n const sorted = dedupe(rules).slice().sort((a, b) => ruleKey(a).localeCompare(ruleKey(b)));\n fs.writeFileSync(approvalsPath(root), `${JSON.stringify({ rules: sorted }, null, 2)}\\n`);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Add one rule (deduped). Returns the saved rule, or null when nothing was written. */\nexport function addApprovalRule(root: string, rule: ApprovalRule): ApprovalRule | null {\n const rules = loadApprovalRules(root);\n if (rules.some((r) => ruleKey(r) === ruleKey(rule))) return rule;\n rules.push(rule);\n return saveApprovalRules(root, rules) ? rule : null;\n}\n\n/** Remove the rule with this {@link ruleKey}. Returns true when one was removed and saved. */\nexport function removeApprovalRule(root: string, key: string): boolean {\n const rules = loadApprovalRules(root);\n const kept = rules.filter((r) => ruleKey(r) !== key);\n if (kept.length === rules.length) return false;\n return saveApprovalRules(root, kept);\n}\n\n/**\n * Normalize a shell command to its rule prefix: the first two tokens for the\n * `cmd subcommand [flags…]` shape (`git status`, `pnpm test --watch`), the\n * single token for a one-word command, and the *whole* command\n * (whitespace-normalized) in every other case. Collapsing `rm -f temp.log` to\n * bare `rm` — or `rm foo.log secrets.env` to `rm foo.log` — would turn one\n * blessed invocation into a standing grant covering paths the user never saw\n * (2026-08 audit, D2), so a command with a flag second-token or positional\n * arguments beyond the subcommand keeps its exact form.\n */\nexport function normalizeCommandPrefix(command: string): string {\n const tokens = command.trim().split(/\\s+/).filter(Boolean);\n const head = tokens[0] ?? '';\n const second = tokens[1];\n if (!second) return head;\n if (second.startsWith('-')) return tokens.join(' ');\n // Extra positionals after `cmd subcommand` make the rule exact; trailing\n // flags still collapse to the extensible two-token prefix.\n if (tokens.slice(2).some((t) => !t.startsWith('-'))) return tokens.join(' ');\n return `${head} ${second}`;\n}\n\n/** True when a rule prefix is the extensible `cmd [subcommand]` shape (no flags). */\nfunction extensiblePrefix(prefix: string): boolean {\n return !prefix.split(' ').some((t) => t.startsWith('-'));\n}\n\n/**\n * Shell constructs that can smuggle a second command into what looks like a\n * flag: command substitution, redirects, sequencing, backgrounding, subshells.\n * A `pnpm test` rule must not bless `pnpm test --reporter=$(curl … | sh)` —\n * the appended token is flag-shaped, but its *value* is another program the\n * user never saw. Same screen {@link readOnlyCommand} applies; kept local so\n * safety and approvals stay independent modules.\n */\nconst SHELL_METACHARS = /[><;&|`\\n(){}$]/;\n\n/**\n * Derive the standing rule an \"Always allow\" on this action should create.\n * Patch actions return null — an atomic multi-file transaction has no single\n * honest generalisation, so hosts do not offer Always on patch cards.\n */\nexport function ruleForAction(action: MutatingAction): ApprovalRule | null {\n switch (action.kind) {\n case 'run':\n return { kind: 'run', prefix: normalizeCommandPrefix(action.command) };\n case 'tool':\n return { kind: 'tool', tool: action.name };\n case 'edit':\n case 'create':\n case 'delete':\n return { kind: action.kind, glob: action.file };\n case 'patch':\n return null;\n }\n}\n\n/**\n * Minimal glob match for rule paths: `*` matches within a segment, `**`\n * across segments, `?` one character. Deterministic, no dependency, and\n * conservative — a malformed pattern simply never matches.\n */\nexport function globMatch(glob: string, file: string): boolean {\n let re = '';\n for (let i = 0; i < glob.length; i++) {\n const c = glob[i];\n if (c === '*') {\n if (glob[i + 1] === '*') {\n // `**/` or trailing `**` — match across segments.\n re += '.*';\n i += glob[i + 2] === '/' ? 2 : 1;\n } else {\n re += '[^/]*';\n }\n } else if (c === '?') {\n re += '[^/]';\n } else if ('\\\\^$.|+()[]{}'.includes(c)) {\n re += `\\\\${c}`;\n } else {\n re += c;\n }\n }\n try {\n return new RegExp(`^${re}$`).test(file);\n } catch {\n return false;\n }\n}\n\nfunction fileRuleMatches(rules: ApprovalRule[], op: 'edit' | 'create' | 'delete', file: string): ApprovalRule | null {\n for (const r of rules) {\n if (r.kind === op && globMatch(r.glob, file)) return r;\n }\n return null;\n}\n\n/**\n * The first rule matching this action, or null. A `patch` matches only when\n * *every* file in the transaction is covered by a matching file rule — an\n * atomic approval must never be half-earned.\n */\nexport function findMatchingRule(action: MutatingAction, rules: ApprovalRule[]): ApprovalRule | null {\n if (!rules.length) return null;\n switch (action.kind) {\n case 'run': {\n // Match the FULL whitespace-normalized command, never a truncated form:\n // collapsing `rm foo.log secrets.env` to its first two tokens before\n // comparing would silently discard the appended argument and let a rule\n // saved for `rm foo.log` bless it (2026-08 audit, D2).\n const full = action.command.trim().split(/\\s+/).filter(Boolean).join(' ');\n for (const r of rules) {\n if (r.kind !== 'run') continue;\n if (full === r.prefix) return r;\n // A `cmd [subcommand]` prefix extends to longer commands only when\n // every appended token is a flag (`git status --short`). Appended\n // positionals (extra paths, script arguments) are new state the user\n // never blessed — they re-prompt, and can be Always-allowed exactly.\n if (extensiblePrefix(r.prefix) && full.startsWith(`${r.prefix} `)) {\n const tail = full.slice(r.prefix.length + 1);\n // A flag whose value is a subshell is not an extension of the blessed\n // command, it is a different one — screen the appended text before\n // the flag-shape check, never after.\n if (SHELL_METACHARS.test(tail)) continue;\n const extension = tail.split(' ');\n if (extension.every((t) => t.startsWith('-'))) return r;\n }\n }\n return null;\n }\n case 'tool':\n return rules.find((r) => r.kind === 'tool' && r.tool === action.name) ?? null;\n case 'edit':\n case 'create':\n case 'delete':\n return fileRuleMatches(rules, action.kind, action.file);\n case 'patch': {\n const covered = action.files.every((f: PatchFileAction) => fileRuleMatches(rules, f.op, f.file));\n if (!covered) return null;\n return fileRuleMatches(rules, action.files[0]!.op, action.files[0]!.file);\n }\n }\n}\n"]}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
// src/code/session-store.ts
|
|
5
|
+
function sessionsDir(root) {
|
|
6
|
+
return path.join(root, ".vibgrate", "code-sessions");
|
|
7
|
+
}
|
|
8
|
+
function latestPointer(root) {
|
|
9
|
+
return path.join(sessionsDir(root), "latest.json");
|
|
10
|
+
}
|
|
11
|
+
function indexFile(root) {
|
|
12
|
+
return path.join(sessionsDir(root), "index.json");
|
|
13
|
+
}
|
|
14
|
+
function sessionTitle(session) {
|
|
15
|
+
const explicit = session.title?.trim();
|
|
16
|
+
if (explicit) return explicit.slice(0, 80);
|
|
17
|
+
return (session.tasks?.[0]?.instruction?.trim() || "New chat").slice(0, 80);
|
|
18
|
+
}
|
|
19
|
+
function toIndexEntry(session) {
|
|
20
|
+
return {
|
|
21
|
+
id: session.id,
|
|
22
|
+
provider: session.provider ?? "",
|
|
23
|
+
model: session.model ?? "",
|
|
24
|
+
startedAt: session.startedAt ?? 0,
|
|
25
|
+
updatedAt: session.updatedAt ?? session.startedAt ?? 0,
|
|
26
|
+
taskCount: session.tasks.length,
|
|
27
|
+
title: sessionTitle(session),
|
|
28
|
+
...session.forkedFrom ? { forkedFrom: session.forkedFrom } : {},
|
|
29
|
+
...session.worktree ? { worktreeId: session.worktree.id } : {}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function readSessionIndex(root) {
|
|
33
|
+
try {
|
|
34
|
+
const raw = JSON.parse(fs.readFileSync(indexFile(root), "utf8"));
|
|
35
|
+
if (!Array.isArray(raw?.sessions)) return null;
|
|
36
|
+
return raw.sessions.filter((s) => s && typeof s.id === "string" && typeof s.title === "string");
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function writeSessionIndex(root, entries) {
|
|
42
|
+
try {
|
|
43
|
+
fs.mkdirSync(sessionsDir(root), { recursive: true });
|
|
44
|
+
fs.writeFileSync(indexFile(root), JSON.stringify({ sessions: entries }, null, 2));
|
|
45
|
+
} catch {
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function updateSessionIndex(root, session) {
|
|
49
|
+
const entries = readSessionIndex(root) ?? rebuildSessionIndex(root);
|
|
50
|
+
const next = entries.filter((e) => e.id !== session.id);
|
|
51
|
+
next.push(toIndexEntry(session));
|
|
52
|
+
next.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
53
|
+
writeSessionIndex(root, next);
|
|
54
|
+
}
|
|
55
|
+
function rebuildSessionIndex(root) {
|
|
56
|
+
const entries = [];
|
|
57
|
+
try {
|
|
58
|
+
const dir = sessionsDir(root);
|
|
59
|
+
if (!fs.existsSync(dir)) return entries;
|
|
60
|
+
for (const f of fs.readdirSync(dir)) {
|
|
61
|
+
if (!f.endsWith(".json") || f === "latest.json" || f === "index.json") continue;
|
|
62
|
+
try {
|
|
63
|
+
const raw = JSON.parse(fs.readFileSync(path.join(dir, f), "utf8"));
|
|
64
|
+
if (raw?.id && Array.isArray(raw.tasks)) entries.push(toIndexEntry(raw));
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
}
|
|
70
|
+
entries.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
71
|
+
return entries;
|
|
72
|
+
}
|
|
73
|
+
function saveSession(root, session) {
|
|
74
|
+
try {
|
|
75
|
+
const dir = sessionsDir(root);
|
|
76
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
77
|
+
fs.writeFileSync(path.join(dir, `${session.id}.json`), JSON.stringify(session, null, 2));
|
|
78
|
+
fs.writeFileSync(latestPointer(root), JSON.stringify({ id: session.id }));
|
|
79
|
+
updateSessionIndex(root, session);
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function renameSession(root, id, title) {
|
|
84
|
+
const s = loadSession(root, id);
|
|
85
|
+
if (!s) return false;
|
|
86
|
+
const t = title.trim().slice(0, 120);
|
|
87
|
+
const next = { ...s };
|
|
88
|
+
if (t) next.title = t;
|
|
89
|
+
else delete next.title;
|
|
90
|
+
try {
|
|
91
|
+
fs.writeFileSync(path.join(sessionsDir(root), `${next.id}.json`), JSON.stringify(next, null, 2));
|
|
92
|
+
updateSessionIndex(root, next);
|
|
93
|
+
return true;
|
|
94
|
+
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function deleteSession(root, id) {
|
|
99
|
+
const safe = id.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
100
|
+
if (!safe || safe !== id) return false;
|
|
101
|
+
const file = path.join(sessionsDir(root), `${safe}.json`);
|
|
102
|
+
if (!fs.existsSync(file)) return false;
|
|
103
|
+
try {
|
|
104
|
+
fs.unlinkSync(file);
|
|
105
|
+
} catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
const entries = (readSessionIndex(root) ?? rebuildSessionIndex(root)).filter((e) => e.id !== safe);
|
|
109
|
+
writeSessionIndex(root, entries);
|
|
110
|
+
try {
|
|
111
|
+
const { id: latest } = JSON.parse(fs.readFileSync(latestPointer(root), "utf8"));
|
|
112
|
+
if (latest === safe) fs.unlinkSync(latestPointer(root));
|
|
113
|
+
} catch {
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
function forkSession(root, id, newId, now) {
|
|
118
|
+
const src = loadSession(root, id);
|
|
119
|
+
if (!src) return void 0;
|
|
120
|
+
const fork = {
|
|
121
|
+
...src,
|
|
122
|
+
id: newId,
|
|
123
|
+
title: `(Fork) ${sessionTitle(src)}`.slice(0, 120),
|
|
124
|
+
forkedFrom: src.id,
|
|
125
|
+
updatedAt: now
|
|
126
|
+
};
|
|
127
|
+
try {
|
|
128
|
+
const dir = sessionsDir(root);
|
|
129
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
130
|
+
fs.writeFileSync(path.join(dir, `${newId}.json`), JSON.stringify(fork, null, 2));
|
|
131
|
+
updateSessionIndex(root, fork);
|
|
132
|
+
return fork;
|
|
133
|
+
} catch {
|
|
134
|
+
return void 0;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function rewindSession(root, id, keepTurns, now) {
|
|
138
|
+
const s = loadSession(root, id);
|
|
139
|
+
if (!s) return void 0;
|
|
140
|
+
const keep = Math.floor(keepTurns);
|
|
141
|
+
if (keep < 0 || keep >= s.tasks.length) return void 0;
|
|
142
|
+
const next = { ...s, tasks: s.tasks.slice(0, keep), updatedAt: now, lastChanges: [] };
|
|
143
|
+
try {
|
|
144
|
+
fs.writeFileSync(path.join(sessionsDir(root), `${next.id}.json`), JSON.stringify(next, null, 2));
|
|
145
|
+
updateSessionIndex(root, next);
|
|
146
|
+
return next;
|
|
147
|
+
} catch {
|
|
148
|
+
return void 0;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function loadLatestSession(root) {
|
|
152
|
+
try {
|
|
153
|
+
const { id } = JSON.parse(fs.readFileSync(latestPointer(root), "utf8"));
|
|
154
|
+
return loadSession(root, id);
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
return void 0;
|
|
158
|
+
}
|
|
159
|
+
function looksLikeSessionId(v) {
|
|
160
|
+
return /^[a-z0-9]{10}$/.test(v);
|
|
161
|
+
}
|
|
162
|
+
function loadSession(root, id) {
|
|
163
|
+
try {
|
|
164
|
+
const safe = id.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
165
|
+
if (!safe || safe !== id) return void 0;
|
|
166
|
+
const raw = JSON.parse(fs.readFileSync(path.join(sessionsDir(root), `${safe}.json`), "utf8"));
|
|
167
|
+
if (Array.isArray(raw.tasks) && raw.id) return raw;
|
|
168
|
+
} catch {
|
|
169
|
+
}
|
|
170
|
+
return void 0;
|
|
171
|
+
}
|
|
172
|
+
function listSessions(root, limit = 50) {
|
|
173
|
+
const indexed = readSessionIndex(root);
|
|
174
|
+
if (indexed) {
|
|
175
|
+
return [...indexed].sort((a, b) => b.updatedAt - a.updatedAt).slice(0, Math.max(1, limit));
|
|
176
|
+
}
|
|
177
|
+
const scanned = rebuildSessionIndex(root);
|
|
178
|
+
if (scanned.length) writeSessionIndex(root, scanned);
|
|
179
|
+
return scanned.slice(0, Math.max(1, limit));
|
|
180
|
+
}
|
|
181
|
+
function setLatestSession(root, id) {
|
|
182
|
+
const s = loadSession(root, id);
|
|
183
|
+
if (!s) return false;
|
|
184
|
+
try {
|
|
185
|
+
fs.mkdirSync(sessionsDir(root), { recursive: true });
|
|
186
|
+
fs.writeFileSync(latestPointer(root), JSON.stringify({ id: s.id }));
|
|
187
|
+
return true;
|
|
188
|
+
} catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
var MAX_INSTRUCTION_CHARS = 32e3;
|
|
193
|
+
var MAX_SUMMARY_CHARS = 2e5;
|
|
194
|
+
function recapSnippet(text, max) {
|
|
195
|
+
const s = String(text || "").replace(/\s+/g, " ").trim();
|
|
196
|
+
if (s.length <= max) return s;
|
|
197
|
+
return s.slice(0, Math.max(0, max - 1)) + "\u2026";
|
|
198
|
+
}
|
|
199
|
+
function summarizeSession(session) {
|
|
200
|
+
if (session.tasks.length === 0) return "";
|
|
201
|
+
const lines = session.tasks.slice(-8).map((t, i) => {
|
|
202
|
+
const instr = recapSnippet(t.instruction, 120);
|
|
203
|
+
const sum = recapSnippet(t.summary, 200);
|
|
204
|
+
const files = t.files.length ? ` (${t.files.join(", ")})` : "";
|
|
205
|
+
return `${i + 1}. ${instr} \u2014 ${sum}${files}`;
|
|
206
|
+
});
|
|
207
|
+
return `Earlier in this session (${session.provider}/${session.model}) you did:
|
|
208
|
+
${lines.join("\n")}`;
|
|
209
|
+
}
|
|
210
|
+
function newSession(id, provider, model, now) {
|
|
211
|
+
return { id, provider, model, startedAt: now, updatedAt: now, tasks: [], lastChanges: [] };
|
|
212
|
+
}
|
|
213
|
+
function condenseSession(session, now) {
|
|
214
|
+
if (session.tasks.length <= 1) return session;
|
|
215
|
+
const files = [...new Set(session.tasks.flatMap((t) => t.files))];
|
|
216
|
+
const lines = session.tasks.slice(-12).map((t) => `${t.instruction.slice(0, 80)} \u2014 ${t.summary.slice(0, 80)}`);
|
|
217
|
+
const checkpoint = {
|
|
218
|
+
instruction: `(compacted: ${session.tasks.length} earlier task(s))`,
|
|
219
|
+
summary: lines.join(" \xB7 ").slice(0, 1500),
|
|
220
|
+
files,
|
|
221
|
+
stopped: "compacted",
|
|
222
|
+
ts: now
|
|
223
|
+
};
|
|
224
|
+
return { ...session, updatedAt: now, tasks: [checkpoint] };
|
|
225
|
+
}
|
|
226
|
+
function recordTask(session, task, now) {
|
|
227
|
+
const files = [...new Set(task.changes.map((c) => c.file))];
|
|
228
|
+
const instruction = String(task.instruction ?? "").slice(0, MAX_INSTRUCTION_CHARS);
|
|
229
|
+
const summary = String(task.summary ?? "").slice(0, MAX_SUMMARY_CHARS);
|
|
230
|
+
const attachments = sanitizeAttachments(task.attachments);
|
|
231
|
+
return {
|
|
232
|
+
...session,
|
|
233
|
+
updatedAt: now,
|
|
234
|
+
tasks: [
|
|
235
|
+
...session.tasks,
|
|
236
|
+
{
|
|
237
|
+
instruction,
|
|
238
|
+
summary,
|
|
239
|
+
files,
|
|
240
|
+
stopped: task.stopped,
|
|
241
|
+
ts: now,
|
|
242
|
+
...attachments.length ? { attachments } : {}
|
|
243
|
+
}
|
|
244
|
+
],
|
|
245
|
+
lastChanges: task.changes.length ? task.changes : session.lastChanges
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
function sanitizeAttachments(raw) {
|
|
249
|
+
if (!Array.isArray(raw)) return [];
|
|
250
|
+
const out = [];
|
|
251
|
+
for (const a of raw.slice(0, 8)) {
|
|
252
|
+
if (!a || typeof a !== "object") continue;
|
|
253
|
+
const r = a;
|
|
254
|
+
const name = typeof r.name === "string" ? r.name.trim().slice(0, 120) : "";
|
|
255
|
+
if (!name) continue;
|
|
256
|
+
const kind = r.kind === "image" ? "image" : "text";
|
|
257
|
+
const mediaType = typeof r.mediaType === "string" ? r.mediaType.slice(0, 80) : void 0;
|
|
258
|
+
const bytes = typeof r.bytes === "number" && Number.isFinite(r.bytes) && r.bytes >= 0 ? r.bytes : void 0;
|
|
259
|
+
out.push({ name, kind, ...mediaType ? { mediaType } : {}, ...bytes !== void 0 ? { bytes } : {} });
|
|
260
|
+
}
|
|
261
|
+
return out;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export { condenseSession, deleteSession, forkSession, listSessions, loadLatestSession, loadSession, looksLikeSessionId, newSession, readSessionIndex, rebuildSessionIndex, recordTask, renameSession, rewindSession, sanitizeAttachments, saveSession, sessionTitle, sessionsDir, setLatestSession, summarizeSession };
|
|
265
|
+
//# sourceMappingURL=chunk-AJS4EL2C.js.map
|
|
266
|
+
//# sourceMappingURL=chunk-AJS4EL2C.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/code/session-store.ts"],"names":[],"mappings":"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","file":"chunk-AJS4EL2C.js","sourcesContent":["/**\n * Session persistence for VG Code (VG-CLI-CODE §16) — `vg code --continue`.\n *\n * A coding session is a series of tasks; this stores each turn's instruction and\n * final answer (plus which files changed) and the last change-set so `/undo`\n * survives a restart. History → pick reloads those turns into the panel so the\n * user can re-read the full thread. `--continue` reloads the most recent session\n * and seeds the model with a *short recap* of prior turns (see\n * {@link summarizeSession}) — never raw file bodies or tool transcripts.\n *\n * Pure over an injected clock; the store is JSON under `.vibgrate/code-sessions/`.\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { FileChange } from './types.js';\n\n/**\n * What was attached to a turn, as METADATA only — name, kind, size. The bytes\n * are deliberately absent: a session file that carried a 20 MiB screenshot\n * would be unreadable, unsyncable, and would blow the History index it feeds.\n * A restored chat shows what was attached, not the attachment itself.\n */\nexport interface SessionAttachment {\n name: string;\n kind: 'image' | 'text';\n mediaType?: string;\n bytes?: number;\n}\n\nexport interface SessionTask {\n instruction: string;\n summary: string;\n files: string[];\n stopped: string;\n ts: number;\n /** v5: what the user attached to this turn (metadata only). */\n attachments?: SessionAttachment[];\n}\n\nexport interface StoredSession {\n id: string;\n provider: string;\n model: string;\n startedAt: number;\n updatedAt: number;\n /** User-chosen chat name (rename). Absent → derived from the first task. */\n title?: string;\n /** Set when this chat was forked from another (History shows the lineage). */\n forkedFrom?: string;\n /** Set when this chat runs in an isolated git worktree (P3 worktree sessions). */\n worktree?: { id: string; path: string; base: string };\n tasks: SessionTask[];\n /** The most recent change-set, so `/undo` works after `--continue`. */\n lastChanges: FileChange[];\n}\n\nexport function sessionsDir(root: string): string {\n return path.join(root, '.vibgrate', 'code-sessions');\n}\nfunction latestPointer(root: string): string {\n return path.join(sessionsDir(root), 'latest.json');\n}\nfunction indexFile(root: string): string {\n return path.join(sessionsDir(root), 'index.json');\n}\n\n/** One row of the History index — everything a list UI needs, no session parse. */\nexport interface SessionIndexEntry {\n id: string;\n provider: string;\n model: string;\n startedAt: number;\n updatedAt: number;\n taskCount: number;\n title: string;\n forkedFrom?: string;\n /** Set when the chat runs in an isolated worktree (History shows a WT badge). */\n worktreeId?: string;\n}\n\n/** The derived list title: explicit rename wins, else the first instruction. */\nexport function sessionTitle(session: Pick<StoredSession, 'title' | 'tasks'>): string {\n const explicit = session.title?.trim();\n if (explicit) return explicit.slice(0, 80);\n return (session.tasks?.[0]?.instruction?.trim() || 'New chat').slice(0, 80);\n}\n\nfunction toIndexEntry(session: StoredSession): SessionIndexEntry {\n return {\n id: session.id,\n provider: session.provider ?? '',\n model: session.model ?? '',\n startedAt: session.startedAt ?? 0,\n updatedAt: session.updatedAt ?? session.startedAt ?? 0,\n taskCount: session.tasks.length,\n title: sessionTitle(session),\n ...(session.forkedFrom ? { forkedFrom: session.forkedFrom } : {}),\n ...(session.worktree ? { worktreeId: session.worktree.id } : {}),\n };\n}\n\n/**\n * Read the History index, or null when absent/unreadable. The index is a\n * best-effort cache maintained on every save/rename/delete: History UIs read\n * one small file instead of parsing every stored session (whose summaries can\n * run to 200k chars each). A missing or stale index is never an error — list\n * paths fall back to the full directory scan and rewrite it.\n */\nexport function readSessionIndex(root: string): SessionIndexEntry[] | null {\n try {\n const raw = JSON.parse(fs.readFileSync(indexFile(root), 'utf8')) as {\n sessions?: SessionIndexEntry[];\n };\n if (!Array.isArray(raw?.sessions)) return null;\n return raw.sessions.filter((s) => s && typeof s.id === 'string' && typeof s.title === 'string');\n } catch {\n return null;\n }\n}\n\nfunction writeSessionIndex(root: string, entries: SessionIndexEntry[]): void {\n try {\n fs.mkdirSync(sessionsDir(root), { recursive: true });\n fs.writeFileSync(indexFile(root), JSON.stringify({ sessions: entries }, null, 2));\n } catch {\n /* best-effort cache */\n }\n}\n\n/** Upsert one session's index row (called from every save). */\nfunction updateSessionIndex(root: string, session: StoredSession): void {\n const entries = readSessionIndex(root) ?? rebuildSessionIndex(root);\n const next = entries.filter((e) => e.id !== session.id);\n next.push(toIndexEntry(session));\n next.sort((a, b) => b.updatedAt - a.updatedAt);\n writeSessionIndex(root, next);\n}\n\n/** Rebuild the index from the stored files (missing/corrupt index, migrations). */\nexport function rebuildSessionIndex(root: string): SessionIndexEntry[] {\n const entries: SessionIndexEntry[] = [];\n try {\n const dir = sessionsDir(root);\n if (!fs.existsSync(dir)) return entries;\n for (const f of fs.readdirSync(dir)) {\n if (!f.endsWith('.json') || f === 'latest.json' || f === 'index.json') continue;\n try {\n const raw = JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8')) as StoredSession;\n if (raw?.id && Array.isArray(raw.tasks)) entries.push(toIndexEntry(raw));\n } catch {\n /* skip bad file */\n }\n }\n } catch {\n /* no dir */\n }\n entries.sort((a, b) => b.updatedAt - a.updatedAt);\n return entries;\n}\n\n/** Write a session and update the `latest` pointer + index. Best-effort; never throws. */\nexport function saveSession(root: string, session: StoredSession): void {\n try {\n const dir = sessionsDir(root);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(path.join(dir, `${session.id}.json`), JSON.stringify(session, null, 2));\n fs.writeFileSync(latestPointer(root), JSON.stringify({ id: session.id }));\n updateSessionIndex(root, session);\n } catch {\n /* persistence is best-effort */\n }\n}\n\n/*\n * NOTE: rename/delete/fork/rewind below are the store's REFERENCE mutations.\n * The VS Code extension mirrors them over the same files\n * (packages/vibgrate-vscode/src/codeSessionHistory.ts) because it cannot link\n * this library — a semantic change must land on both sides, and the\n * extension's protocol-conformance test pins the shared index.json shape.\n */\n\n/**\n * Rename a stored chat. Empty title clears the rename (back to the derived\n * name). Returns false when the session does not exist or the write failed.\n */\nexport function renameSession(root: string, id: string, title: string): boolean {\n const s = loadSession(root, id);\n if (!s) return false;\n const t = title.trim().slice(0, 120);\n const next: StoredSession = { ...s };\n if (t) next.title = t;\n else delete next.title;\n try {\n fs.writeFileSync(path.join(sessionsDir(root), `${next.id}.json`), JSON.stringify(next, null, 2));\n updateSessionIndex(root, next);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Delete a stored chat (and its index row). The `latest` pointer is cleared\n * when it referenced the deleted chat, so `--continue` cannot resume a ghost.\n */\nexport function deleteSession(root: string, id: string): boolean {\n const safe = id.replace(/[^a-zA-Z0-9_-]/g, '');\n if (!safe || safe !== id) return false;\n const file = path.join(sessionsDir(root), `${safe}.json`);\n if (!fs.existsSync(file)) return false;\n try {\n fs.unlinkSync(file);\n } catch {\n return false;\n }\n const entries = (readSessionIndex(root) ?? rebuildSessionIndex(root)).filter((e) => e.id !== safe);\n writeSessionIndex(root, entries);\n try {\n const { id: latest } = JSON.parse(fs.readFileSync(latestPointer(root), 'utf8')) as { id: string };\n if (latest === safe) fs.unlinkSync(latestPointer(root));\n } catch {\n /* no pointer */\n }\n return true;\n}\n\n/**\n * Fork a stored chat: copy it under a fresh id, renamed \"(Fork) <title>\", so\n * the user can explore an alternative continuation without losing the original.\n * Returns the new session, or undefined when the source is missing/unwritable.\n */\nexport function forkSession(\n root: string,\n id: string,\n newId: string,\n now: number,\n): StoredSession | undefined {\n const src = loadSession(root, id);\n if (!src) return undefined;\n const fork: StoredSession = {\n ...src,\n id: newId,\n title: `(Fork) ${sessionTitle(src)}`.slice(0, 120),\n forkedFrom: src.id,\n updatedAt: now,\n };\n try {\n const dir = sessionsDir(root);\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(path.join(dir, `${newId}.json`), JSON.stringify(fork, null, 2));\n updateSessionIndex(root, fork);\n return fork;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Rewind a stored chat to its first `keepTurns` tasks (0 empties it). The\n * discarded turns are gone from the recap the next launch seeds — pair with\n * checkpoint restores to also rewind the files. Returns the rewound session,\n * or undefined when the session is missing or `keepTurns` is not a rewind.\n */\nexport function rewindSession(\n root: string,\n id: string,\n keepTurns: number,\n now: number,\n): StoredSession | undefined {\n const s = loadSession(root, id);\n if (!s) return undefined;\n const keep = Math.floor(keepTurns);\n if (keep < 0 || keep >= s.tasks.length) return undefined;\n const next: StoredSession = { ...s, tasks: s.tasks.slice(0, keep), updatedAt: now, lastChanges: [] };\n try {\n fs.writeFileSync(path.join(sessionsDir(root), `${next.id}.json`), JSON.stringify(next, null, 2));\n updateSessionIndex(root, next);\n return next;\n } catch {\n return undefined;\n }\n}\n\n/** Load the most recent session, or undefined if none/there was a read error. */\nexport function loadLatestSession(root: string): StoredSession | undefined {\n try {\n const { id } = JSON.parse(fs.readFileSync(latestPointer(root), 'utf8')) as { id: string };\n return loadSession(root, id);\n } catch {\n /* no session */\n }\n return undefined;\n}\n\n/**\n * True for a string shaped like a minted session id (see `sessionId()` in\n * interactive.ts: exactly ten lowercase `[a-z0-9]` characters). `--continue`\n * takes an optional id, and commander cannot tell an id from the first word of\n * an instruction (`vg code --continue fix the tests` hands the parser\n * `continue=\"fix\"`) — callers use this shape test to fold anything else back\n * into the instruction instead of losing it to a doomed session lookup.\n */\nexport function looksLikeSessionId(v: string): boolean {\n return /^[a-z0-9]{10}$/.test(v);\n}\n\n/** Load one session by id, or undefined if missing/invalid. */\nexport function loadSession(root: string, id: string): StoredSession | undefined {\n try {\n const safe = id.replace(/[^a-zA-Z0-9_-]/g, '');\n if (!safe || safe !== id) return undefined;\n const raw = JSON.parse(fs.readFileSync(path.join(sessionsDir(root), `${safe}.json`), 'utf8')) as StoredSession;\n if (Array.isArray(raw.tasks) && raw.id) return raw;\n } catch {\n /* missing */\n }\n return undefined;\n}\n\nexport type SessionListEntry = SessionIndexEntry;\n\n/**\n * List stored sessions newest-first (multi-chat history). Served from the\n * index when one exists; otherwise a full scan that also rewrites the index\n * so the next list is cheap. Best-effort.\n */\nexport function listSessions(root: string, limit = 50): SessionListEntry[] {\n const indexed = readSessionIndex(root);\n if (indexed) {\n return [...indexed].sort((a, b) => b.updatedAt - a.updatedAt).slice(0, Math.max(1, limit));\n }\n const scanned = rebuildSessionIndex(root);\n if (scanned.length) writeSessionIndex(root, scanned);\n return scanned.slice(0, Math.max(1, limit));\n}\n\n/** Point `latest` at an existing session (resume a prior chat). */\nexport function setLatestSession(root: string, id: string): boolean {\n const s = loadSession(root, id);\n if (!s) return false;\n try {\n fs.mkdirSync(sessionsDir(root), { recursive: true });\n fs.writeFileSync(latestPointer(root), JSON.stringify({ id: s.id }));\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Cap stored fields so a runaway answer cannot fill the disk, while still\n * keeping enough of each prompt/answer for History → reload to repaint the\n * full panel transcript (the old 300-char cap cut answers mid-sentence).\n */\nconst MAX_INSTRUCTION_CHARS = 32_000;\nconst MAX_SUMMARY_CHARS = 200_000;\n\n/** Truncate for model recaps only — disk storage keeps the longer form. */\nfunction recapSnippet(text: string, max: number): string {\n const s = String(text || '').replace(/\\s+/g, ' ').trim();\n if (s.length <= max) return s;\n return s.slice(0, Math.max(0, max - 1)) + '…';\n}\n\n/** A short natural-language recap of a session to seed a continued run's context. */\nexport function summarizeSession(session: StoredSession): string {\n if (session.tasks.length === 0) return '';\n const lines = session.tasks.slice(-8).map((t, i) => {\n const instr = recapSnippet(t.instruction, 120);\n const sum = recapSnippet(t.summary, 200);\n const files = t.files.length ? ` (${t.files.join(', ')})` : '';\n return `${i + 1}. ${instr} — ${sum}${files}`;\n });\n return `Earlier in this session (${session.provider}/${session.model}) you did:\\n${lines.join('\\n')}`;\n}\n\n/** A fresh session record. `id` and `now` are injected so this stays deterministic. */\nexport function newSession(id: string, provider: string, model: string, now: number): StoredSession {\n return { id, provider, model, startedAt: now, updatedAt: now, tasks: [], lastChanges: [] };\n}\n\n/**\n * Manually compact a session: collapse all recorded tasks into a single\n * checkpoint entry so the recap seeded into future turns shrinks to one line\n * per prior task, capped. Deterministic (no model call) — the in-turn LLM\n * compaction handles rich summaries; this is the user-invoked `/compact`.\n */\nexport function condenseSession(session: StoredSession, now: number): StoredSession {\n if (session.tasks.length <= 1) return session;\n const files = [...new Set(session.tasks.flatMap((t) => t.files))];\n const lines = session.tasks\n .slice(-12)\n .map((t) => `${t.instruction.slice(0, 80)} — ${t.summary.slice(0, 80)}`);\n const checkpoint: SessionTask = {\n instruction: `(compacted: ${session.tasks.length} earlier task(s))`,\n summary: lines.join(' · ').slice(0, 1500),\n files,\n stopped: 'compacted',\n ts: now,\n };\n return { ...session, updatedAt: now, tasks: [checkpoint] };\n}\n\n/** Append a completed task (immutably updating the record's timestamps). */\nexport function recordTask(\n session: StoredSession,\n task: {\n instruction: string;\n summary: string;\n changes: FileChange[];\n stopped: string;\n attachments?: SessionAttachment[];\n },\n now: number,\n): StoredSession {\n const files = [...new Set(task.changes.map((c) => c.file))];\n const instruction = String(task.instruction ?? '').slice(0, MAX_INSTRUCTION_CHARS);\n const summary = String(task.summary ?? '').slice(0, MAX_SUMMARY_CHARS);\n const attachments = sanitizeAttachments(task.attachments);\n return {\n ...session,\n updatedAt: now,\n tasks: [\n ...session.tasks,\n {\n instruction,\n summary,\n files,\n stopped: task.stopped,\n ts: now,\n ...(attachments.length ? { attachments } : {}),\n },\n ],\n lastChanges: task.changes.length ? task.changes : session.lastChanges,\n };\n}\n\n/** Metadata only, capped and name-trimmed — never any file content. */\nexport function sanitizeAttachments(raw: unknown): SessionAttachment[] {\n if (!Array.isArray(raw)) return [];\n const out: SessionAttachment[] = [];\n for (const a of raw.slice(0, 8)) {\n if (!a || typeof a !== 'object') continue;\n const r = a as Record<string, unknown>;\n const name = typeof r.name === 'string' ? r.name.trim().slice(0, 120) : '';\n if (!name) continue;\n const kind = r.kind === 'image' ? 'image' : 'text';\n const mediaType = typeof r.mediaType === 'string' ? r.mediaType.slice(0, 80) : undefined;\n const bytes = typeof r.bytes === 'number' && Number.isFinite(r.bytes) && r.bytes >= 0 ? r.bytes : undefined;\n out.push({ name, kind, ...(mediaType ? { mediaType } : {}), ...(bytes !== undefined ? { bytes } : {}) });\n }\n return out;\n}\n"]}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { loadConfig, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem } from './chunk-INIULQEP.js';
|
|
2
1
|
import { availableRegionIds, resolveIngestHost, dashHostForIngestHost, ingestHostForRegionId } from './chunk-HTLDXEHW.js';
|
|
3
|
-
import {
|
|
2
|
+
import { loadConfig, computeUpgradeImpact, getChangelogSignals, gitHistoryAvailable, buildVersionTimelines, findPackageTimeline, severityRank, projectTypeToVulnEcosystem } from './chunk-N6ZMZ5AI.js';
|
|
3
|
+
import { searchSymbols, resolveVersion, resolveLib, loadCatalog, driftFor, localPackageDocs, readDoc, inventory, localApiSurface } from './chunk-NXDYWBZB.js';
|
|
4
|
+
import { resolveOne } from './chunk-J7LKEB2R.js';
|
|
5
|
+
import { impactOf, indexFor, queryGraphSemantic, queryGraph } from './chunk-EBUTRJHX.js';
|
|
4
6
|
import { discoverModels } from './chunk-CQDUBAQY.js';
|
|
5
7
|
import { loadOrDiscoverFederation, highConfidenceBridges } from './chunk-5I4VSBWQ.js';
|
|
6
8
|
import { writeTextFile, pathExists, readJsonFile } from './chunk-CRAMOEBE.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { parseGraph, analyzeQuestion, loadTopicTags, cacheDir } from './chunk-2M6B5V2U.js';
|
|
9
|
+
import { withTimeout, getNodeEmbeddings, loadEmbedder, isModelReady } from './chunk-UJAOCFQQ.js';
|
|
10
|
+
import { cacheDir } from './chunk-JYY6G46J.js';
|
|
11
|
+
import { parseGraph, analyzeQuestion, loadTopicTags } from './chunk-K7MONYIM.js';
|
|
11
12
|
import { repositoryIdFromRoot } from './chunk-BWPBB45M.js';
|
|
12
13
|
import { bidirectional } from 'graphology-shortest-path/unweighted.js';
|
|
13
14
|
import Graph from 'graphology';
|
|
@@ -2223,5 +2224,5 @@ function navigationToolsetConfig(serverName = "vibgrate") {
|
|
|
2223
2224
|
}
|
|
2224
2225
|
|
|
2225
2226
|
export { FREE_PACK, HOT_TOOLS, TOOLS, assessDocQuality, budgetSuffix, clearStoredCredentials, countTokens, coveringTests, createWorkspaceDsn, credentialsPath, deferredToolNames, detectRunner, dsnCommand, ensureGitignored, fetchHostedDocsCached, findGitRoot, gitignoreEntryForCredentials, homeCredentialsPath, hostedBase, listedToolNames, navigationToolsetConfig, parseDsn, pathDisconnect, projectCredentialsPath, publishPrivateLibrary, pushCommand, readScanArtifact, readStoredCredentials, renderToolResult, resolveDsn, selectForBudget, shortestPath, symbolsFromApi, testsToRun, uploadScanArtifact, warmEmbedderInBackground, writeStoredCredentials };
|
|
2226
|
-
//# sourceMappingURL=chunk-
|
|
2227
|
-
//# sourceMappingURL=chunk-
|
|
2227
|
+
//# sourceMappingURL=chunk-BTYFZ3NM.js.map
|
|
2228
|
+
//# sourceMappingURL=chunk-BTYFZ3NM.js.map
|