@vibgrate/cli 2026.722.2 → 2026.727.2
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/DOCS.md +418 -213
- package/README.md +34 -8
- package/dist/baseline-TG4ATZGZ.js +8 -0
- package/dist/{baseline-BD3A7EXD.js.map → baseline-TG4ATZGZ.js.map} +1 -1
- package/dist/chunk-2MBL43EJ.js +82 -0
- package/dist/chunk-2MBL43EJ.js.map +1 -0
- package/dist/chunk-2XQDMLKB.js +465 -0
- package/dist/chunk-2XQDMLKB.js.map +1 -0
- package/dist/{chunk-PY3DNX5H.js → chunk-3Q7QBWFW.js} +13 -2
- package/dist/chunk-3Q7QBWFW.js.map +1 -0
- package/dist/chunk-3V24O47U.js +478 -0
- package/dist/chunk-3V24O47U.js.map +1 -0
- package/dist/chunk-5I4VSBWQ.js +764 -0
- package/dist/chunk-5I4VSBWQ.js.map +1 -0
- package/dist/{chunk-WNIIKCNF.js → chunk-6ABXHZRN.js} +59 -311
- package/dist/chunk-6ABXHZRN.js.map +1 -0
- package/dist/chunk-6AYDFFP5.js +136 -0
- package/dist/chunk-6AYDFFP5.js.map +1 -0
- package/dist/chunk-6UQMXIAG.js +122 -0
- package/dist/chunk-6UQMXIAG.js.map +1 -0
- package/dist/chunk-7N4Y3E47.js +108 -0
- package/dist/chunk-7N4Y3E47.js.map +1 -0
- package/dist/chunk-BPF3YX2U.js +64 -0
- package/dist/chunk-BPF3YX2U.js.map +1 -0
- package/dist/chunk-BWPBB45M.js +88 -0
- package/dist/chunk-BWPBB45M.js.map +1 -0
- package/dist/{chunk-6CXTPC74.js → chunk-CY4WKXB2.js} +27 -6
- package/dist/chunk-CY4WKXB2.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +10 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/{chunk-NONYJLOJ.js → chunk-EX4PQT6W.js} +3 -3
- package/dist/{chunk-NONYJLOJ.js.map → chunk-EX4PQT6W.js.map} +1 -1
- package/dist/chunk-IS7VTH2Q.js +627 -0
- package/dist/chunk-IS7VTH2Q.js.map +1 -0
- package/dist/chunk-IZDH67DX.js +289 -0
- package/dist/chunk-IZDH67DX.js.map +1 -0
- package/dist/chunk-J4RFRKZH.js +2496 -0
- package/dist/chunk-J4RFRKZH.js.map +1 -0
- package/dist/chunk-J7LKEB2R.js +51 -0
- package/dist/chunk-J7LKEB2R.js.map +1 -0
- package/dist/chunk-JIBOJTGS.js +313 -0
- package/dist/chunk-JIBOJTGS.js.map +1 -0
- package/dist/chunk-K3MZIVZW.js +88 -0
- package/dist/chunk-K3MZIVZW.js.map +1 -0
- package/dist/chunk-K7REHDQS.js +776 -0
- package/dist/chunk-K7REHDQS.js.map +1 -0
- package/dist/{chunk-2O5YZVZV.js → chunk-KCKYKDMS.js} +23 -19
- package/dist/chunk-KCKYKDMS.js.map +1 -0
- package/dist/{chunk-3MXNNBLI.js → chunk-KNELIQJO.js} +1647 -2042
- package/dist/chunk-KNELIQJO.js.map +1 -0
- package/dist/chunk-KSBHGWRV.js +613 -0
- package/dist/chunk-KSBHGWRV.js.map +1 -0
- package/dist/chunk-LKVMI67M.js +1695 -0
- package/dist/chunk-LKVMI67M.js.map +1 -0
- package/dist/chunk-NDMDSPFX.js +69 -0
- package/dist/chunk-NDMDSPFX.js.map +1 -0
- package/dist/{chunk-CS37OBE3.js → chunk-OFUC6RYN.js} +791 -783
- package/dist/chunk-OFUC6RYN.js.map +1 -0
- package/dist/chunk-OOIG3RUM.js +276 -0
- package/dist/chunk-OOIG3RUM.js.map +1 -0
- package/dist/chunk-PMYWZRRS.js +62 -0
- package/dist/chunk-PMYWZRRS.js.map +1 -0
- package/dist/chunk-PXHNTC6D.js +382 -0
- package/dist/chunk-PXHNTC6D.js.map +1 -0
- package/dist/{chunk-I7GY7MVN.js → chunk-RNP3SMTV.js} +60 -12
- package/dist/chunk-RNP3SMTV.js.map +1 -0
- package/dist/chunk-TCDAZZNP.js +843 -0
- package/dist/chunk-TCDAZZNP.js.map +1 -0
- package/dist/{chunk-5BDHQ7DD.js → chunk-WGUL45AV.js} +24 -17
- package/dist/chunk-WGUL45AV.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +3373 -617
- package/dist/cli.js.map +1 -1
- package/dist/execution-env-ZU45JZ4G.js +4 -0
- package/dist/execution-env-ZU45JZ4G.js.map +1 -0
- package/dist/federation-SZWZIFZ7.js +7 -0
- package/dist/federation-SZWZIFZ7.js.map +1 -0
- package/dist/{fs-IPY4FJKH.js → fs-B5HZ2MIU.js} +3 -2
- package/dist/fs-B5HZ2MIU.js.map +1 -0
- package/dist/{fs-KCABDURV.js → fs-O5IQYXHO.js} +3 -2
- package/dist/fs-O5IQYXHO.js.map +1 -0
- package/dist/git-ref-XDXB6JP5.js +4 -0
- package/dist/git-ref-XDXB6JP5.js.map +1 -0
- package/dist/graph-backend-73URGTFY.js +11 -0
- package/dist/graph-backend-73URGTFY.js.map +1 -0
- package/dist/index.d.ts +472 -137
- package/dist/index.js +15 -7
- package/dist/index.js.map +1 -1
- package/dist/{interactive-A4DISSIW.js → interactive-372NLI5C.js} +186 -226
- package/dist/interactive-372NLI5C.js.map +1 -0
- package/dist/llm-host-7GWDO6IX.js +9 -0
- package/dist/llm-host-7GWDO6IX.js.map +1 -0
- package/dist/load-I333TGNI.js +8 -0
- package/dist/load-I333TGNI.js.map +1 -0
- package/dist/local-runtime-5O2NN3GA.js +5 -0
- package/dist/local-runtime-5O2NN3GA.js.map +1 -0
- package/dist/mcp-tools-I7JK3S2M.js +4 -0
- package/dist/{mcp-tools-I6PME3AV.js.map → mcp-tools-I7JK3S2M.js.map} +1 -1
- package/dist/model-execution-profile-TFBKCNJG.js +4 -0
- package/dist/model-execution-profile-TFBKCNJG.js.map +1 -0
- package/dist/model-orchestrator-TVONAUPS.js +9 -0
- package/dist/model-orchestrator-TVONAUPS.js.map +1 -0
- package/dist/parse-worker.d.ts +1 -1
- package/dist/parse-worker.js +2 -1
- package/dist/parse-worker.js.map +1 -1
- package/dist/paths-2RAH3GME.js +6 -0
- package/dist/paths-2RAH3GME.js.map +1 -0
- package/dist/resolve-gguf-VPL4YQ6J.js +8 -0
- package/dist/resolve-gguf-VPL4YQ6J.js.map +1 -0
- package/dist/runtime-session-JRKCQUHY.js +18 -0
- package/dist/runtime-session-JRKCQUHY.js.map +1 -0
- package/dist/session-ITPYQ5XI.js +11 -0
- package/dist/{session-POJUDFPH.js.map → session-ITPYQ5XI.js.map} +1 -1
- package/dist/session-store-WFBX7HXL.js +4 -0
- package/dist/{session-store-W5RZZHA3.js.map → session-store-WFBX7HXL.js.map} +1 -1
- package/dist/{stream-json-Q2JGWPHB.js → stream-json-SLMDKLXP.js} +17 -6
- package/dist/stream-json-SLMDKLXP.js.map +1 -0
- package/dist/{types-Jl6RJ175.d.ts → types-7AHBUbFm.d.ts} +8 -2
- package/dist/ui-Q6DJ3GCU.js +5 -0
- package/dist/ui-Q6DJ3GCU.js.map +1 -0
- package/dist/vgd-7JV5KCNJ.js +17 -0
- package/dist/vgd-7JV5KCNJ.js.map +1 -0
- package/package.json +9 -6
- package/dist/baseline-BD3A7EXD.js +0 -7
- package/dist/chunk-2O5YZVZV.js.map +0 -1
- package/dist/chunk-3MXNNBLI.js.map +0 -1
- package/dist/chunk-5BDHQ7DD.js.map +0 -1
- package/dist/chunk-6CXTPC74.js.map +0 -1
- package/dist/chunk-CS37OBE3.js.map +0 -1
- package/dist/chunk-GGJZA3Q6.js +0 -961
- package/dist/chunk-GGJZA3Q6.js.map +0 -1
- package/dist/chunk-I7GY7MVN.js.map +0 -1
- package/dist/chunk-JBXNQCGE.js +0 -484
- package/dist/chunk-JBXNQCGE.js.map +0 -1
- package/dist/chunk-PY3DNX5H.js.map +0 -1
- package/dist/chunk-WNIIKCNF.js.map +0 -1
- package/dist/fs-IPY4FJKH.js.map +0 -1
- package/dist/fs-KCABDURV.js.map +0 -1
- package/dist/interactive-A4DISSIW.js.map +0 -1
- package/dist/mcp-tools-I6PME3AV.js +0 -3
- package/dist/session-POJUDFPH.js +0 -6
- package/dist/session-store-W5RZZHA3.js +0 -3
- package/dist/stream-json-Q2JGWPHB.js.map +0 -1
- package/dist/ui-HOVOPWMX.js +0 -4
- package/dist/ui-HOVOPWMX.js.map +0 -1
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
import { indexFor, queryGraph, impactOf } from './chunk-6ABXHZRN.js';
|
|
2
|
+
import { cacheDir } from './chunk-K3MZIVZW.js';
|
|
3
|
+
import { hashString } from './chunk-JFGYT6BI.js';
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
|
|
7
|
+
var LEDGER = "savings.jsonl";
|
|
8
|
+
var PER_FILE_TOKENS = 400;
|
|
9
|
+
var SAVINGS_TOOLS = /* @__PURE__ */ new Set(["query_graph", "get_node", "search_symbols"]);
|
|
10
|
+
var CLI_TOOL_ALIASES = {
|
|
11
|
+
ask: "query_graph",
|
|
12
|
+
show: "get_node",
|
|
13
|
+
impact: "impact_of",
|
|
14
|
+
path: "find_path",
|
|
15
|
+
hubs: "list_hubs",
|
|
16
|
+
areas: "list_areas",
|
|
17
|
+
tree: "tree"
|
|
18
|
+
};
|
|
19
|
+
function sanitizeClient(name) {
|
|
20
|
+
if (typeof name !== "string") return "unknown";
|
|
21
|
+
const cleaned = name.trim().toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
22
|
+
return cleaned || "unknown";
|
|
23
|
+
}
|
|
24
|
+
function sanitizeModelId(name) {
|
|
25
|
+
if (typeof name !== "string") return void 0;
|
|
26
|
+
const cleaned = name.trim().toLowerCase().replace(/[^a-z0-9._:/-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
|
|
27
|
+
return cleaned || void 0;
|
|
28
|
+
}
|
|
29
|
+
function recordCliCall(root, entry, now) {
|
|
30
|
+
recordSaving(
|
|
31
|
+
root,
|
|
32
|
+
{
|
|
33
|
+
tool: entry.tool,
|
|
34
|
+
source: "cli",
|
|
35
|
+
client: sanitizeClient(entry.client),
|
|
36
|
+
provider: entry.provider ? sanitizeClient(entry.provider) : void 0,
|
|
37
|
+
model: sanitizeModelId(entry.model),
|
|
38
|
+
outcome: entry.outcome,
|
|
39
|
+
vgTokens: entry.vgTokens ?? 0,
|
|
40
|
+
baselineTokens: (entry.baselineFiles ?? 0) * PER_FILE_TOKENS
|
|
41
|
+
},
|
|
42
|
+
now
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
function savingsLedgerPath(root) {
|
|
46
|
+
return path.join(cacheDir(root), LEDGER);
|
|
47
|
+
}
|
|
48
|
+
function ledgerPath(root) {
|
|
49
|
+
return savingsLedgerPath(root);
|
|
50
|
+
}
|
|
51
|
+
function savingsRecorded(root) {
|
|
52
|
+
return fs.existsSync(ledgerPath(root));
|
|
53
|
+
}
|
|
54
|
+
function clearSavings(root) {
|
|
55
|
+
const file = ledgerPath(root);
|
|
56
|
+
const existed = fs.existsSync(file);
|
|
57
|
+
fs.rmSync(file, { force: true });
|
|
58
|
+
fs.rmSync(path.join(cacheDir(root), "stats-share.json"), { force: true });
|
|
59
|
+
return existed;
|
|
60
|
+
}
|
|
61
|
+
function recordSaving(root, entry, now) {
|
|
62
|
+
try {
|
|
63
|
+
fs.mkdirSync(cacheDir(root), { recursive: true });
|
|
64
|
+
const line = JSON.stringify({ ts: now, ...entry });
|
|
65
|
+
fs.appendFileSync(ledgerPath(root), line + "\n");
|
|
66
|
+
} catch {
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
var DEFAULT_RATE_PER_M = 3;
|
|
70
|
+
var DEFAULT_RATE_LABEL = "input @ $3/1M";
|
|
71
|
+
function readSavings(root, days, now, ratePerM = DEFAULT_RATE_PER_M) {
|
|
72
|
+
const file = ledgerPath(root);
|
|
73
|
+
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
74
|
+
let queries = 0;
|
|
75
|
+
let vgTokens = 0;
|
|
76
|
+
let baselineTokens = 0;
|
|
77
|
+
if (fs.existsSync(file)) {
|
|
78
|
+
for (const line of fs.readFileSync(file, "utf8").split("\n")) {
|
|
79
|
+
if (!line.trim()) continue;
|
|
80
|
+
try {
|
|
81
|
+
const e = JSON.parse(line);
|
|
82
|
+
if (e.ts < cutoff) continue;
|
|
83
|
+
if (!SAVINGS_TOOLS.has(e.tool)) continue;
|
|
84
|
+
queries++;
|
|
85
|
+
vgTokens += e.vgTokens;
|
|
86
|
+
baselineTokens += e.baselineTokens;
|
|
87
|
+
} catch {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const estCostVg = vgTokens / 1e6 * ratePerM;
|
|
92
|
+
const estCostBaseline = baselineTokens / 1e6 * ratePerM;
|
|
93
|
+
return {
|
|
94
|
+
enabled: savingsRecorded(root),
|
|
95
|
+
days,
|
|
96
|
+
queries,
|
|
97
|
+
vgTokens,
|
|
98
|
+
baselineTokens,
|
|
99
|
+
ratio: vgTokens > 0 ? Math.round(baselineTokens / vgTokens * 100) / 100 : 0,
|
|
100
|
+
estCostVg: round2(estCostVg),
|
|
101
|
+
estCostBaseline: round2(estCostBaseline),
|
|
102
|
+
saved: round2(estCostBaseline - estCostVg),
|
|
103
|
+
rateLabel: DEFAULT_RATE_LABEL
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function round2(x) {
|
|
107
|
+
return Math.round(x * 100) / 100;
|
|
108
|
+
}
|
|
109
|
+
function readModelSavings(root, days, now, ratePerM = DEFAULT_RATE_PER_M) {
|
|
110
|
+
const file = ledgerPath(root);
|
|
111
|
+
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
112
|
+
const byModel = /* @__PURE__ */ new Map();
|
|
113
|
+
if (fs.existsSync(file)) {
|
|
114
|
+
for (const line of fs.readFileSync(file, "utf8").split("\n")) {
|
|
115
|
+
if (!line.trim()) continue;
|
|
116
|
+
try {
|
|
117
|
+
const e = JSON.parse(line);
|
|
118
|
+
if (e.ts < cutoff || !e.model || !SAVINGS_TOOLS.has(e.tool)) continue;
|
|
119
|
+
const provider = e.provider ?? "unknown";
|
|
120
|
+
const key = `${provider}/${e.model}`;
|
|
121
|
+
const row = byModel.get(key) ?? { provider, model: e.model, queries: 0, vg: 0, base: 0 };
|
|
122
|
+
row.queries++;
|
|
123
|
+
row.vg += e.vgTokens;
|
|
124
|
+
row.base += e.baselineTokens;
|
|
125
|
+
byModel.set(key, row);
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return [...byModel.entries()].map(([key, r]) => ({
|
|
131
|
+
key,
|
|
132
|
+
provider: r.provider,
|
|
133
|
+
model: r.model,
|
|
134
|
+
queries: r.queries,
|
|
135
|
+
vgTokens: r.vg,
|
|
136
|
+
baselineTokens: r.base,
|
|
137
|
+
saved: round2((r.base - r.vg) / 1e6 * ratePerM)
|
|
138
|
+
})).sort((a, b) => b.saved - a.saved || a.key.localeCompare(b.key));
|
|
139
|
+
}
|
|
140
|
+
function toDimensionStats(byKey) {
|
|
141
|
+
return [...byKey.entries()].map(([key, r]) => {
|
|
142
|
+
const calls = r.complete + r.partial + r.miss;
|
|
143
|
+
return {
|
|
144
|
+
key,
|
|
145
|
+
calls,
|
|
146
|
+
complete: r.complete,
|
|
147
|
+
partial: r.partial,
|
|
148
|
+
miss: r.miss,
|
|
149
|
+
successPct: calls ? Math.round((r.complete + r.partial) / calls * 100) : 0
|
|
150
|
+
};
|
|
151
|
+
}).sort((a, b) => b.calls - a.calls || a.key.localeCompare(b.key));
|
|
152
|
+
}
|
|
153
|
+
function readUsage(root, days, now) {
|
|
154
|
+
const file = ledgerPath(root);
|
|
155
|
+
const cutoff = now - days * 24 * 60 * 60 * 1e3;
|
|
156
|
+
const byTool = /* @__PURE__ */ new Map();
|
|
157
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
158
|
+
const byClient = /* @__PURE__ */ new Map();
|
|
159
|
+
const msByTool = /* @__PURE__ */ new Map();
|
|
160
|
+
const bump = (m, key, outcome) => {
|
|
161
|
+
const row = m.get(key) ?? { complete: 0, partial: 0, miss: 0 };
|
|
162
|
+
row[outcome]++;
|
|
163
|
+
m.set(key, row);
|
|
164
|
+
};
|
|
165
|
+
if (fs.existsSync(file)) {
|
|
166
|
+
for (const line of fs.readFileSync(file, "utf8").split("\n")) {
|
|
167
|
+
if (!line.trim()) continue;
|
|
168
|
+
try {
|
|
169
|
+
const e = JSON.parse(line);
|
|
170
|
+
if (e.ts < cutoff) continue;
|
|
171
|
+
const outcome = e.outcome ?? "complete";
|
|
172
|
+
bump(byTool, e.tool, outcome);
|
|
173
|
+
bump(bySource, e.source ?? "mcp", outcome);
|
|
174
|
+
bump(byClient, e.client ?? "unknown", outcome);
|
|
175
|
+
if (typeof e.ms === "number" && e.ms >= 0) {
|
|
176
|
+
const timing = msByTool.get(e.tool) ?? { sum: 0, n: 0 };
|
|
177
|
+
timing.sum += e.ms;
|
|
178
|
+
timing.n++;
|
|
179
|
+
msByTool.set(e.tool, timing);
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const commands = toDimensionStats(byTool).map((d) => {
|
|
186
|
+
const timing = msByTool.get(d.key);
|
|
187
|
+
return {
|
|
188
|
+
tool: d.key,
|
|
189
|
+
calls: d.calls,
|
|
190
|
+
complete: d.complete,
|
|
191
|
+
partial: d.partial,
|
|
192
|
+
miss: d.miss,
|
|
193
|
+
successPct: d.successPct,
|
|
194
|
+
avgMs: timing && timing.n > 0 ? Math.round(timing.sum / timing.n) : null
|
|
195
|
+
};
|
|
196
|
+
});
|
|
197
|
+
const totals = commands.reduce(
|
|
198
|
+
(t, c) => ({
|
|
199
|
+
calls: t.calls + c.calls,
|
|
200
|
+
complete: t.complete + c.complete,
|
|
201
|
+
partial: t.partial + c.partial,
|
|
202
|
+
miss: t.miss + c.miss
|
|
203
|
+
}),
|
|
204
|
+
{ calls: 0, complete: 0, partial: 0, miss: 0 }
|
|
205
|
+
);
|
|
206
|
+
const avgSuccessPct = commands.length ? Math.round(commands.reduce((s, c) => s + c.successPct, 0) / commands.length) : 0;
|
|
207
|
+
return {
|
|
208
|
+
enabled: savingsRecorded(root),
|
|
209
|
+
days,
|
|
210
|
+
commands,
|
|
211
|
+
sources: toDimensionStats(bySource),
|
|
212
|
+
clients: toDimensionStats(byClient),
|
|
213
|
+
totals,
|
|
214
|
+
avgSuccessPct
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// src/code/context.ts
|
|
219
|
+
function buildCodeContext(graph, instruction, options = {}) {
|
|
220
|
+
const budget = options.budget ?? 3e3;
|
|
221
|
+
const seedLimit = options.seeds ?? 8;
|
|
222
|
+
const impactDepth = options.impactDepth ?? 2;
|
|
223
|
+
const index = indexFor(graph);
|
|
224
|
+
const q = queryGraph(graph, instruction, { budget: Math.floor(budget * 0.6), limit: seedLimit * 2 });
|
|
225
|
+
let seeds = q.matches.map((m) => ({ node: m.node, why: m.why }));
|
|
226
|
+
if (options.files && options.files.length) {
|
|
227
|
+
const set = new Set(options.files.map(normalize));
|
|
228
|
+
const inScope = seeds.filter((s) => set.has(normalize(s.node.file)));
|
|
229
|
+
if (inScope.length) seeds = inScope;
|
|
230
|
+
}
|
|
231
|
+
seeds = seeds.slice(0, seedLimit);
|
|
232
|
+
const impacted = /* @__PURE__ */ new Map();
|
|
233
|
+
for (const s of seeds) {
|
|
234
|
+
const impact = impactOf(graph, s.node.id, { depth: impactDepth });
|
|
235
|
+
for (const item of impact.affected.slice(0, 6)) {
|
|
236
|
+
const node = index.node(item.id);
|
|
237
|
+
if (node && !impacted.has(node.id)) impacted.set(node.id, { node, via: s.node.qualifiedName });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const targetFiles = options.files?.length ? [...new Set(options.files.map(normalize))].sort() : [...new Set(seeds.map((s) => s.node.file))].sort();
|
|
241
|
+
const seedIds = new Set(seeds.map((s) => s.node.id));
|
|
242
|
+
const pinnedFacts = pinnedFactsFor(graph, seedIds);
|
|
243
|
+
const rendered = render(instruction, seeds, [...impacted.values()], targetFiles, pinnedFacts, index, budget);
|
|
244
|
+
return {
|
|
245
|
+
instruction,
|
|
246
|
+
seeds,
|
|
247
|
+
targetFiles,
|
|
248
|
+
impacted: [...impacted.values()],
|
|
249
|
+
pinnedFacts,
|
|
250
|
+
rendered,
|
|
251
|
+
tokensEstimate: estimateTokens(rendered)
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
function pinnedFactsFor(graph, seedIds) {
|
|
255
|
+
if (!graph.facts?.length) return [];
|
|
256
|
+
const out = [];
|
|
257
|
+
for (const f of graph.facts) {
|
|
258
|
+
if (f.kind === "characterization") continue;
|
|
259
|
+
if (!f.subjectIds.some((id) => seedIds.has(id))) continue;
|
|
260
|
+
const subjects = f.subjectIds.map((id) => graph.nodes.find((n) => n.id === id)?.qualifiedName ?? id).join(", ");
|
|
261
|
+
out.push(`${f.kind}: ${subjects} \u2014 ${JSON.stringify(f.predicate)}`);
|
|
262
|
+
}
|
|
263
|
+
return out.sort().slice(0, 12);
|
|
264
|
+
}
|
|
265
|
+
function render(instruction, seeds, impacted, targetFiles, pinnedFacts, index, budget) {
|
|
266
|
+
const lines = [];
|
|
267
|
+
lines.push("# Repository context (from the deterministic code graph)");
|
|
268
|
+
lines.push("");
|
|
269
|
+
if (pinnedFacts.length) {
|
|
270
|
+
lines.push("## Hard constraints (do not violate)");
|
|
271
|
+
for (const f of pinnedFacts) lines.push(`- ${f}`);
|
|
272
|
+
lines.push("");
|
|
273
|
+
}
|
|
274
|
+
lines.push("## Relevant symbols");
|
|
275
|
+
for (const { node, why } of seeds) {
|
|
276
|
+
const block = [];
|
|
277
|
+
block.push(`### ${node.qualifiedName} (${node.kind}, ${node.file}:${node.span.start}-${node.span.end})`);
|
|
278
|
+
if (node.signature) block.push("`" + node.signature + "`");
|
|
279
|
+
const callees = uniqueNames(index.callees(node.id).map((x) => x.node.qualifiedName)).slice(0, 6);
|
|
280
|
+
const callers = uniqueNames(index.callers(node.id).map((x) => x.node.qualifiedName)).slice(0, 6);
|
|
281
|
+
if (callees.length) block.push(`calls: ${callees.join(", ")}`);
|
|
282
|
+
if (callers.length) block.push(`called by: ${callers.join(", ")}`);
|
|
283
|
+
block.push(`importance ${node.importance.toFixed(3)}${node.isHub ? " \xB7 hub (high blast radius)" : ""} \xB7 ${why}`);
|
|
284
|
+
block.push("");
|
|
285
|
+
const candidate = lines.concat(block).join("\n");
|
|
286
|
+
if (estimateTokens(candidate) > budget && lines.length > 3) break;
|
|
287
|
+
lines.push(...block);
|
|
288
|
+
}
|
|
289
|
+
if (impacted.length) {
|
|
290
|
+
lines.push("## Blast radius (would be affected by a change here)");
|
|
291
|
+
for (const { node, via } of impacted.slice(0, 10)) {
|
|
292
|
+
lines.push(`- ${node.qualifiedName} (${node.file}:${node.span.start}) \u2014 depends on ${via}`);
|
|
293
|
+
}
|
|
294
|
+
lines.push("");
|
|
295
|
+
}
|
|
296
|
+
lines.push("## Files in scope for this edit");
|
|
297
|
+
for (const f of targetFiles) lines.push(`- ${f}`);
|
|
298
|
+
lines.push("");
|
|
299
|
+
lines.push("## Task");
|
|
300
|
+
lines.push(instruction);
|
|
301
|
+
return lines.join("\n");
|
|
302
|
+
}
|
|
303
|
+
function uniqueNames(xs) {
|
|
304
|
+
return [...new Set(xs)];
|
|
305
|
+
}
|
|
306
|
+
function normalize(file) {
|
|
307
|
+
return file.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
308
|
+
}
|
|
309
|
+
function estimateTokens(text) {
|
|
310
|
+
return Math.ceil(text.length / 4);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// src/code/capsule.ts
|
|
314
|
+
var TASK_CAPSULE_SCHEMA_VERSION = "task-capsule/0";
|
|
315
|
+
var CAPSULE_RANKING_VERSION = "capsule-rank@2026.07.0";
|
|
316
|
+
var CAPSULE_COMPILER_ID = "vg-task-capsule/0";
|
|
317
|
+
function summarizeCapsule(capsule) {
|
|
318
|
+
const sourceFiles = [...new Set(capsule.sourceSlices.map((s) => s.file))].sort();
|
|
319
|
+
const preview = capsule.rendered.length > 4e3 ? `${capsule.rendered.slice(0, 4e3)}
|
|
320
|
+
\u2026 (truncated)` : capsule.rendered;
|
|
321
|
+
return {
|
|
322
|
+
schemaVersion: capsule.schemaVersion,
|
|
323
|
+
instruction: capsule.instruction,
|
|
324
|
+
primary: capsule.primary.map((p) => ({ qualifiedName: p.qualifiedName, file: p.file, kind: p.kind })),
|
|
325
|
+
supporting: capsule.supporting.map((p) => ({ qualifiedName: p.qualifiedName, file: p.file, kind: p.kind })),
|
|
326
|
+
sourceSliceCount: capsule.sourceSlices.length,
|
|
327
|
+
sourceFiles,
|
|
328
|
+
tokensEstimate: capsule.tokensEstimate,
|
|
329
|
+
rankingVersion: capsule.provenance.rankingVersion,
|
|
330
|
+
preview
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
function buildTaskCapsule(graph, instruction, options = {}) {
|
|
334
|
+
const base = buildCodeContext(graph, instruction, options);
|
|
335
|
+
const padding = options.padding ?? 1;
|
|
336
|
+
const maxSlices = options.maxSlices ?? 12;
|
|
337
|
+
const budget = options.budget ?? 3e3;
|
|
338
|
+
const index = indexFor(graph);
|
|
339
|
+
const primary = base.seeds.map((s) => toRef(s.node, s.why));
|
|
340
|
+
const primaryIds = new Set(primary.map((p) => p.id));
|
|
341
|
+
const supporting = [];
|
|
342
|
+
for (const { node, via } of base.impacted) {
|
|
343
|
+
if (primaryIds.has(node.id)) continue;
|
|
344
|
+
supporting.push(toRef(node, `depends on ${via}`));
|
|
345
|
+
}
|
|
346
|
+
const relationships = collectRelationships(base, index);
|
|
347
|
+
const sourceSlices = options.readFile ? buildSourceSlices(
|
|
348
|
+
[...base.seeds.map((s) => s.node), ...base.impacted.map((i) => i.node)],
|
|
349
|
+
options.readFile,
|
|
350
|
+
padding,
|
|
351
|
+
maxSlices
|
|
352
|
+
) : [];
|
|
353
|
+
const extraFacts = (options.extraPinnedFacts ?? []).filter((f) => typeof f === "string" && f.trim());
|
|
354
|
+
const pinnedFacts = [...base.pinnedFacts, ...extraFacts];
|
|
355
|
+
const verificationPlan = buildVerificationPlan(graph, base);
|
|
356
|
+
const rendered = renderCapsule(instruction, primary, supporting, sourceSlices, pinnedFacts, base.targetFiles, verificationPlan, relationships, budget);
|
|
357
|
+
return {
|
|
358
|
+
schemaVersion: TASK_CAPSULE_SCHEMA_VERSION,
|
|
359
|
+
instruction,
|
|
360
|
+
primary,
|
|
361
|
+
supporting,
|
|
362
|
+
sourceSlices,
|
|
363
|
+
relationships,
|
|
364
|
+
pinnedFacts,
|
|
365
|
+
targetFiles: base.targetFiles,
|
|
366
|
+
verificationPlan,
|
|
367
|
+
rendered,
|
|
368
|
+
tokensEstimate: estimateTokens2(rendered),
|
|
369
|
+
provenance: {
|
|
370
|
+
compiler: CAPSULE_COMPILER_ID,
|
|
371
|
+
rankingVersion: CAPSULE_RANKING_VERSION,
|
|
372
|
+
graphCorpusHash: graph.provenance?.corpusHash ?? null,
|
|
373
|
+
repositoryId: options.repositoryId ?? null,
|
|
374
|
+
modelProfileId: options.provenance?.modelProfileId ?? null,
|
|
375
|
+
securityTier: options.provenance?.securityTier ?? null,
|
|
376
|
+
policyVersion: options.provenance?.policyVersion ?? null
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function capsuleToCodeContext(capsule) {
|
|
381
|
+
return {
|
|
382
|
+
instruction: capsule.instruction,
|
|
383
|
+
seeds: capsule.primary.map((p) => ({
|
|
384
|
+
node: {
|
|
385
|
+
id: p.id,
|
|
386
|
+
qualifiedName: p.qualifiedName,
|
|
387
|
+
kind: p.kind,
|
|
388
|
+
file: p.file,
|
|
389
|
+
span: p.span,
|
|
390
|
+
signature: p.signature ?? void 0,
|
|
391
|
+
importance: p.importance
|
|
392
|
+
},
|
|
393
|
+
why: p.why
|
|
394
|
+
})),
|
|
395
|
+
targetFiles: capsule.targetFiles,
|
|
396
|
+
impacted: capsule.supporting.map((s) => ({
|
|
397
|
+
node: {
|
|
398
|
+
id: s.id,
|
|
399
|
+
qualifiedName: s.qualifiedName,
|
|
400
|
+
kind: s.kind,
|
|
401
|
+
file: s.file,
|
|
402
|
+
span: s.span,
|
|
403
|
+
signature: s.signature ?? void 0,
|
|
404
|
+
importance: s.importance
|
|
405
|
+
},
|
|
406
|
+
via: s.why.replace(/^depends on /, "")
|
|
407
|
+
})),
|
|
408
|
+
pinnedFacts: capsule.pinnedFacts,
|
|
409
|
+
rendered: capsule.rendered,
|
|
410
|
+
tokensEstimate: capsule.tokensEstimate
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
function toRef(node, why) {
|
|
414
|
+
return {
|
|
415
|
+
id: node.id,
|
|
416
|
+
qualifiedName: node.qualifiedName,
|
|
417
|
+
kind: node.kind,
|
|
418
|
+
file: normalize2(node.file),
|
|
419
|
+
span: { start: node.span.start, end: node.span.end },
|
|
420
|
+
signature: node.signature ?? null,
|
|
421
|
+
why,
|
|
422
|
+
importance: node.importance
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
function collectRelationships(base, index) {
|
|
426
|
+
const out = [];
|
|
427
|
+
const seen = /* @__PURE__ */ new Set();
|
|
428
|
+
const push = (kind, from, to) => {
|
|
429
|
+
const key = `${kind}|${from}|${to}`;
|
|
430
|
+
if (seen.has(key)) return;
|
|
431
|
+
seen.add(key);
|
|
432
|
+
out.push({ kind, from, to });
|
|
433
|
+
};
|
|
434
|
+
for (const { node } of base.seeds) {
|
|
435
|
+
for (const c of index.callees(node.id).slice(0, 6)) {
|
|
436
|
+
push("calls", node.qualifiedName, c.node.qualifiedName);
|
|
437
|
+
}
|
|
438
|
+
for (const c of index.callers(node.id).slice(0, 6)) {
|
|
439
|
+
push("called-by", node.qualifiedName, c.node.qualifiedName);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
for (const { node, via } of base.impacted.slice(0, 10)) {
|
|
443
|
+
push("impacts", via, node.qualifiedName);
|
|
444
|
+
}
|
|
445
|
+
return out.sort((a, b) => a.kind.localeCompare(b.kind) || a.from.localeCompare(b.from) || a.to.localeCompare(b.to));
|
|
446
|
+
}
|
|
447
|
+
function buildSourceSlices(nodes, readFile, padding, maxSlices) {
|
|
448
|
+
const eligible = nodes.filter((n) => !["file", "module", "package"].includes(n.kind));
|
|
449
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
450
|
+
for (const n of eligible) {
|
|
451
|
+
const file = normalize2(n.file);
|
|
452
|
+
const list = byFile.get(file) ?? [];
|
|
453
|
+
list.push(n);
|
|
454
|
+
byFile.set(file, list);
|
|
455
|
+
}
|
|
456
|
+
const ranges = [];
|
|
457
|
+
for (const [file, fileNodes] of [...byFile.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
458
|
+
const sorted = [...fileNodes].sort((a, b) => a.span.start - b.span.start || b.importance - a.importance);
|
|
459
|
+
let current = null;
|
|
460
|
+
for (const n of sorted) {
|
|
461
|
+
const start = Math.max(1, n.span.start - padding);
|
|
462
|
+
const end = n.span.end + padding;
|
|
463
|
+
const score = n.importance + (n.isHub ? 0.25 : 0);
|
|
464
|
+
if (current && current.file === file && start <= current.end + 2) {
|
|
465
|
+
current.end = Math.max(current.end, end);
|
|
466
|
+
current.symbolIds.push(n.id);
|
|
467
|
+
current.score = Math.max(current.score, score);
|
|
468
|
+
} else {
|
|
469
|
+
if (current) ranges.push(current);
|
|
470
|
+
current = { file, start, end, symbolIds: [n.id], score };
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (current) ranges.push(current);
|
|
474
|
+
}
|
|
475
|
+
ranges.sort((a, b) => b.score - a.score || a.file.localeCompare(b.file) || a.start - b.start);
|
|
476
|
+
const slices = [];
|
|
477
|
+
for (const range of ranges) {
|
|
478
|
+
if (slices.length >= maxSlices) break;
|
|
479
|
+
const raw = readFile(range.file);
|
|
480
|
+
if (raw === null) continue;
|
|
481
|
+
const lines = raw.split("\n");
|
|
482
|
+
const start = Math.min(range.start, lines.length);
|
|
483
|
+
const end = Math.min(range.end, lines.length);
|
|
484
|
+
if (start < 1 || end < start) continue;
|
|
485
|
+
const content = lines.slice(start - 1, end).join("\n");
|
|
486
|
+
slices.push({
|
|
487
|
+
file: range.file,
|
|
488
|
+
start,
|
|
489
|
+
end,
|
|
490
|
+
content,
|
|
491
|
+
contentHash: hashString(content),
|
|
492
|
+
symbolIds: [...new Set(range.symbolIds)].sort()
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
return slices.sort((a, b) => a.file.localeCompare(b.file) || a.start - b.start);
|
|
496
|
+
}
|
|
497
|
+
function buildVerificationPlan(graph, base) {
|
|
498
|
+
const syntaxFiles = [...base.targetFiles].sort();
|
|
499
|
+
const seedIds = new Set(base.seeds.map((s) => s.node.id));
|
|
500
|
+
const suggestedTests = [];
|
|
501
|
+
for (const n of graph.nodes) {
|
|
502
|
+
if (n.kind !== "test") continue;
|
|
503
|
+
const sameFile = base.seeds.some((s) => normalize2(s.node.file) === normalize2(n.file));
|
|
504
|
+
const nameHit = base.seeds.some((s) => n.qualifiedName.toLowerCase().includes(s.node.name.toLowerCase()));
|
|
505
|
+
if (sameFile || nameHit || seedIds.has(n.id)) {
|
|
506
|
+
suggestedTests.push(n.qualifiedName);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
suggestedTests.sort();
|
|
510
|
+
const notes = [];
|
|
511
|
+
if (!suggestedTests.length) {
|
|
512
|
+
notes.push("No graph-linked tests found for seed symbols; run the nearest project test command after apply.");
|
|
513
|
+
}
|
|
514
|
+
if (base.impacted.some((i) => i.node.isHub || i.node.importance >= 0.7)) {
|
|
515
|
+
notes.push("High-importance dependents in blast radius \u2014 prefer targeted tests before a full build.");
|
|
516
|
+
}
|
|
517
|
+
return {
|
|
518
|
+
syntaxFiles,
|
|
519
|
+
suggestedTests: suggestedTests.slice(0, 12),
|
|
520
|
+
notes
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
function renderCapsule(instruction, primary, supporting, slices, pinnedFacts, targetFiles, verification, relationships, budget) {
|
|
524
|
+
const lines = [];
|
|
525
|
+
lines.push("# Task capsule (source-bearing, from the deterministic code graph)");
|
|
526
|
+
lines.push("");
|
|
527
|
+
lines.push("Use the exact source evidence below. Search or read outside it only if this capsule is insufficient.");
|
|
528
|
+
lines.push("");
|
|
529
|
+
if (pinnedFacts.length) {
|
|
530
|
+
lines.push("## Hard constraints (do not violate)");
|
|
531
|
+
for (const f of pinnedFacts) lines.push(`- ${f}`);
|
|
532
|
+
lines.push("");
|
|
533
|
+
}
|
|
534
|
+
lines.push("## Primary symbols");
|
|
535
|
+
for (const s of primary) {
|
|
536
|
+
lines.push(`- ${s.qualifiedName} (${s.kind}, ${s.file}:${s.span.start}-${s.span.end})${s.signature ? ` \u2014 \`${s.signature}\`` : ""} \u2014 ${s.why}`);
|
|
537
|
+
}
|
|
538
|
+
lines.push("");
|
|
539
|
+
if (supporting.length) {
|
|
540
|
+
lines.push("## Supporting / blast radius");
|
|
541
|
+
for (const s of supporting.slice(0, 10)) {
|
|
542
|
+
lines.push(`- ${s.qualifiedName} (${s.file}:${s.span.start}) \u2014 ${s.why}`);
|
|
543
|
+
}
|
|
544
|
+
lines.push("");
|
|
545
|
+
}
|
|
546
|
+
if (relationships.length) {
|
|
547
|
+
lines.push("## Relationships");
|
|
548
|
+
for (const r of relationships.slice(0, 24)) {
|
|
549
|
+
lines.push(`- ${r.kind}: ${r.from} \u2192 ${r.to}`);
|
|
550
|
+
}
|
|
551
|
+
lines.push("");
|
|
552
|
+
}
|
|
553
|
+
lines.push("## Exact source evidence");
|
|
554
|
+
if (!slices.length) {
|
|
555
|
+
lines.push("_No source slices available (file reader missing or spans unreadable)._");
|
|
556
|
+
lines.push("");
|
|
557
|
+
} else {
|
|
558
|
+
let included = 0;
|
|
559
|
+
for (const slice of slices) {
|
|
560
|
+
const block = [
|
|
561
|
+
`### ${slice.file}:${slice.start}-${slice.end}`,
|
|
562
|
+
`\`\`\`${langFor(slice.file)}`,
|
|
563
|
+
slice.content,
|
|
564
|
+
"```",
|
|
565
|
+
""
|
|
566
|
+
];
|
|
567
|
+
const candidate = [...lines, ...block, "## Task", instruction].join("\n");
|
|
568
|
+
if (estimateTokens2(candidate) > budget && included > 0) break;
|
|
569
|
+
lines.push(...block);
|
|
570
|
+
included += 1;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
lines.push("## Files in scope");
|
|
574
|
+
for (const f of targetFiles) lines.push(`- ${f}`);
|
|
575
|
+
lines.push("");
|
|
576
|
+
lines.push("## Verification plan");
|
|
577
|
+
for (const f of verification.syntaxFiles) lines.push(`- syntax: ${f}`);
|
|
578
|
+
for (const t of verification.suggestedTests) lines.push(`- test: ${t}`);
|
|
579
|
+
for (const n of verification.notes) lines.push(`- note: ${n}`);
|
|
580
|
+
lines.push("");
|
|
581
|
+
lines.push("## Task");
|
|
582
|
+
lines.push(instruction);
|
|
583
|
+
return lines.join("\n");
|
|
584
|
+
}
|
|
585
|
+
function langFor(file) {
|
|
586
|
+
const ext = file.split(".").pop()?.toLowerCase() ?? "";
|
|
587
|
+
const map = {
|
|
588
|
+
ts: "ts",
|
|
589
|
+
tsx: "tsx",
|
|
590
|
+
js: "js",
|
|
591
|
+
jsx: "jsx",
|
|
592
|
+
mjs: "js",
|
|
593
|
+
cjs: "js",
|
|
594
|
+
py: "python",
|
|
595
|
+
rs: "rust",
|
|
596
|
+
go: "go",
|
|
597
|
+
java: "java",
|
|
598
|
+
cs: "csharp",
|
|
599
|
+
md: "md",
|
|
600
|
+
json: "json"
|
|
601
|
+
};
|
|
602
|
+
return map[ext] ?? "";
|
|
603
|
+
}
|
|
604
|
+
function normalize2(file) {
|
|
605
|
+
return file.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
606
|
+
}
|
|
607
|
+
function estimateTokens2(text) {
|
|
608
|
+
return Math.ceil(text.length / 4);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
export { CAPSULE_COMPILER_ID, CAPSULE_RANKING_VERSION, CLI_TOOL_ALIASES, PER_FILE_TOKENS, SAVINGS_TOOLS, TASK_CAPSULE_SCHEMA_VERSION, buildCodeContext, buildTaskCapsule, capsuleToCodeContext, clearSavings, readModelSavings, readSavings, readUsage, recordCliCall, recordSaving, sanitizeClient, savingsLedgerPath, savingsRecorded, summarizeCapsule };
|
|
612
|
+
//# sourceMappingURL=chunk-KSBHGWRV.js.map
|
|
613
|
+
//# sourceMappingURL=chunk-KSBHGWRV.js.map
|