@vibgrate/cli 2026.721.3 → 2026.722.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 +185 -0
- package/README.md +2 -1
- package/dist/baseline-BD3A7EXD.js +7 -0
- package/dist/{baseline-37G4TINM.js.map → baseline-BD3A7EXD.js.map} +1 -1
- package/dist/chunk-2O5YZVZV.js +417 -0
- package/dist/chunk-2O5YZVZV.js.map +1 -0
- package/dist/{chunk-ELUFCQDR.js → chunk-3MXNNBLI.js} +240 -2519
- package/dist/chunk-3MXNNBLI.js.map +1 -0
- package/dist/{chunk-JLRF5TFA.js → chunk-5BDHQ7DD.js} +42 -71
- package/dist/chunk-5BDHQ7DD.js.map +1 -0
- package/dist/chunk-6CXTPC74.js +234 -0
- package/dist/chunk-6CXTPC74.js.map +1 -0
- package/dist/chunk-CRAMOEBE.js +675 -0
- package/dist/chunk-CRAMOEBE.js.map +1 -0
- package/dist/chunk-CS37OBE3.js +861 -0
- package/dist/chunk-CS37OBE3.js.map +1 -0
- package/dist/chunk-CT5BS56U.js +23 -0
- package/dist/chunk-CT5BS56U.js.map +1 -0
- package/dist/chunk-GGJZA3Q6.js +961 -0
- package/dist/chunk-GGJZA3Q6.js.map +1 -0
- package/dist/chunk-I7GY7MVN.js +833 -0
- package/dist/chunk-I7GY7MVN.js.map +1 -0
- package/dist/chunk-JBXNQCGE.js +484 -0
- package/dist/chunk-JBXNQCGE.js.map +1 -0
- package/dist/chunk-JFGYT6BI.js +32 -0
- package/dist/chunk-JFGYT6BI.js.map +1 -0
- package/dist/chunk-JYS4OBN2.js +148 -0
- package/dist/chunk-JYS4OBN2.js.map +1 -0
- package/dist/chunk-K3SLGULW.js +50 -0
- package/dist/chunk-K3SLGULW.js.map +1 -0
- package/dist/chunk-LEPTUB4H.js +108 -0
- package/dist/chunk-LEPTUB4H.js.map +1 -0
- package/dist/{chunk-5PLKADD6.js → chunk-NONYJLOJ.js} +4 -3
- package/dist/chunk-NONYJLOJ.js.map +1 -0
- package/dist/{chunk-IKJBELUV.js → chunk-PY3DNX5H.js} +4 -29
- package/dist/chunk-PY3DNX5H.js.map +1 -0
- package/dist/chunk-WNIIKCNF.js +972 -0
- package/dist/chunk-WNIIKCNF.js.map +1 -0
- package/dist/cli.js +2512 -1224
- package/dist/cli.js.map +1 -1
- package/dist/fs-KCABDURV.js +3 -0
- package/dist/fs-KCABDURV.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +8 -3
- package/dist/index.js.map +1 -1
- package/dist/interactive-A4DISSIW.js +681 -0
- package/dist/interactive-A4DISSIW.js.map +1 -0
- package/dist/mcp-tools-I6PME3AV.js +3 -0
- package/dist/mcp-tools-I6PME3AV.js.map +1 -0
- package/dist/parse-worker.js +2 -1
- package/dist/parse-worker.js.map +1 -1
- package/dist/session-POJUDFPH.js +6 -0
- package/dist/session-POJUDFPH.js.map +1 -0
- package/dist/session-store-W5RZZHA3.js +3 -0
- package/dist/session-store-W5RZZHA3.js.map +1 -0
- package/dist/stream-json-Q2JGWPHB.js +65 -0
- package/dist/stream-json-Q2JGWPHB.js.map +1 -0
- package/dist/ui-HOVOPWMX.js +4 -0
- package/dist/ui-HOVOPWMX.js.map +1 -0
- package/package.json +1 -1
- package/dist/baseline-37G4TINM.js +0 -6
- package/dist/chunk-5PLKADD6.js.map +0 -1
- package/dist/chunk-ELUFCQDR.js.map +0 -1
- package/dist/chunk-IKJBELUV.js.map +0 -1
- package/dist/chunk-JLRF5TFA.js.map +0 -1
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { blake3 } from '@noble/hashes/blake3.js';
|
|
2
|
+
import { bytesToHex } from '@noble/hashes/utils.js';
|
|
3
|
+
|
|
4
|
+
// src/engine/hash.ts
|
|
5
|
+
var encoder = new TextEncoder();
|
|
6
|
+
function hashString(input) {
|
|
7
|
+
return bytesToHex(blake3(encoder.encode(input)));
|
|
8
|
+
}
|
|
9
|
+
function hashBytes(input) {
|
|
10
|
+
return bytesToHex(blake3(input));
|
|
11
|
+
}
|
|
12
|
+
function shortId(input) {
|
|
13
|
+
return bytesToHex(blake3(encoder.encode(input), { dkLen: 16 }));
|
|
14
|
+
}
|
|
15
|
+
function canonicalize(value) {
|
|
16
|
+
return JSON.stringify(sortKeys(value));
|
|
17
|
+
}
|
|
18
|
+
function sortKeys(value) {
|
|
19
|
+
if (Array.isArray(value)) return value.map(sortKeys);
|
|
20
|
+
if (value && typeof value === "object") {
|
|
21
|
+
const out = {};
|
|
22
|
+
for (const key of Object.keys(value).sort()) {
|
|
23
|
+
out[key] = sortKeys(value[key]);
|
|
24
|
+
}
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { canonicalize, hashBytes, hashString, shortId };
|
|
31
|
+
//# sourceMappingURL=chunk-JFGYT6BI.js.map
|
|
32
|
+
//# sourceMappingURL=chunk-JFGYT6BI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/engine/hash.ts"],"names":[],"mappings":";;;;AAYA,IAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAGzB,SAAS,WAAW,KAAA,EAAuB;AAChD,EAAA,OAAO,WAAW,MAAA,CAAO,OAAA,CAAQ,MAAA,CAAO,KAAK,CAAC,CAAC,CAAA;AACjD;AAGO,SAAS,UAAU,KAAA,EAA2B;AACnD,EAAA,OAAO,UAAA,CAAW,MAAA,CAAO,KAAK,CAAC,CAAA;AACjC;AAOO,SAAS,QAAQ,KAAA,EAAuB;AAC7C,EAAA,OAAO,UAAA,CAAW,MAAA,CAAO,OAAA,CAAQ,MAAA,CAAO,KAAK,GAAG,EAAE,KAAA,EAAO,EAAA,EAAI,CAAC,CAAA;AAChE;AAOO,SAAS,aAAa,KAAA,EAAwB;AACnD,EAAA,OAAO,IAAA,CAAK,SAAA,CAAU,QAAA,CAAS,KAAK,CAAC,CAAA;AACvC;AAEA,SAAS,SAAS,KAAA,EAAyB;AACzC,EAAA,IAAI,MAAM,OAAA,CAAQ,KAAK,GAAG,OAAO,KAAA,CAAM,IAAI,QAAQ,CAAA;AACnD,EAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,EAAU;AACtC,IAAA,MAAM,MAA+B,EAAC;AACtC,IAAA,KAAA,MAAW,OAAO,MAAA,CAAO,IAAA,CAAK,KAAgC,CAAA,CAAE,MAAK,EAAG;AACtE,MAAA,GAAA,CAAI,GAAG,CAAA,GAAI,QAAA,CAAU,KAAA,CAAkC,GAAG,CAAC,CAAA;AAAA,IAC7D;AACA,IAAA,OAAO,GAAA;AAAA,EACT;AACA,EAAA,OAAO,KAAA;AACT","file":"chunk-JFGYT6BI.js","sourcesContent":["import { blake3 } from '@noble/hashes/blake3.js';\nimport { bytesToHex } from '@noble/hashes/utils.js';\n\n/**\n * Content hashing — the bedrock of vg's determinism contract.\n *\n * Ids are blake3 over a canonical payload. blake3 is pure-WASM/JS (no native\n * build, no platform variance), so identical input → identical id on every\n * machine. We never let timestamps, RNG, locale, or filesystem order into a\n * hashed payload (VG-ENGINE-TEARDOWN.md §5).\n */\n\nconst encoder = new TextEncoder();\n\n/** Full hex digest of a UTF-8 string. */\nexport function hashString(input: string): string {\n return bytesToHex(blake3(encoder.encode(input)));\n}\n\n/** Full hex digest of raw bytes (file contents). */\nexport function hashBytes(input: Uint8Array): string {\n return bytesToHex(blake3(input));\n}\n\n/**\n * A short, collision-resistant id for nodes/edges. 16 bytes (128 bits) of\n * blake3 is ample for repo-scale graphs and keeps `graph.json` compact and\n * human-diffable.\n */\nexport function shortId(input: string): string {\n return bytesToHex(blake3(encoder.encode(input), { dkLen: 16 }));\n}\n\n/**\n * Canonical JSON: object keys sorted recursively, no insignificant whitespace.\n * The single source of truth for \"what bytes get hashed\" so two runs that see\n * the same logical payload produce byte-identical input to blake3.\n */\nexport function canonicalize(value: unknown): string {\n return JSON.stringify(sortKeys(value));\n}\n\nfunction sortKeys(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(sortKeys);\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>).sort()) {\n out[key] = sortKeys((value as Record<string, unknown>)[key]);\n }\n return out;\n }\n return value;\n}\n"]}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { c } from './chunk-CT5BS56U.js';
|
|
2
|
+
import * as readline from 'readline';
|
|
3
|
+
|
|
4
|
+
var PromptCancelled = class extends Error {
|
|
5
|
+
constructor() {
|
|
6
|
+
super("cancelled");
|
|
7
|
+
this.name = "PromptCancelled";
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var teal = (s) => c.hex("#3FB0A4")(s);
|
|
11
|
+
var mint = (s) => c.hex("#4FE3C1")(s);
|
|
12
|
+
var err = (s) => {
|
|
13
|
+
process.stderr.write(s);
|
|
14
|
+
};
|
|
15
|
+
var TtyPrompter = class {
|
|
16
|
+
intro(message) {
|
|
17
|
+
err(`
|
|
18
|
+
${mint("\u25C6")} ${c.bold(message)}
|
|
19
|
+
`);
|
|
20
|
+
}
|
|
21
|
+
outro(message) {
|
|
22
|
+
err(`${mint("\u25C7")} ${message}
|
|
23
|
+
|
|
24
|
+
`);
|
|
25
|
+
}
|
|
26
|
+
note(message) {
|
|
27
|
+
err(`${c.dim("\u2502")} ${message}
|
|
28
|
+
`);
|
|
29
|
+
}
|
|
30
|
+
async select(message, choices) {
|
|
31
|
+
if (choices.length === 0) throw new Error("select called with no choices");
|
|
32
|
+
err(`${teal("?")} ${c.bold(message)} ${c.dim("(\u2191/\u2193, Enter)")}
|
|
33
|
+
`);
|
|
34
|
+
const stdin = process.stdin;
|
|
35
|
+
const canRaw = typeof stdin.setRawMode === "function" && stdin.isTTY;
|
|
36
|
+
if (!canRaw) return this.numberedSelect(message, choices);
|
|
37
|
+
readline.emitKeypressEvents(stdin);
|
|
38
|
+
stdin.setRawMode(true);
|
|
39
|
+
stdin.resume();
|
|
40
|
+
let index = 0;
|
|
41
|
+
const render = (first = false) => {
|
|
42
|
+
if (!first) err(`\x1B[${choices.length}A`);
|
|
43
|
+
for (let i = 0; i < choices.length; i++) {
|
|
44
|
+
const active = i === index;
|
|
45
|
+
const pointer = active ? mint("\u276F") : " ";
|
|
46
|
+
const label = active ? c.bold(choices[i].label) : choices[i].label;
|
|
47
|
+
const hint = choices[i].hint ? c.dim(` ${choices[i].hint}`) : "";
|
|
48
|
+
err(`\x1B[2K${pointer} ${label}${hint}
|
|
49
|
+
`);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
render(true);
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const onKey = (_str, key) => {
|
|
55
|
+
if (!key) return;
|
|
56
|
+
if (key.name === "up" || key.name === "k" && !key.ctrl) {
|
|
57
|
+
index = (index - 1 + choices.length) % choices.length;
|
|
58
|
+
render();
|
|
59
|
+
} else if (key.name === "down" || key.name === "j" && !key.ctrl) {
|
|
60
|
+
index = (index + 1) % choices.length;
|
|
61
|
+
render();
|
|
62
|
+
} else if (key.name === "return" || key.name === "enter") {
|
|
63
|
+
cleanup();
|
|
64
|
+
err(`${teal("\u2714")} ${c.dim(choices[index].label)}
|
|
65
|
+
`);
|
|
66
|
+
resolve(choices[index].value);
|
|
67
|
+
} else if (key.name === "escape" || key.ctrl && key.name === "c") {
|
|
68
|
+
cleanup();
|
|
69
|
+
reject(new PromptCancelled());
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const cleanup = () => {
|
|
73
|
+
stdin.removeListener("keypress", onKey);
|
|
74
|
+
if (typeof stdin.setRawMode === "function") stdin.setRawMode(false);
|
|
75
|
+
stdin.pause();
|
|
76
|
+
};
|
|
77
|
+
stdin.on("keypress", onKey);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/** Numbered fallback when raw mode isn't available (e.g. a dumb terminal). */
|
|
81
|
+
async numberedSelect(message, choices) {
|
|
82
|
+
for (let i = 0; i < choices.length; i++) {
|
|
83
|
+
err(` ${c.bold(String(i + 1))}. ${choices[i].label}${choices[i].hint ? c.dim(` ${choices[i].hint}`) : ""}
|
|
84
|
+
`);
|
|
85
|
+
}
|
|
86
|
+
for (; ; ) {
|
|
87
|
+
const answer = await this.input(`${message} \u2014 number`);
|
|
88
|
+
const n = Number(answer);
|
|
89
|
+
if (Number.isInteger(n) && n >= 1 && n <= choices.length) return choices[n - 1].value;
|
|
90
|
+
this.note(c.yellow(`enter 1\u2013${choices.length}`));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
input(message, opts = {}) {
|
|
94
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
|
|
95
|
+
const suffix = opts.default ? c.dim(` (${opts.default})`) : opts.placeholder ? c.dim(` (${opts.placeholder})`) : "";
|
|
96
|
+
return new Promise((resolve) => {
|
|
97
|
+
rl.question(`${teal("?")} ${c.bold(message)}${suffix} `, (answer) => {
|
|
98
|
+
rl.close();
|
|
99
|
+
const trimmed = answer.trim();
|
|
100
|
+
resolve(trimmed || opts.default || "");
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
async confirm(message, def = false) {
|
|
105
|
+
const answer = await this.input(`${message} ${c.dim(def ? "[Y/n]" : "[y/N]")}`);
|
|
106
|
+
if (!answer) return def;
|
|
107
|
+
return /^y(es)?$/i.test(answer.trim());
|
|
108
|
+
}
|
|
109
|
+
spinner(message) {
|
|
110
|
+
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
111
|
+
let i = 0;
|
|
112
|
+
let current = message;
|
|
113
|
+
const active = process.stderr.isTTY;
|
|
114
|
+
let timer;
|
|
115
|
+
if (active) {
|
|
116
|
+
timer = setInterval(() => {
|
|
117
|
+
err(`\x1B[2K\r${teal(frames[i = (i + 1) % frames.length])} ${current}`);
|
|
118
|
+
}, 80);
|
|
119
|
+
timer.unref?.();
|
|
120
|
+
} else {
|
|
121
|
+
err(`${current}
|
|
122
|
+
`);
|
|
123
|
+
}
|
|
124
|
+
const clear = () => {
|
|
125
|
+
if (timer) clearInterval(timer);
|
|
126
|
+
if (active) err(`\x1B[2K\r`);
|
|
127
|
+
};
|
|
128
|
+
return {
|
|
129
|
+
update(m) {
|
|
130
|
+
current = m;
|
|
131
|
+
},
|
|
132
|
+
stop(finalMessage) {
|
|
133
|
+
clear();
|
|
134
|
+
if (finalMessage) err(`${teal("\u2714")} ${finalMessage}
|
|
135
|
+
`);
|
|
136
|
+
},
|
|
137
|
+
fail(m) {
|
|
138
|
+
clear();
|
|
139
|
+
err(`${c.red("\u2717")} ${m}
|
|
140
|
+
`);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export { PromptCancelled, TtyPrompter };
|
|
147
|
+
//# sourceMappingURL=chunk-JYS4OBN2.js.map
|
|
148
|
+
//# sourceMappingURL=chunk-JYS4OBN2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/code/ui.ts"],"names":[],"mappings":"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","file":"chunk-JYS4OBN2.js","sourcesContent":["/**\n * Zero-dependency interactive UI for the `vg code` guided flow (VG-CLI-CODE §9).\n *\n * We keep the published CLI lean and install-free, so rather than pull an\n * interactive-prompt dependency we ship a small, focused prompter built on\n * `node:readline` + raw-mode keypresses: an arrow-key `select`, a text `input`,\n * a `confirm`, notes, and a `spinner` that wraps noisy child-process output\n * (e.g. an `ollama pull`) behind a single clean status line.\n *\n * Everything is behind the {@link Prompter} interface so the wizard/REPL logic\n * is driven by a scripted fake in tests — no TTY, no raw mode, fully\n * deterministic. All UI writes go to stderr, leaving stdout clean for piped\n * JSON.\n */\n\nimport * as readline from 'node:readline';\nimport { c } from '../util/output.js';\n\n/** Thrown when the user cancels (Esc / Ctrl-C) so callers can exit gracefully. */\nexport class PromptCancelled extends Error {\n constructor() {\n super('cancelled');\n this.name = 'PromptCancelled';\n }\n}\n\nexport interface SelectChoice<T> {\n label: string;\n value: T;\n hint?: string;\n}\n\nexport interface Spinner {\n update(message: string): void;\n stop(finalMessage?: string): void;\n fail(message: string): void;\n}\n\n/** The interactive surface the wizard/REPL depend on (injectable for tests). */\nexport interface Prompter {\n intro(message: string): void;\n outro(message: string): void;\n note(message: string): void;\n select<T>(message: string, choices: SelectChoice<T>[]): Promise<T>;\n input(message: string, opts?: { default?: string; placeholder?: string }): Promise<string>;\n confirm(message: string, def?: boolean): Promise<boolean>;\n spinner(message: string): Spinner;\n}\n\nconst teal = (s: string): string => c.hex('#3FB0A4')(s);\nconst mint = (s: string): string => c.hex('#4FE3C1')(s);\nconst err = (s: string): void => {\n process.stderr.write(s);\n};\n\n/** The real terminal prompter. Only used at an interactive TTY. */\nexport class TtyPrompter implements Prompter {\n intro(message: string): void {\n err(`\\n${mint('◆')} ${c.bold(message)}\\n`);\n }\n outro(message: string): void {\n err(`${mint('◇')} ${message}\\n\\n`);\n }\n note(message: string): void {\n err(`${c.dim('│')} ${message}\\n`);\n }\n\n async select<T>(message: string, choices: SelectChoice<T>[]): Promise<T> {\n if (choices.length === 0) throw new Error('select called with no choices');\n err(`${teal('?')} ${c.bold(message)} ${c.dim('(↑/↓, Enter)')}\\n`);\n const stdin = process.stdin;\n const canRaw = typeof stdin.setRawMode === 'function' && stdin.isTTY;\n // Without a raw-capable TTY, fall back to a numbered prompt.\n if (!canRaw) return this.numberedSelect(message, choices);\n\n readline.emitKeypressEvents(stdin);\n stdin.setRawMode(true);\n stdin.resume();\n let index = 0;\n const render = (first = false): void => {\n if (!first) err(`\\x1b[${choices.length}A`); // cursor up N lines\n for (let i = 0; i < choices.length; i++) {\n const active = i === index;\n const pointer = active ? mint('❯') : ' ';\n const label = active ? c.bold(choices[i].label) : choices[i].label;\n const hint = choices[i].hint ? c.dim(` ${choices[i].hint}`) : '';\n err(`\\x1b[2K${pointer} ${label}${hint}\\n`);\n }\n };\n render(true);\n return new Promise<T>((resolve, reject) => {\n const onKey = (_str: string, key: readline.Key): void => {\n if (!key) return;\n if (key.name === 'up' || (key.name === 'k' && !key.ctrl)) {\n index = (index - 1 + choices.length) % choices.length;\n render();\n } else if (key.name === 'down' || (key.name === 'j' && !key.ctrl)) {\n index = (index + 1) % choices.length;\n render();\n } else if (key.name === 'return' || key.name === 'enter') {\n cleanup();\n err(`${teal('✔')} ${c.dim(choices[index].label)}\\n`);\n resolve(choices[index].value);\n } else if (key.name === 'escape' || (key.ctrl && key.name === 'c')) {\n cleanup();\n reject(new PromptCancelled());\n }\n };\n const cleanup = (): void => {\n stdin.removeListener('keypress', onKey);\n if (typeof stdin.setRawMode === 'function') stdin.setRawMode(false);\n stdin.pause();\n };\n stdin.on('keypress', onKey);\n });\n }\n\n /** Numbered fallback when raw mode isn't available (e.g. a dumb terminal). */\n private async numberedSelect<T>(message: string, choices: SelectChoice<T>[]): Promise<T> {\n for (let i = 0; i < choices.length; i++) {\n err(` ${c.bold(String(i + 1))}. ${choices[i].label}${choices[i].hint ? c.dim(` ${choices[i].hint}`) : ''}\\n`);\n }\n for (;;) {\n const answer = await this.input(`${message} — number`);\n const n = Number(answer);\n if (Number.isInteger(n) && n >= 1 && n <= choices.length) return choices[n - 1].value;\n this.note(c.yellow(`enter 1–${choices.length}`));\n }\n }\n\n input(message: string, opts: { default?: string; placeholder?: string } = {}): Promise<string> {\n const rl = readline.createInterface({ input: process.stdin, output: process.stderr });\n const suffix = opts.default ? c.dim(` (${opts.default})`) : opts.placeholder ? c.dim(` (${opts.placeholder})`) : '';\n return new Promise((resolve) => {\n rl.question(`${teal('?')} ${c.bold(message)}${suffix} `, (answer) => {\n rl.close();\n const trimmed = answer.trim();\n resolve(trimmed || opts.default || '');\n });\n });\n }\n\n async confirm(message: string, def = false): Promise<boolean> {\n const answer = await this.input(`${message} ${c.dim(def ? '[Y/n]' : '[y/N]')}`);\n if (!answer) return def;\n return /^y(es)?$/i.test(answer.trim());\n }\n\n spinner(message: string): Spinner {\n const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n let i = 0;\n let current = message;\n const active = process.stderr.isTTY;\n let timer: ReturnType<typeof setInterval> | undefined;\n if (active) {\n timer = setInterval(() => {\n err(`\\x1b[2K\\r${teal(frames[(i = (i + 1) % frames.length)])} ${current}`);\n }, 80);\n timer.unref?.();\n } else {\n err(`${current}\\n`);\n }\n const clear = (): void => {\n if (timer) clearInterval(timer);\n if (active) err(`\\x1b[2K\\r`);\n };\n return {\n update(m) {\n current = m;\n },\n stop(finalMessage) {\n clear();\n if (finalMessage) err(`${teal('✔')} ${finalMessage}\\n`);\n },\n fail(m) {\n clear();\n err(`${c.red('✗')} ${m}\\n`);\n },\n };\n }\n}\n"]}
|
|
@@ -0,0 +1,50 @@
|
|
|
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 saveSession(root, session) {
|
|
12
|
+
try {
|
|
13
|
+
const dir = sessionsDir(root);
|
|
14
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
15
|
+
fs.writeFileSync(path.join(dir, `${session.id}.json`), JSON.stringify(session, null, 2));
|
|
16
|
+
fs.writeFileSync(latestPointer(root), JSON.stringify({ id: session.id }));
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function loadLatestSession(root) {
|
|
21
|
+
try {
|
|
22
|
+
const { id } = JSON.parse(fs.readFileSync(latestPointer(root), "utf8"));
|
|
23
|
+
const raw = JSON.parse(fs.readFileSync(path.join(sessionsDir(root), `${id}.json`), "utf8"));
|
|
24
|
+
if (Array.isArray(raw.tasks)) return raw;
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
return void 0;
|
|
28
|
+
}
|
|
29
|
+
function summarizeSession(session) {
|
|
30
|
+
if (session.tasks.length === 0) return "";
|
|
31
|
+
const lines = session.tasks.slice(-8).map((t, i) => `${i + 1}. ${t.instruction} \u2014 ${t.summary}${t.files.length ? ` (${t.files.join(", ")})` : ""}`);
|
|
32
|
+
return `Earlier in this session (${session.provider}/${session.model}) you did:
|
|
33
|
+
${lines.join("\n")}`;
|
|
34
|
+
}
|
|
35
|
+
function newSession(id, provider, model, now) {
|
|
36
|
+
return { id, provider, model, startedAt: now, updatedAt: now, tasks: [], lastChanges: [] };
|
|
37
|
+
}
|
|
38
|
+
function recordTask(session, task, now) {
|
|
39
|
+
const files = [...new Set(task.changes.map((c) => c.file))];
|
|
40
|
+
return {
|
|
41
|
+
...session,
|
|
42
|
+
updatedAt: now,
|
|
43
|
+
tasks: [...session.tasks, { instruction: task.instruction.slice(0, 300), summary: task.summary.slice(0, 300), files, stopped: task.stopped, ts: now }],
|
|
44
|
+
lastChanges: task.changes.length ? task.changes : session.lastChanges
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export { loadLatestSession, newSession, recordTask, saveSession, sessionsDir, summarizeSession };
|
|
49
|
+
//# sourceMappingURL=chunk-K3SLGULW.js.map
|
|
50
|
+
//# sourceMappingURL=chunk-K3SLGULW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/code/session-store.ts"],"names":[],"mappings":"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","file":"chunk-K3SLGULW.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 a compact record of them\n * (what you asked, a one-line outcome, which files changed) plus the last\n * change-set so `/undo` survives a restart. `--continue` reloads the most recent\n * session and hands the model a short summary of what was already done, so you\n * can pick a conversation back up instead of re-explaining. We persist a\n * *summary*, never raw file contents or tool transcripts — small, private, and\n * safe to keep on disk.\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\nexport interface SessionTask {\n instruction: string;\n summary: string;\n files: string[];\n stopped: string;\n ts: number;\n}\n\nexport interface StoredSession {\n id: string;\n provider: string;\n model: string;\n startedAt: number;\n updatedAt: number;\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}\n\n/** Write a session and update the `latest` pointer. 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 } catch {\n /* persistence is best-effort */\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 const raw = JSON.parse(fs.readFileSync(path.join(sessionsDir(root), `${id}.json`), 'utf8')) as StoredSession;\n if (Array.isArray(raw.tasks)) return raw;\n } catch {\n /* no session */\n }\n return undefined;\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) => `${i + 1}. ${t.instruction} — ${t.summary}${t.files.length ? ` (${t.files.join(', ')})` : ''}`);\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/** Append a completed task (immutably updating the record's timestamps). */\nexport function recordTask(\n session: StoredSession,\n task: { instruction: string; summary: string; changes: FileChange[]; stopped: string },\n now: number,\n): StoredSession {\n const files = [...new Set(task.changes.map((c) => c.file))];\n return {\n ...session,\n updatedAt: now,\n tasks: [...session.tasks, { instruction: task.instruction.slice(0, 300), summary: task.summary.slice(0, 300), files, stopped: task.stopped, ts: now }],\n lastChanges: task.changes.length ? task.changes : session.lastChanges,\n };\n}\n"]}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// src/code/mcp-tools.ts
|
|
2
|
+
var NS = "mcp__";
|
|
3
|
+
var sep = "__";
|
|
4
|
+
var McpToolset = class _McpToolset {
|
|
5
|
+
constructor(clients, tools) {
|
|
6
|
+
this.clients = clients;
|
|
7
|
+
this.tools = tools;
|
|
8
|
+
}
|
|
9
|
+
clients;
|
|
10
|
+
tools;
|
|
11
|
+
/**
|
|
12
|
+
* Connect to every configured server and enumerate its tools. A server that
|
|
13
|
+
* fails to connect or list is skipped with a note in `warnings` — one broken
|
|
14
|
+
* server never sinks the session.
|
|
15
|
+
*/
|
|
16
|
+
static async connect(servers, connect) {
|
|
17
|
+
const clients = /* @__PURE__ */ new Map();
|
|
18
|
+
const tools = /* @__PURE__ */ new Map();
|
|
19
|
+
const warnings = [];
|
|
20
|
+
for (const [server, config] of Object.entries(servers)) {
|
|
21
|
+
try {
|
|
22
|
+
const client = await connect(server, config);
|
|
23
|
+
const listed = await client.listTools();
|
|
24
|
+
clients.set(server, client);
|
|
25
|
+
for (const t of listed.tools) {
|
|
26
|
+
const name = `${NS}${server}${sep}${t.name}`;
|
|
27
|
+
tools.set(name, {
|
|
28
|
+
server,
|
|
29
|
+
toolName: t.name,
|
|
30
|
+
readOnly: t.annotations?.readOnlyHint === true,
|
|
31
|
+
spec: {
|
|
32
|
+
name,
|
|
33
|
+
description: `[${server}] ${t.description ?? t.name}`,
|
|
34
|
+
parameters: t.inputSchema && typeof t.inputSchema === "object" ? t.inputSchema : { type: "object", properties: {} }
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
} catch (e) {
|
|
39
|
+
warnings.push(`MCP server "${server}" unavailable: ${e.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { toolset: new _McpToolset(clients, tools), warnings };
|
|
43
|
+
}
|
|
44
|
+
/** The tool specs to advertise to the model. */
|
|
45
|
+
specs() {
|
|
46
|
+
return [...this.tools.values()].map((t) => t.spec).sort((a, b) => a.name.localeCompare(b.name));
|
|
47
|
+
}
|
|
48
|
+
/** Whether a tool call name belongs to this toolset. */
|
|
49
|
+
owns(name) {
|
|
50
|
+
return this.tools.has(name);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Execute an external tool call. A non-read-only tool is gated through
|
|
54
|
+
* `approve` first (default-deny if declined). Never throws — errors come back
|
|
55
|
+
* as tool content for the model to read.
|
|
56
|
+
*/
|
|
57
|
+
async execute(call, approve) {
|
|
58
|
+
const owned = this.tools.get(call.name);
|
|
59
|
+
if (!owned) return { content: `unknown MCP tool ${call.name}`, mutated: false };
|
|
60
|
+
if (!owned.readOnly) {
|
|
61
|
+
if (!await approve({ kind: "tool", name: call.name, args: call.arguments })) {
|
|
62
|
+
return { content: `calling ${call.name} was declined by the user`, mutated: false };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const client = this.clients.get(owned.server);
|
|
66
|
+
if (!client) return { content: `MCP server ${owned.server} is not connected`, mutated: false };
|
|
67
|
+
try {
|
|
68
|
+
const res = await client.callTool({ name: owned.toolName, arguments: call.arguments });
|
|
69
|
+
const text = (res.content ?? []).map((c) => c.type === "text" ? c.text ?? "" : `[${c.type}]`).join("\n") || "(no output)";
|
|
70
|
+
return { content: text.slice(0, 12e3), mutated: !owned.readOnly && !res.isError };
|
|
71
|
+
} catch (e) {
|
|
72
|
+
return { content: `MCP tool ${call.name} failed: ${e.message}`, mutated: false };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async dispose() {
|
|
76
|
+
for (const client of this.clients.values()) {
|
|
77
|
+
try {
|
|
78
|
+
await client.close();
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var defaultMcpConnect = async (name, config) => {
|
|
85
|
+
const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');
|
|
86
|
+
const client = new Client({ name: `vg-code:${name}`, version: "1.0.0" }, { capabilities: {} });
|
|
87
|
+
if (config.url) {
|
|
88
|
+
const url = new URL(config.url);
|
|
89
|
+
const requestInit = config.headers ? { headers: config.headers } : void 0;
|
|
90
|
+
if (config.type === "sse") {
|
|
91
|
+
const { SSEClientTransport } = await import('@modelcontextprotocol/sdk/client/sse.js');
|
|
92
|
+
await client.connect(new SSEClientTransport(url, requestInit ? { requestInit } : void 0));
|
|
93
|
+
} else {
|
|
94
|
+
const { StreamableHTTPClientTransport } = await import('@modelcontextprotocol/sdk/client/streamableHttp.js');
|
|
95
|
+
await client.connect(new StreamableHTTPClientTransport(url, requestInit ? { requestInit } : void 0));
|
|
96
|
+
}
|
|
97
|
+
} else if (config.command) {
|
|
98
|
+
const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js');
|
|
99
|
+
await client.connect(new StdioClientTransport({ command: config.command, args: config.args ?? [], env: config.env }));
|
|
100
|
+
} else {
|
|
101
|
+
throw new Error("server config has neither a command (stdio) nor a url (remote)");
|
|
102
|
+
}
|
|
103
|
+
return client;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export { McpToolset, defaultMcpConnect };
|
|
107
|
+
//# sourceMappingURL=chunk-LEPTUB4H.js.map
|
|
108
|
+
//# sourceMappingURL=chunk-LEPTUB4H.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/code/mcp-tools.ts"],"names":[],"mappings":"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","file":"chunk-LEPTUB4H.js","sourcesContent":["/**\n * External MCP tools for the VG Code agent (VG-CLI-CODE §17).\n *\n * Like Claude Code, VG Code can use tools from external MCP servers you list in\n * `.vibgrate/code.json` (`mcpServers`). We connect to each server, list its\n * tools, expose them to the model namespaced as `mcp__<server>__<tool>`, and\n * route calls back. Read-only tools (per their `readOnlyHint`) run freely;\n * anything else goes through the same approval gate as a built-in mutating tool,\n * so an external server can't mutate or exfiltrate without consent.\n *\n * The transport is injectable, so namespacing, routing, the read-only/gated\n * split, and lifecycle are unit-tested with a fake client — no real server.\n */\n\nimport type { McpServerConfig } from './config.js';\nimport type { ToolResult } from './tools.js';\nimport type { MutatingAction } from './tools.js';\nimport type { ToolCall, ToolSpec } from './types.js';\n\n/** The subset of an MCP client we use — satisfied by the real SDK client and by test fakes. */\nexport interface McpClientLike {\n listTools(): Promise<{ tools: McpToolDef[] }>;\n callTool(args: { name: string; arguments: Record<string, unknown> }): Promise<{ content?: { type: string; text?: string }[]; isError?: boolean }>;\n close(): Promise<void>;\n}\n\nexport interface McpToolDef {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n annotations?: { readOnlyHint?: boolean; destructiveHint?: boolean };\n}\n\n/** How to open a connection to a configured server (injectable; default uses the SDK). */\nexport type McpConnect = (name: string, config: McpServerConfig) => Promise<McpClientLike>;\n\nconst NS = 'mcp__';\nconst sep = '__';\n\ninterface OwnedTool {\n server: string;\n toolName: string;\n spec: ToolSpec;\n readOnly: boolean;\n}\n\n/** A live set of external MCP tools, keyed by their namespaced name. */\nexport class McpToolset {\n private constructor(\n private readonly clients: Map<string, McpClientLike>,\n private readonly tools: Map<string, OwnedTool>,\n ) {}\n\n /**\n * Connect to every configured server and enumerate its tools. A server that\n * fails to connect or list is skipped with a note in `warnings` — one broken\n * server never sinks the session.\n */\n static async connect(servers: Record<string, McpServerConfig>, connect: McpConnect): Promise<{ toolset: McpToolset; warnings: string[] }> {\n const clients = new Map<string, McpClientLike>();\n const tools = new Map<string, OwnedTool>();\n const warnings: string[] = [];\n for (const [server, config] of Object.entries(servers)) {\n try {\n const client = await connect(server, config);\n const listed = await client.listTools();\n clients.set(server, client);\n for (const t of listed.tools) {\n const name = `${NS}${server}${sep}${t.name}`;\n tools.set(name, {\n server,\n toolName: t.name,\n readOnly: t.annotations?.readOnlyHint === true,\n spec: {\n name,\n description: `[${server}] ${t.description ?? t.name}`,\n parameters: t.inputSchema && typeof t.inputSchema === 'object' ? t.inputSchema : { type: 'object', properties: {} },\n },\n });\n }\n } catch (e) {\n warnings.push(`MCP server \"${server}\" unavailable: ${(e as Error).message}`);\n }\n }\n return { toolset: new McpToolset(clients, tools), warnings };\n }\n\n /** The tool specs to advertise to the model. */\n specs(): ToolSpec[] {\n return [...this.tools.values()].map((t) => t.spec).sort((a, b) => a.name.localeCompare(b.name));\n }\n\n /** Whether a tool call name belongs to this toolset. */\n owns(name: string): boolean {\n return this.tools.has(name);\n }\n\n /**\n * Execute an external tool call. A non-read-only tool is gated through\n * `approve` first (default-deny if declined). Never throws — errors come back\n * as tool content for the model to read.\n */\n async execute(call: ToolCall, approve: (a: MutatingAction) => Promise<boolean>): Promise<ToolResult> {\n const owned = this.tools.get(call.name);\n if (!owned) return { content: `unknown MCP tool ${call.name}`, mutated: false };\n if (!owned.readOnly) {\n if (!(await approve({ kind: 'tool', name: call.name, args: call.arguments }))) {\n return { content: `calling ${call.name} was declined by the user`, mutated: false };\n }\n }\n const client = this.clients.get(owned.server);\n if (!client) return { content: `MCP server ${owned.server} is not connected`, mutated: false };\n try {\n const res = await client.callTool({ name: owned.toolName, arguments: call.arguments });\n const text = (res.content ?? []).map((c) => (c.type === 'text' ? (c.text ?? '') : `[${c.type}]`)).join('\\n') || '(no output)';\n return { content: text.slice(0, 12_000), mutated: !owned.readOnly && !res.isError };\n } catch (e) {\n return { content: `MCP tool ${call.name} failed: ${(e as Error).message}`, mutated: false };\n }\n }\n\n async dispose(): Promise<void> {\n for (const client of this.clients.values()) {\n try {\n await client.close();\n } catch {\n /* best-effort */\n }\n }\n }\n}\n\n/**\n * Default connect using the MCP SDK (loaded lazily). Handles both a local stdio\n * server (`command`) and a remote server (`url`, streamable-HTTP or SSE) — the\n * two shapes the standard config files use.\n */\nexport const defaultMcpConnect: McpConnect = async (name, config) => {\n const { Client } = await import('@modelcontextprotocol/sdk/client/index.js');\n const client = new Client({ name: `vg-code:${name}`, version: '1.0.0' }, { capabilities: {} });\n if (config.url) {\n const url = new URL(config.url);\n const requestInit = config.headers ? { headers: config.headers } : undefined;\n if (config.type === 'sse') {\n const { SSEClientTransport } = await import('@modelcontextprotocol/sdk/client/sse.js');\n await client.connect(new SSEClientTransport(url, requestInit ? { requestInit } : undefined));\n } else {\n const { StreamableHTTPClientTransport } = await import('@modelcontextprotocol/sdk/client/streamableHttp.js');\n await client.connect(new StreamableHTTPClientTransport(url, requestInit ? { requestInit } : undefined));\n }\n } else if (config.command) {\n const { StdioClientTransport } = await import('@modelcontextprotocol/sdk/client/stdio.js');\n await client.connect(new StdioClientTransport({ command: config.command, args: config.args ?? [], env: config.env }));\n } else {\n throw new Error('server config has neither a command (stdio) nor a url (remote)');\n }\n return client as unknown as McpClientLike;\n};\n"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { runCoreScan,
|
|
1
|
+
import { runCoreScan, compactUiPurpose } from './chunk-5BDHQ7DD.js';
|
|
2
2
|
import { findPackageJsonFiles, readJsonFile, findFiles, readTextFile, pathExists } from './chunk-GI6W53LM.js';
|
|
3
|
+
import { writeJsonFile } from './chunk-CRAMOEBE.js';
|
|
3
4
|
import * as path3 from 'path';
|
|
4
5
|
import { Command } from 'commander';
|
|
5
6
|
import chalk from 'chalk';
|
|
@@ -4355,5 +4356,5 @@ var baselineCommand = new Command("baseline").description("Create a drift baseli
|
|
|
4355
4356
|
});
|
|
4356
4357
|
|
|
4357
4358
|
export { baselineCommand, loadAdvancedScanHook, runBaseline };
|
|
4358
|
-
//# sourceMappingURL=chunk-
|
|
4359
|
-
//# sourceMappingURL=chunk-
|
|
4359
|
+
//# sourceMappingURL=chunk-NONYJLOJ.js.map
|
|
4360
|
+
//# sourceMappingURL=chunk-NONYJLOJ.js.map
|