veefore 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/veefore.mjs +574 -147
- package/package.json +1 -1
package/dist/veefore.mjs
CHANGED
|
@@ -1030,6 +1030,97 @@ async function select(question, real, settings = {}) {
|
|
|
1030
1030
|
draw();
|
|
1031
1031
|
});
|
|
1032
1032
|
}
|
|
1033
|
+
async function selectMany(question, options, settings = {}) {
|
|
1034
|
+
if (options.length === 0) return [];
|
|
1035
|
+
const ticked = options.map((option) => settings.selected?.(option) ?? true);
|
|
1036
|
+
if (!isInteractive()) return options.filter((_, index) => ticked[index]).map((o) => o.value);
|
|
1037
|
+
flushCapture();
|
|
1038
|
+
let cursor = 0;
|
|
1039
|
+
let rendered = 0;
|
|
1040
|
+
const stdin = process.stdin;
|
|
1041
|
+
const stdout = process.stdout;
|
|
1042
|
+
const labelWidth = Math.max(...options.map((option) => [...option.label].length));
|
|
1043
|
+
const draw = () => {
|
|
1044
|
+
const width = columns();
|
|
1045
|
+
const count = ticked.filter(Boolean).length;
|
|
1046
|
+
const lines = [];
|
|
1047
|
+
lines.push(
|
|
1048
|
+
`${brand("?")} ${style.bold(question)} ${style.grey(`(${count}/${options.length})`)}`
|
|
1049
|
+
);
|
|
1050
|
+
if (settings.help) lines.push(` ${style.grey(settings.help)}`);
|
|
1051
|
+
const room = Math.max(3, (process.stdout.rows || 24) - lines.length - 3);
|
|
1052
|
+
const window = Math.min(options.length, room);
|
|
1053
|
+
let first = Math.max(0, Math.min(cursor - (window >> 1), options.length - window));
|
|
1054
|
+
if (window === options.length) first = 0;
|
|
1055
|
+
if (first > 0) lines.push(style.grey(` \u2191 ${first} more`));
|
|
1056
|
+
for (let index = first; index < first + window; index += 1) {
|
|
1057
|
+
const option = options[index];
|
|
1058
|
+
const active = index === cursor;
|
|
1059
|
+
const pointer = active ? brand("\u276F") : " ";
|
|
1060
|
+
const box = ticked[index] ? style.green("\u25C9") : style.grey("\u25EF");
|
|
1061
|
+
const text = option.hint ? option.label.padEnd(labelWidth) : option.label;
|
|
1062
|
+
const hint = option.hint ? ` ${style.grey(option.hint)}` : "";
|
|
1063
|
+
const label = active ? brand(text) : ticked[index] ? text : style.grey(text);
|
|
1064
|
+
lines.push(fit(`${pointer} ${box} ${label}${hint}`, width - 1));
|
|
1065
|
+
}
|
|
1066
|
+
const rest = options.length - (first + window);
|
|
1067
|
+
if (rest > 0) lines.push(style.grey(` \u2193 ${rest} more`));
|
|
1068
|
+
lines.push(style.grey(" \u2191\u2193 move \xB7 space toggle \xB7 a all/none \xB7 enter confirm \xB7 esc cancel"));
|
|
1069
|
+
if (rendered > 0) stdout.write(`\x1B[${rendered}A\r\x1B[J`);
|
|
1070
|
+
stdout.write(`${lines.join("\n")}
|
|
1071
|
+
`);
|
|
1072
|
+
rendered = lines.length;
|
|
1073
|
+
};
|
|
1074
|
+
return new Promise((resolve, reject) => {
|
|
1075
|
+
const wasRaw = stdin.isRaw;
|
|
1076
|
+
const restoreCursor = () => {
|
|
1077
|
+
if (!inApp()) stdout.write("\x1B[?25h");
|
|
1078
|
+
};
|
|
1079
|
+
const onResize = () => draw();
|
|
1080
|
+
const finish = (confirmed) => {
|
|
1081
|
+
stdin.off("keypress", onKey);
|
|
1082
|
+
stdin.setRawMode(wasRaw);
|
|
1083
|
+
stdin.pause();
|
|
1084
|
+
process.stdout.off("resize", onResize);
|
|
1085
|
+
process.off("exit", restoreCursor);
|
|
1086
|
+
if (rendered > 0) stdout.write(`\x1B[${rendered}A\r\x1B[J`);
|
|
1087
|
+
restoreCursor();
|
|
1088
|
+
if (!confirmed) {
|
|
1089
|
+
stdout.write(`${style.grey("\u2717")} ${question} ${style.grey("cancelled")}
|
|
1090
|
+
`);
|
|
1091
|
+
reject(new MenuCancelled());
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
const chosen = options.filter((_, index) => ticked[index]);
|
|
1095
|
+
const summary = chosen.length === options.length ? `all ${options.length}` : chosen.length === 0 ? "none" : `${chosen.length} of ${options.length}`;
|
|
1096
|
+
stdout.write(`${style.green("\u2713")} ${question} ${brand(summary)}
|
|
1097
|
+
`);
|
|
1098
|
+
resolve(chosen.map((option) => option.value));
|
|
1099
|
+
};
|
|
1100
|
+
const onKey = (input, key = {}) => {
|
|
1101
|
+
if (key.ctrl && key.name === "c" || key.name === "escape") return finish(false);
|
|
1102
|
+
if (key.name === "return" || key.name === "enter") return finish(true);
|
|
1103
|
+
if (key.name === "space") ticked[cursor] = !ticked[cursor];
|
|
1104
|
+
else if (input === "a" || input === "A") {
|
|
1105
|
+
const all = ticked.every(Boolean);
|
|
1106
|
+
ticked.fill(!all);
|
|
1107
|
+
} else if (key.name === "up" || key.name === "k") {
|
|
1108
|
+
cursor = (cursor - 1 + options.length) % options.length;
|
|
1109
|
+
} else if (key.name === "down" || key.name === "j" || key.name === "tab") {
|
|
1110
|
+
cursor = (cursor + 1) % options.length;
|
|
1111
|
+
} else return;
|
|
1112
|
+
draw();
|
|
1113
|
+
};
|
|
1114
|
+
process.once("exit", restoreCursor);
|
|
1115
|
+
emitKeypressEvents(stdin);
|
|
1116
|
+
stdin.setRawMode(true);
|
|
1117
|
+
stdin.resume();
|
|
1118
|
+
stdout.write("\x1B[?25l");
|
|
1119
|
+
stdin.on("keypress", onKey);
|
|
1120
|
+
process.stdout.on("resize", onResize);
|
|
1121
|
+
draw();
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1033
1124
|
async function selectYesNo(question, defaultYes = false) {
|
|
1034
1125
|
return select(
|
|
1035
1126
|
question,
|
|
@@ -4258,6 +4349,9 @@ function findDispatchGuards(sources) {
|
|
|
4258
4349
|
const dynamic = /(?<![\w$.])[A-Za-z_$]*[gG]uard\(\s*(?!['"`])[A-Za-z_$][\w$.]*(?:\[[^\]\n]{1,40}\])?\s*[,)]/;
|
|
4259
4350
|
return sources.filter(({ text }) => imports.test(text) && dynamic.test(stripDefinitions(text))).map(({ file }) => file);
|
|
4260
4351
|
}
|
|
4352
|
+
function escapeRegex(value) {
|
|
4353
|
+
return value.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
|
|
4354
|
+
}
|
|
4261
4355
|
function stripDefinitions(text) {
|
|
4262
4356
|
return text.replace(/\b(?:function|def)\s+[A-Za-z_$]*[gG]uard\s*\(/g, "");
|
|
4263
4357
|
}
|
|
@@ -4267,6 +4361,11 @@ function toCamel(key) {
|
|
|
4267
4361
|
function findGuardedKeys(sources) {
|
|
4268
4362
|
const found = /* @__PURE__ */ new Map();
|
|
4269
4363
|
const pattern = /(?:\b|@)(?:[A-Za-z_$]*[gG]uard|[A-Za-z_$]*[cC]heckpoint)\(\s*['"]([a-z][a-z0-9_]*)['"]/g;
|
|
4364
|
+
for (const { file, text } of sources) {
|
|
4365
|
+
for (const [key, where] of dynamicCheckpointKeys(text, file)) {
|
|
4366
|
+
if (!found.has(key)) found.set(key, where);
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
4270
4369
|
for (const { file, text } of sources) {
|
|
4271
4370
|
for (const match of text.matchAll(pattern)) {
|
|
4272
4371
|
const key = match[1];
|
|
@@ -4275,6 +4374,24 @@ function findGuardedKeys(sources) {
|
|
|
4275
4374
|
}
|
|
4276
4375
|
return found;
|
|
4277
4376
|
}
|
|
4377
|
+
function dynamicCheckpointKeys(text, file) {
|
|
4378
|
+
const found = [];
|
|
4379
|
+
const calls = /(?:\b)[A-Za-z_$]*[cC]heckpoint\(\s*([A-Za-z_$][\w$]*(?:\s*(?:\?\.|\.)\s*[A-Za-z_$][\w$]*)+)\s*[,)]/g;
|
|
4380
|
+
for (const call of text.matchAll(calls)) {
|
|
4381
|
+
const expression = (call[1] ?? "").replace(/\s+/g, "");
|
|
4382
|
+
if (expression === "") continue;
|
|
4383
|
+
const head = text.slice(Math.max(0, (call.index ?? 0) - 600), call.index ?? 0);
|
|
4384
|
+
const [object, ...rest] = expression.split(".");
|
|
4385
|
+
if (object === void 0 || rest.length === 0) continue;
|
|
4386
|
+
const access = rest.map((part) => `\\s*(?:\\?\\.|\\.)\\s*${escapeRegex(part)}`).join("");
|
|
4387
|
+
const pattern = new RegExp(
|
|
4388
|
+
`(?<![\\w$.])${escapeRegex(object)}${access}\\s*={2,3}\\s*['"\`]([a-z][a-z0-9_]*)['"\`]`,
|
|
4389
|
+
"g"
|
|
4390
|
+
);
|
|
4391
|
+
for (const match of head.matchAll(pattern)) found.push([match[1], file]);
|
|
4392
|
+
}
|
|
4393
|
+
return found;
|
|
4394
|
+
}
|
|
4278
4395
|
var PROVIDER_ENV = {
|
|
4279
4396
|
anthropic: "ANTHROPIC_API_KEY",
|
|
4280
4397
|
openai: "OPENAI_API_KEY"
|
|
@@ -5006,7 +5123,20 @@ var SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
|
5006
5123
|
var SKIP_FILES = /\.(test|spec)\.[cm]?[jt]sx?$|^test_.*\.py$|_test\.py$|\.d\.ts$|\.min\.[cm]?js$/;
|
|
5007
5124
|
var DEFAULT_BUDGET_MS = 6e4;
|
|
5008
5125
|
var MAX_PARSE_BYTES = 4 * 1024 * 1024;
|
|
5126
|
+
var cache = /* @__PURE__ */ new Map();
|
|
5127
|
+
function forgetIndex(root) {
|
|
5128
|
+
if (root === void 0) cache.clear();
|
|
5129
|
+
else cache.delete(path7.resolve(root));
|
|
5130
|
+
}
|
|
5009
5131
|
function indexFiles(root, options = {}) {
|
|
5132
|
+
const key = path7.resolve(root);
|
|
5133
|
+
const hit = cache.get(key);
|
|
5134
|
+
if (hit) return hit;
|
|
5135
|
+
const built = buildIndex(root, options);
|
|
5136
|
+
if (!built.truncated) cache.set(key, built);
|
|
5137
|
+
return built;
|
|
5138
|
+
}
|
|
5139
|
+
function buildIndex(root, options) {
|
|
5010
5140
|
const deadline2 = Date.now() + (options.budgetMs ?? DEFAULT_BUDGET_MS);
|
|
5011
5141
|
const fromGit = gitFiles(root, deadline2);
|
|
5012
5142
|
if (fromGit) {
|
|
@@ -5266,8 +5396,8 @@ function listSources(root = process.cwd()) {
|
|
|
5266
5396
|
const index = indexFiles(root);
|
|
5267
5397
|
return { files: index.files, truncated: index.truncated, method: index.method };
|
|
5268
5398
|
}
|
|
5269
|
-
function discoverAgents(root = process.cwd()) {
|
|
5270
|
-
const scan = scanProject(root);
|
|
5399
|
+
function discoverAgents(root = process.cwd(), reuse) {
|
|
5400
|
+
const scan = reuse ?? scanProject(root);
|
|
5271
5401
|
const index = indexFiles(root);
|
|
5272
5402
|
const byDirectory = /* @__PURE__ */ new Map();
|
|
5273
5403
|
for (const tool of scan.tools) {
|
|
@@ -5391,7 +5521,7 @@ function combineRisk(inferred, declared) {
|
|
|
5391
5521
|
function findJsTools(text) {
|
|
5392
5522
|
const hits = [];
|
|
5393
5523
|
const importsFrom = (pkg) => new RegExp(
|
|
5394
|
-
`from\\s+['"]${
|
|
5524
|
+
`from\\s+['"]${escapeRegex2(pkg)}(/[^'"]*)?['"]|require\\(\\s*['"]${escapeRegex2(pkg)}`
|
|
5395
5525
|
).test(text);
|
|
5396
5526
|
const vercel = importsFrom("ai");
|
|
5397
5527
|
const langchain = importsFrom("@langchain/core") || importsFrom("langchain");
|
|
@@ -5924,7 +6054,7 @@ function push(map, key, value) {
|
|
|
5924
6054
|
function unique(values) {
|
|
5925
6055
|
return [...new Set(values)];
|
|
5926
6056
|
}
|
|
5927
|
-
function
|
|
6057
|
+
function escapeRegex2(value) {
|
|
5928
6058
|
return value.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
|
|
5929
6059
|
}
|
|
5930
6060
|
|
|
@@ -6528,6 +6658,185 @@ function guardDispatch(source, fn, typescript) {
|
|
|
6528
6658
|
return { after, at: target.start };
|
|
6529
6659
|
}
|
|
6530
6660
|
var CHECKPOINT_ARG_PROPERTIES = ["args", "arguments", "params", "parameters", "input", "payload"];
|
|
6661
|
+
var CONTROL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
6662
|
+
"if",
|
|
6663
|
+
"else",
|
|
6664
|
+
"for",
|
|
6665
|
+
"while",
|
|
6666
|
+
"do",
|
|
6667
|
+
"switch",
|
|
6668
|
+
"catch",
|
|
6669
|
+
"with",
|
|
6670
|
+
"return",
|
|
6671
|
+
"typeof",
|
|
6672
|
+
"await",
|
|
6673
|
+
"new",
|
|
6674
|
+
"throw",
|
|
6675
|
+
"case",
|
|
6676
|
+
"function",
|
|
6677
|
+
"delete",
|
|
6678
|
+
"void",
|
|
6679
|
+
"in",
|
|
6680
|
+
"of",
|
|
6681
|
+
"yield"
|
|
6682
|
+
]);
|
|
6683
|
+
var BRANCH_KEYWORDS = /* @__PURE__ */ new Set(["if", "for", "while"]);
|
|
6684
|
+
function readStructure(source) {
|
|
6685
|
+
const parens = [];
|
|
6686
|
+
const blocks = [];
|
|
6687
|
+
const parenStack = [];
|
|
6688
|
+
const braceStack = [];
|
|
6689
|
+
const closedAt = /* @__PURE__ */ new Map();
|
|
6690
|
+
let awaitingBody = null;
|
|
6691
|
+
let previous = "";
|
|
6692
|
+
let index = 0;
|
|
6693
|
+
while (index < source.length) {
|
|
6694
|
+
const char = source[index];
|
|
6695
|
+
const next = source[index + 1];
|
|
6696
|
+
if (char === "/" && next === "/") {
|
|
6697
|
+
const stop = source.indexOf("\n", index);
|
|
6698
|
+
index = stop < 0 ? source.length : stop;
|
|
6699
|
+
continue;
|
|
6700
|
+
}
|
|
6701
|
+
if (char === "/" && next === "*") {
|
|
6702
|
+
const stop = source.indexOf("*/", index + 2);
|
|
6703
|
+
index = stop < 0 ? source.length : stop + 2;
|
|
6704
|
+
continue;
|
|
6705
|
+
}
|
|
6706
|
+
if (char === '"' || char === "'") {
|
|
6707
|
+
const stop = skipString(source, index);
|
|
6708
|
+
index = stop < 0 ? nextLine(source, index) : stop;
|
|
6709
|
+
previous = "a";
|
|
6710
|
+
continue;
|
|
6711
|
+
}
|
|
6712
|
+
if (char === "`") {
|
|
6713
|
+
const stop = skipTemplate(source, index);
|
|
6714
|
+
index = stop < 0 ? nextLine(source, index) : stop;
|
|
6715
|
+
previous = "a";
|
|
6716
|
+
continue;
|
|
6717
|
+
}
|
|
6718
|
+
if (char === "/" && regexAllowed(source, index, previous)) {
|
|
6719
|
+
const stop = skipRegex(source, index);
|
|
6720
|
+
index = stop < 0 ? nextLine(source, index) : stop;
|
|
6721
|
+
previous = "a";
|
|
6722
|
+
continue;
|
|
6723
|
+
}
|
|
6724
|
+
if (char === "(") parenStack.push(index);
|
|
6725
|
+
else if (char === ")") {
|
|
6726
|
+
const open = parenStack.pop();
|
|
6727
|
+
if (open !== void 0) {
|
|
6728
|
+
const head = headBefore(source, open);
|
|
6729
|
+
const group = { open, close: index, name: head.name, async: head.async };
|
|
6730
|
+
parens.push(group);
|
|
6731
|
+
closedAt.set(index, group);
|
|
6732
|
+
if (!CONTROL_KEYWORDS.has(group.name) && /^\s*:/.test(source.slice(index + 1, index + 40))) {
|
|
6733
|
+
awaitingBody = { async: group.async, depth: braceStack.length, params: group };
|
|
6734
|
+
}
|
|
6735
|
+
}
|
|
6736
|
+
} else if (char === ";" || char === "=") {
|
|
6737
|
+
if (awaitingBody && braceStack.length === awaitingBody.depth) awaitingBody = null;
|
|
6738
|
+
} else if (char === "{") {
|
|
6739
|
+
const kind = bodyKind(source, index, closedAt, awaitingBody);
|
|
6740
|
+
if (kind.fn) awaitingBody = null;
|
|
6741
|
+
braceStack.push({ open: index, ...kind });
|
|
6742
|
+
} else if (char === "}") {
|
|
6743
|
+
const open = braceStack.pop();
|
|
6744
|
+
if (open !== void 0) blocks.push({ ...open, close: index });
|
|
6745
|
+
}
|
|
6746
|
+
if (!/\s/.test(char)) previous = char;
|
|
6747
|
+
index += 1;
|
|
6748
|
+
}
|
|
6749
|
+
for (const open of braceStack) blocks.push({ ...open, close: source.length });
|
|
6750
|
+
return { parens, blocks };
|
|
6751
|
+
}
|
|
6752
|
+
function nextLine(source, from) {
|
|
6753
|
+
const stop = source.indexOf("\n", from);
|
|
6754
|
+
return stop < 0 ? source.length : stop + 1;
|
|
6755
|
+
}
|
|
6756
|
+
function bodyKind(source, index, closedAt, awaitingBody) {
|
|
6757
|
+
const before = significantBefore(source, index);
|
|
6758
|
+
if (before.text === "=>") {
|
|
6759
|
+
let cursor = before.at - 1;
|
|
6760
|
+
while (cursor >= 0 && /\s/.test(source[cursor])) cursor -= 1;
|
|
6761
|
+
const group = source[cursor] === ")" ? closedAt.get(cursor) : void 0;
|
|
6762
|
+
if (group) return { fn: true, async: group.async, params: group };
|
|
6763
|
+
return { fn: true, async: asyncBefore(source, cursor + 1), params: null };
|
|
6764
|
+
}
|
|
6765
|
+
if (before.text === ")") {
|
|
6766
|
+
const group = closedAt.get(before.at);
|
|
6767
|
+
if (!group) return { fn: false, async: false, params: null };
|
|
6768
|
+
if (CONTROL_KEYWORDS.has(group.name)) return { fn: false, async: false, params: null };
|
|
6769
|
+
return { fn: true, async: group.async, params: group };
|
|
6770
|
+
}
|
|
6771
|
+
if (awaitingBody && /[>\])\w$]/.test(before.text)) {
|
|
6772
|
+
return { fn: true, async: awaitingBody.async, params: awaitingBody.params };
|
|
6773
|
+
}
|
|
6774
|
+
return { fn: false, async: false, params: null };
|
|
6775
|
+
}
|
|
6776
|
+
function significantBefore(source, index) {
|
|
6777
|
+
let cursor = index - 1;
|
|
6778
|
+
for (; ; ) {
|
|
6779
|
+
while (cursor >= 0 && /\s/.test(source[cursor])) cursor -= 1;
|
|
6780
|
+
if (cursor >= 1 && source[cursor] === "/" && source[cursor - 1] === "*") {
|
|
6781
|
+
const open = source.lastIndexOf("/*", cursor - 1);
|
|
6782
|
+
if (open < 0) break;
|
|
6783
|
+
cursor = open - 1;
|
|
6784
|
+
continue;
|
|
6785
|
+
}
|
|
6786
|
+
break;
|
|
6787
|
+
}
|
|
6788
|
+
if (cursor < 0) return { text: "", at: 0 };
|
|
6789
|
+
if (cursor >= 1 && source[cursor] === ">" && source[cursor - 1] === "=") {
|
|
6790
|
+
return { text: "=>", at: cursor - 1 };
|
|
6791
|
+
}
|
|
6792
|
+
return { text: source[cursor], at: cursor };
|
|
6793
|
+
}
|
|
6794
|
+
function headBefore(source, parenOpen) {
|
|
6795
|
+
let cursor = parenOpen - 1;
|
|
6796
|
+
while (cursor >= 0 && /\s/.test(source[cursor])) cursor -= 1;
|
|
6797
|
+
let end = cursor + 1;
|
|
6798
|
+
while (cursor >= 0 && /[\w$]/.test(source[cursor])) cursor -= 1;
|
|
6799
|
+
const name = source.slice(cursor + 1, end);
|
|
6800
|
+
let before = cursor;
|
|
6801
|
+
if (name === "") {
|
|
6802
|
+
return { name: "", async: asyncBefore(source, parenOpen) };
|
|
6803
|
+
}
|
|
6804
|
+
if (name === "async") return { name: "", async: true };
|
|
6805
|
+
while (before >= 0 && /[\s*]/.test(source[before])) before -= 1;
|
|
6806
|
+
end = before + 1;
|
|
6807
|
+
while (before >= 0 && /[\w$]/.test(source[before])) before -= 1;
|
|
6808
|
+
const keyword = source.slice(before + 1, end);
|
|
6809
|
+
if (keyword === "function") return { name, async: asyncBefore(source, before + 1) };
|
|
6810
|
+
return { name, async: asyncBefore(source, cursor + 1) };
|
|
6811
|
+
}
|
|
6812
|
+
function asyncBefore(source, at) {
|
|
6813
|
+
let cursor = at - 1;
|
|
6814
|
+
while (cursor >= 0 && /[\s*]/.test(source[cursor])) cursor -= 1;
|
|
6815
|
+
const end = cursor + 1;
|
|
6816
|
+
while (cursor >= 0 && /[\w$]/.test(source[cursor])) cursor -= 1;
|
|
6817
|
+
return source.slice(cursor + 1, end) === "async";
|
|
6818
|
+
}
|
|
6819
|
+
function innermost(list, at) {
|
|
6820
|
+
return enclosing(list, at)[0] ?? null;
|
|
6821
|
+
}
|
|
6822
|
+
function enclosing(list, at) {
|
|
6823
|
+
return list.filter((item) => at > item.open && at <= item.close).sort((a, b) => b.open - a.open);
|
|
6824
|
+
}
|
|
6825
|
+
function enclosingArguments(source, structure, at) {
|
|
6826
|
+
const body = innermost(
|
|
6827
|
+
structure.blocks.filter((block) => block.fn && block.params !== null),
|
|
6828
|
+
at
|
|
6829
|
+
);
|
|
6830
|
+
if (!body?.params) return null;
|
|
6831
|
+
return parameterExpression(source.slice(body.params.open + 1, body.params.close));
|
|
6832
|
+
}
|
|
6833
|
+
function awaitAllowed(structure, at) {
|
|
6834
|
+
const body = innermost(
|
|
6835
|
+
structure.blocks.filter((block) => block.fn),
|
|
6836
|
+
at
|
|
6837
|
+
);
|
|
6838
|
+
return body ? body.async : null;
|
|
6839
|
+
}
|
|
6531
6840
|
function planCheckpoints(files, read, toolKeys) {
|
|
6532
6841
|
const plans = [];
|
|
6533
6842
|
const outstanding = new Set(toolKeys);
|
|
@@ -6541,24 +6850,41 @@ function planCheckpoints(files, read, toolKeys) {
|
|
|
6541
6850
|
(key) => source.includes(key) || source.includes(camelOf(key))
|
|
6542
6851
|
);
|
|
6543
6852
|
if (present.length === 0) continue;
|
|
6544
|
-
const
|
|
6853
|
+
const structure = readStructure(source);
|
|
6545
6854
|
const lineOf = lineLocator2(source);
|
|
6546
6855
|
const alias = chooseAliasFor(source, "checkpoint", "js");
|
|
6547
|
-
const
|
|
6548
|
-
const placed = [];
|
|
6856
|
+
const sites = /* @__PURE__ */ new Map();
|
|
6549
6857
|
for (const key of present) {
|
|
6550
|
-
const site = checkpointSite(source, key,
|
|
6858
|
+
const site = checkpointSite(source, key, structure);
|
|
6551
6859
|
if (!site) continue;
|
|
6552
|
-
const
|
|
6860
|
+
const existing = sites.get(site.at);
|
|
6861
|
+
if (existing) existing.keys.push(key);
|
|
6862
|
+
else sites.set(site.at, { keys: [key], site });
|
|
6863
|
+
}
|
|
6864
|
+
if (sites.size === 0) continue;
|
|
6865
|
+
const edits = [];
|
|
6866
|
+
const placed = [];
|
|
6867
|
+
for (const [at, { keys, site }] of sites) {
|
|
6868
|
+
if (/^\s*(?:await\s+)?[A-Za-z_$]*[cC]heckpoint\(/.test(source.slice(at, at + 200))) continue;
|
|
6869
|
+
const many = keys.length > 1;
|
|
6870
|
+
const keyExpression = many && site.keyExpression ? site.keyExpression : quote(keys[0] ?? "");
|
|
6871
|
+
if (many && !site.keyExpression) continue;
|
|
6553
6872
|
const args = site.argsExpression ? `, ${site.argsExpression}` : "";
|
|
6554
6873
|
edits.push({
|
|
6555
|
-
start:
|
|
6556
|
-
end:
|
|
6874
|
+
start: at,
|
|
6875
|
+
end: at,
|
|
6557
6876
|
text: `
|
|
6558
|
-
${
|
|
6877
|
+
${blockIndent(source, at)}await ${alias}(${keyExpression}${args});`
|
|
6559
6878
|
});
|
|
6560
|
-
|
|
6561
|
-
|
|
6879
|
+
for (const key of keys) {
|
|
6880
|
+
placed.push({
|
|
6881
|
+
key,
|
|
6882
|
+
line: lineOf(at),
|
|
6883
|
+
shape: site.shape,
|
|
6884
|
+
...many ? { dynamic: true } : {}
|
|
6885
|
+
});
|
|
6886
|
+
outstanding.delete(key);
|
|
6887
|
+
}
|
|
6562
6888
|
}
|
|
6563
6889
|
if (edits.length === 0) continue;
|
|
6564
6890
|
const after = addJsSdkImport(applyEdits(source, edits), "checkpoint", alias);
|
|
@@ -6566,30 +6892,43 @@ ${indent}await ${alias}(${quote(key)}${args});`
|
|
|
6566
6892
|
}
|
|
6567
6893
|
return plans;
|
|
6568
6894
|
}
|
|
6569
|
-
function checkpointSite(source, key,
|
|
6895
|
+
function checkpointSite(source, key, structure) {
|
|
6570
6896
|
const quoted = `['"\`]${escapeForRegex(key)}['"\`]`;
|
|
6571
|
-
const usable = (at) =>
|
|
6897
|
+
const usable = (at) => awaitAllowed(structure, at) === true;
|
|
6898
|
+
const bodyAfter = (close) => {
|
|
6899
|
+
const brace = /^\s*\{/.exec(source.slice(close + 1, close + 200));
|
|
6900
|
+
return brace ? close + 1 + brace[0].length : null;
|
|
6901
|
+
};
|
|
6572
6902
|
for (const match of source.matchAll(new RegExp(`\\bcase\\s+${quoted}\\s*:`, "g"))) {
|
|
6573
6903
|
const colon = (match.index ?? 0) + match[0].length;
|
|
6574
6904
|
const braced = /^\s*\{/.exec(source.slice(colon));
|
|
6575
6905
|
const at = braced ? colon + braced[0].length : colon;
|
|
6576
6906
|
if (!usable(at)) continue;
|
|
6577
|
-
|
|
6578
|
-
}
|
|
6579
|
-
const comparison = new RegExp(
|
|
6580
|
-
`(?:if|else\\s+if)\\s*\\(([^()]{0,200}?)${quoted}([^()]{0,200}?)\\)\\s*\\{`,
|
|
6581
|
-
"g"
|
|
6582
|
-
);
|
|
6583
|
-
for (const match of source.matchAll(comparison)) {
|
|
6584
|
-
const left = match[1] ?? "";
|
|
6585
|
-
if (!/(?<![!<>])={2,3}\s*$/.test(left)) continue;
|
|
6586
|
-
const at = (match.index ?? 0) + match[0].length;
|
|
6587
|
-
if (!usable(at)) continue;
|
|
6588
|
-
const object = /([A-Za-z_$][\w$]*)\s*(?:\?\.|\.)\s*[A-Za-z_$][\w$]*\s*={2,3}\s*$/.exec(left);
|
|
6907
|
+
const discriminant = switchKey(source, match.index ?? 0);
|
|
6589
6908
|
return {
|
|
6590
6909
|
at,
|
|
6591
|
-
shape: "
|
|
6592
|
-
argsExpression:
|
|
6910
|
+
shape: "case",
|
|
6911
|
+
argsExpression: discriminant ? argumentsOf(source, discriminant.object) : null,
|
|
6912
|
+
keyExpression: discriminant?.expression ?? null
|
|
6913
|
+
};
|
|
6914
|
+
}
|
|
6915
|
+
for (const match of source.matchAll(new RegExp(quoted, "g"))) {
|
|
6916
|
+
const at = match.index ?? 0;
|
|
6917
|
+
if (!positiveComparison(source, at)) continue;
|
|
6918
|
+
const group = enclosing(structure.parens, at).find(
|
|
6919
|
+
(candidate) => BRANCH_KEYWORDS.has(candidate.name)
|
|
6920
|
+
);
|
|
6921
|
+
if (!group) continue;
|
|
6922
|
+
const insertAt = bodyAfter(group.close);
|
|
6923
|
+
if (insertAt === null || !usable(insertAt)) continue;
|
|
6924
|
+
const compared = comparedExpression(source, at);
|
|
6925
|
+
const loop = group.name === "for" ? loopVariable(source, group.open) : null;
|
|
6926
|
+
const subject = loop ?? compared?.object ?? null;
|
|
6927
|
+
return {
|
|
6928
|
+
at: insertAt,
|
|
6929
|
+
shape: group.name === "if" ? "branch" : "loop",
|
|
6930
|
+
argsExpression: subject ? argumentsOf(source, subject) : enclosingArguments(source, structure, insertAt),
|
|
6931
|
+
keyExpression: loop ? `${loop}.name` : compared?.expression ?? null
|
|
6593
6932
|
};
|
|
6594
6933
|
}
|
|
6595
6934
|
const declaration = new RegExp(
|
|
@@ -6599,18 +6938,23 @@ function checkpointSite(source, key, scopes) {
|
|
|
6599
6938
|
for (const match of source.matchAll(declaration)) {
|
|
6600
6939
|
const name = match[1];
|
|
6601
6940
|
const end = (match.index ?? 0) + match[0].length;
|
|
6602
|
-
const
|
|
6603
|
-
|
|
6941
|
+
const block = innermost(structure.blocks, end);
|
|
6942
|
+
const limit = block ? block.close : source.length;
|
|
6943
|
+
const head = new RegExp(`\\bif\\s*\\(\\s*${escapeForRegex(name)}\\b`).exec(
|
|
6944
|
+
source.slice(end, limit)
|
|
6604
6945
|
);
|
|
6605
6946
|
if (!head) continue;
|
|
6606
|
-
const parenOpen = end + head[0].
|
|
6607
|
-
const
|
|
6608
|
-
if (
|
|
6609
|
-
const
|
|
6610
|
-
if (!
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6947
|
+
const parenOpen = end + (head.index ?? 0) + head[0].indexOf("(");
|
|
6948
|
+
const group = structure.parens.find((candidate) => candidate.open === parenOpen);
|
|
6949
|
+
if (!group) continue;
|
|
6950
|
+
const insertAt = bodyAfter(group.close);
|
|
6951
|
+
if (insertAt === null || !usable(insertAt)) continue;
|
|
6952
|
+
return {
|
|
6953
|
+
at: insertAt,
|
|
6954
|
+
shape: "found",
|
|
6955
|
+
argsExpression: argumentsOf(source, name),
|
|
6956
|
+
keyExpression: `${name}.name`
|
|
6957
|
+
};
|
|
6614
6958
|
}
|
|
6615
6959
|
const handler = new RegExp(
|
|
6616
6960
|
`${quoted}\\s*:\\s*(?:async\\s*)?\\(([^)]{0,200})\\)\\s*(?::[^=>{]{0,120})?=>\\s*\\{`,
|
|
@@ -6619,28 +6963,55 @@ function checkpointSite(source, key, scopes) {
|
|
|
6619
6963
|
for (const match of source.matchAll(handler)) {
|
|
6620
6964
|
const at = (match.index ?? 0) + match[0].length;
|
|
6621
6965
|
if (!usable(at)) continue;
|
|
6622
|
-
return {
|
|
6966
|
+
return {
|
|
6967
|
+
at,
|
|
6968
|
+
shape: "handler",
|
|
6969
|
+
argsExpression: parameterExpression(match[1] ?? ""),
|
|
6970
|
+
keyExpression: null
|
|
6971
|
+
};
|
|
6623
6972
|
}
|
|
6624
|
-
const camel = camelOf(key);
|
|
6625
6973
|
const named = new RegExp(
|
|
6626
|
-
`\\basync\\s+function\\s+(?:${escapeForRegex(
|
|
6974
|
+
`\\basync\\s+function\\s+(?:${escapeForRegex(camelOf(key))}|${escapeForRegex(key)})\\s*\\(`,
|
|
6627
6975
|
"g"
|
|
6628
6976
|
);
|
|
6629
6977
|
for (const match of source.matchAll(named)) {
|
|
6630
6978
|
const parenOpen = (match.index ?? 0) + match[0].length - 1;
|
|
6631
|
-
const
|
|
6632
|
-
if (
|
|
6633
|
-
const
|
|
6634
|
-
if (
|
|
6635
|
-
if (!/^\s*(?::[^{;]*)?$/.test(source.slice(parenClose + 1, bodyOpen))) continue;
|
|
6979
|
+
const group = structure.parens.find((candidate) => candidate.open === parenOpen);
|
|
6980
|
+
if (!group) continue;
|
|
6981
|
+
const body = structure.blocks.filter((block) => block.fn && block.open > group.close).sort((a, b) => a.open - b.open)[0];
|
|
6982
|
+
if (!body) continue;
|
|
6636
6983
|
return {
|
|
6637
|
-
at:
|
|
6984
|
+
at: body.open + 1,
|
|
6638
6985
|
shape: "function",
|
|
6639
|
-
argsExpression: parameterExpression(source.slice(
|
|
6986
|
+
argsExpression: parameterExpression(source.slice(group.open + 1, group.close)),
|
|
6987
|
+
keyExpression: null
|
|
6640
6988
|
};
|
|
6641
6989
|
}
|
|
6642
6990
|
return null;
|
|
6643
6991
|
}
|
|
6992
|
+
function positiveComparison(source, at) {
|
|
6993
|
+
const before = source.slice(Math.max(0, at - 96), at);
|
|
6994
|
+
return /(?<![!<>])={2,3}\s*$/.test(before);
|
|
6995
|
+
}
|
|
6996
|
+
function comparedExpression(source, at) {
|
|
6997
|
+
const before = source.slice(Math.max(0, at - 160), at);
|
|
6998
|
+
const match = /([A-Za-z_$][\w$]*)\s*(?:\?\.|\.)\s*([A-Za-z_$][\w$]*)\s*={2,3}\s*$/.exec(before);
|
|
6999
|
+
if (!match) return null;
|
|
7000
|
+
return { object: match[1], expression: `${match[1]}.${match[2]}` };
|
|
7001
|
+
}
|
|
7002
|
+
function switchKey(source, at) {
|
|
7003
|
+
const before = source.slice(Math.max(0, at - 4e3), at);
|
|
7004
|
+
const found = [
|
|
7005
|
+
...before.matchAll(/\bswitch\s*\(\s*([A-Za-z_$][\w$]*)\s*(?:\?\.|\.)\s*([A-Za-z_$][\w$]*)/g)
|
|
7006
|
+
];
|
|
7007
|
+
const last = found[found.length - 1];
|
|
7008
|
+
return last ? { object: last[1], expression: `${last[1]}.${last[2]}` } : null;
|
|
7009
|
+
}
|
|
7010
|
+
function loopVariable(source, parenOpen) {
|
|
7011
|
+
return /^\s*\(\s*(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s+of\b/.exec(
|
|
7012
|
+
source.slice(parenOpen, parenOpen + 120)
|
|
7013
|
+
)?.[1] ?? null;
|
|
7014
|
+
}
|
|
6644
7015
|
function parameterExpression(parameters) {
|
|
6645
7016
|
const names = parameters.split(",").map((part) => /^\s*(?:\.\.\.)?([A-Za-z_$][\w$]*)/.exec(part)?.[1]).filter((name) => name !== void 0);
|
|
6646
7017
|
const named = names.find((name) => CHECKPOINT_ARG_PROPERTIES.includes(name));
|
|
@@ -6650,14 +7021,6 @@ function parameterExpression(parameters) {
|
|
|
6650
7021
|
function camelOf(key) {
|
|
6651
7022
|
return key.replace(/_([a-z0-9])/g, (_match, char) => char.toUpperCase());
|
|
6652
7023
|
}
|
|
6653
|
-
function switchArguments(source, at) {
|
|
6654
|
-
const before = source.slice(Math.max(0, at - 4e3), at);
|
|
6655
|
-
const found = [
|
|
6656
|
-
...before.matchAll(/\bswitch\s*\(\s*([A-Za-z_$][\w$]*)\s*(?:\?\.|\.)\s*[A-Za-z_$][\w$]*/g)
|
|
6657
|
-
];
|
|
6658
|
-
const last = found[found.length - 1];
|
|
6659
|
-
return last ? argumentsOf(source, last[1]) : null;
|
|
6660
|
-
}
|
|
6661
7024
|
function argumentsOf(source, object) {
|
|
6662
7025
|
for (const property of CHECKPOINT_ARG_PROPERTIES) {
|
|
6663
7026
|
const pattern = new RegExp(`\\b${escapeForRegex(object)}\\s*(?:\\?\\.|\\.)\\s*${property}\\b`);
|
|
@@ -6675,63 +7038,6 @@ function blockIndent(source, at) {
|
|
|
6675
7038
|
const lineStart = source.lastIndexOf("\n", at) + 1;
|
|
6676
7039
|
return `${/^[ \t]*/.exec(source.slice(lineStart, at))?.[0] ?? ""} `;
|
|
6677
7040
|
}
|
|
6678
|
-
function functionScopes(source) {
|
|
6679
|
-
const scopes = [];
|
|
6680
|
-
const add = (isAsync, parenOpen, arrow) => {
|
|
6681
|
-
const parenClose = matchBracket(source, parenOpen);
|
|
6682
|
-
if (parenClose < 0) return;
|
|
6683
|
-
const bodyOpen = source.indexOf("{", parenClose);
|
|
6684
|
-
if (bodyOpen < 0) return;
|
|
6685
|
-
const between = source.slice(parenClose + 1, bodyOpen);
|
|
6686
|
-
if (arrow !== /=>/.test(between)) return;
|
|
6687
|
-
if (!/^\s*(?::[^{;=]*)?(?:=>\s*)?$/.test(between)) return;
|
|
6688
|
-
const bodyClose = matchBracket(source, bodyOpen);
|
|
6689
|
-
if (bodyClose < 0) return;
|
|
6690
|
-
scopes.push({ bodyOpen, bodyClose, async: isAsync });
|
|
6691
|
-
};
|
|
6692
|
-
for (const match of source.matchAll(/\b(async\s+)?function\s*\*?\s*[A-Za-z_$][\w$]*\s*\(/g)) {
|
|
6693
|
-
add(match[1] !== void 0, (match.index ?? 0) + match[0].length - 1, false);
|
|
6694
|
-
}
|
|
6695
|
-
for (const match of source.matchAll(/(\basync\s*)?\(/g)) {
|
|
6696
|
-
add(match[1] !== void 0, (match.index ?? 0) + match[0].length - 1, true);
|
|
6697
|
-
}
|
|
6698
|
-
const method = /^[ \t]*((?:(?:public|private|protected|static|override|async|get|set)\s+)*)\*?\s*([A-Za-z_$][\w$]*)\s*\(/gm;
|
|
6699
|
-
for (const match of source.matchAll(method)) {
|
|
6700
|
-
if (CONTROL_KEYWORDS.has(match[2])) continue;
|
|
6701
|
-
add(/\basync\b/.test(match[1] ?? ""), (match.index ?? 0) + match[0].length - 1, false);
|
|
6702
|
-
}
|
|
6703
|
-
for (const match of source.matchAll(/\b(async\s+)?([A-Za-z_$][\w$]*)\s*=>\s*\{/g)) {
|
|
6704
|
-
const bodyOpen = (match.index ?? 0) + match[0].length - 1;
|
|
6705
|
-
const bodyClose = matchBracket(source, bodyOpen);
|
|
6706
|
-
if (bodyClose > 0) scopes.push({ bodyOpen, bodyClose, async: match[1] !== void 0 });
|
|
6707
|
-
}
|
|
6708
|
-
return scopes;
|
|
6709
|
-
}
|
|
6710
|
-
var CONTROL_KEYWORDS = /* @__PURE__ */ new Set([
|
|
6711
|
-
"if",
|
|
6712
|
-
"else",
|
|
6713
|
-
"for",
|
|
6714
|
-
"while",
|
|
6715
|
-
"do",
|
|
6716
|
-
"switch",
|
|
6717
|
-
"catch",
|
|
6718
|
-
"with",
|
|
6719
|
-
"return",
|
|
6720
|
-
"typeof",
|
|
6721
|
-
"new",
|
|
6722
|
-
"await",
|
|
6723
|
-
"function",
|
|
6724
|
-
"throw",
|
|
6725
|
-
"case"
|
|
6726
|
-
]);
|
|
6727
|
-
function enclosingAsync(scopes, at) {
|
|
6728
|
-
let best = null;
|
|
6729
|
-
for (const scope of scopes) {
|
|
6730
|
-
if (at <= scope.bodyOpen || at > scope.bodyClose) continue;
|
|
6731
|
-
if (!best || scope.bodyOpen > best.bodyOpen) best = scope;
|
|
6732
|
-
}
|
|
6733
|
-
return best ? best.async : null;
|
|
6734
|
-
}
|
|
6735
7041
|
function unguardJs(source) {
|
|
6736
7042
|
const checkpoints = uncheckpointJs(source);
|
|
6737
7043
|
const imported = jsGuardImport(checkpoints.after);
|
|
@@ -6788,9 +7094,10 @@ function uncheckpointJs(source) {
|
|
|
6788
7094
|
const lineOf = lineLocator2(source);
|
|
6789
7095
|
const leftovers = [];
|
|
6790
7096
|
let removed = 0;
|
|
7097
|
+
const key = `(?:(['"\`])[a-z][a-z0-9_]*\\1|[A-Za-z_$][\\w$]*(?:\\s*(?:\\?\\.|\\.)\\s*[A-Za-z_$][\\w$]*)+)`;
|
|
6791
7098
|
let after = source.replace(
|
|
6792
7099
|
new RegExp(
|
|
6793
|
-
`^[ \\t]*(?:await[ \\t]+)?${escapeForRegex(alias)}\\(\\s
|
|
7100
|
+
`^[ \\t]*(?:await[ \\t]+)?${escapeForRegex(alias)}\\(\\s*${key}[^\\n]*\\)[ \\t]*;?[ \\t]*\\r?\\n`,
|
|
6794
7101
|
"gm"
|
|
6795
7102
|
),
|
|
6796
7103
|
() => {
|
|
@@ -6812,8 +7119,28 @@ function uncheckpointJs(source) {
|
|
|
6812
7119
|
function unguardPython(source) {
|
|
6813
7120
|
const imported = /^from[ \t]+veefore[ \t]+import[ \t]+([^\n]+)$/m.exec(source);
|
|
6814
7121
|
if (!imported) return { after: source, removed: 0, leftovers: [] };
|
|
6815
|
-
const
|
|
6816
|
-
|
|
7122
|
+
const names = (imported[1] ?? "").replace(/[()]/g, "").split(",");
|
|
7123
|
+
const spec = names.map((part) => /^guard(?:\s+as\s+(\w+))?$/.exec(part.trim())).find((found) => found !== null);
|
|
7124
|
+
const gate = names.map((part) => /^checkpoint(?:\s+as\s+(\w+))?$/.exec(part.trim())).find((found) => found !== null);
|
|
7125
|
+
if (!spec && !gate) return { after: source, removed: 0, leftovers: [] };
|
|
7126
|
+
if (!spec) {
|
|
7127
|
+
const alias2 = gate?.[1] ?? "checkpoint";
|
|
7128
|
+
let removed2 = 0;
|
|
7129
|
+
let after2 = source.replace(
|
|
7130
|
+
new RegExp(
|
|
7131
|
+
`^[ \\t]*${escapeForRegex(alias2)}\\(\\s*(['"])[a-z][a-z0-9_]*\\1[^\\n]*\\)[ \\t]*\\r?\\n`,
|
|
7132
|
+
"gm"
|
|
7133
|
+
),
|
|
7134
|
+
() => {
|
|
7135
|
+
removed2 += 1;
|
|
7136
|
+
return "";
|
|
7137
|
+
}
|
|
7138
|
+
);
|
|
7139
|
+
if (!new RegExp(`(?<![\\w.])${escapeForRegex(alias2)}\\s*\\(`).test(after2)) {
|
|
7140
|
+
after2 = removePythonImport(after2, alias2);
|
|
7141
|
+
}
|
|
7142
|
+
return { after: after2, removed: removed2, leftovers: [] };
|
|
7143
|
+
}
|
|
6817
7144
|
const alias = spec[1] ?? "guard";
|
|
6818
7145
|
const lineOf = lineLocator2(source);
|
|
6819
7146
|
let removed = 0;
|
|
@@ -6852,18 +7179,20 @@ function unguardPython(source) {
|
|
|
6852
7179
|
after = applyEdits(after, edits);
|
|
6853
7180
|
removed += edits.length;
|
|
6854
7181
|
const stillUsed = new RegExp(`(?<![\\w.])@?${escapeForRegex(alias)}\\s*\\(`).test(after);
|
|
6855
|
-
if (!stillUsed)
|
|
6856
|
-
after = after.replace(
|
|
6857
|
-
/^from[ \t]+veefore[ \t]+import[ \t]+([^\n]+)\r?\n?/m,
|
|
6858
|
-
(line, names) => {
|
|
6859
|
-
const rest = names.replace(/[()]/g, "").split(",").map((part) => part.trim()).filter((part) => part && !/^guard(?:\s+as\s+\w+)?$/.test(part));
|
|
6860
|
-
return rest.length === 0 ? "" : `from veefore import ${rest.join(", ")}
|
|
6861
|
-
`;
|
|
6862
|
-
}
|
|
6863
|
-
);
|
|
6864
|
-
}
|
|
7182
|
+
if (!stillUsed) after = removePythonImport(after, "guard");
|
|
6865
7183
|
return { after, removed, leftovers };
|
|
6866
7184
|
}
|
|
7185
|
+
function removePythonImport(source, name) {
|
|
7186
|
+
const target = new RegExp(`^${escapeForRegex(name)}(?:\\s+as\\s+\\w+)?$`);
|
|
7187
|
+
return source.replace(
|
|
7188
|
+
/^from[ \t]+veefore[ \t]+import[ \t]+([^\n]+)\r?\n?/m,
|
|
7189
|
+
(_line, names) => {
|
|
7190
|
+
const rest = names.replace(/[()]/g, "").split(",").map((part) => part.trim()).filter((part) => part && !target.test(part));
|
|
7191
|
+
return rest.length === 0 ? "" : `from veefore import ${rest.join(", ")}
|
|
7192
|
+
`;
|
|
7193
|
+
}
|
|
7194
|
+
);
|
|
7195
|
+
}
|
|
6867
7196
|
function arrowExpressionBody(fn) {
|
|
6868
7197
|
let cursor = 0;
|
|
6869
7198
|
if (fn.startsWith("(")) {
|
|
@@ -7107,7 +7436,6 @@ async function integrate(root, toolKeys, options) {
|
|
|
7107
7436
|
const candidates = shortlist(root, index, anchors);
|
|
7108
7437
|
const sourceFiles = candidates.files;
|
|
7109
7438
|
const truncated = index.truncated || candidates.truncated;
|
|
7110
|
-
const plan = planRewrite(root, sourceFiles, toolKeys);
|
|
7111
7439
|
const interactive = options.interactive ?? (process.stdin.isTTY === true && process.stdout.isTTY === true && !options.yes);
|
|
7112
7440
|
if (truncated && !options.quiet) {
|
|
7113
7441
|
out(
|
|
@@ -7118,13 +7446,39 @@ async function integrate(root, toolKeys, options) {
|
|
|
7118
7446
|
const dispatchGuardFiles = findDispatchGuards(sources);
|
|
7119
7447
|
const dispatchGuarded = dispatchGuardFiles.length > 0;
|
|
7120
7448
|
const alreadyGuarded = guardedToolKeys(sources, toolKeys);
|
|
7121
|
-
|
|
7122
|
-
for (const
|
|
7123
|
-
|
|
7124
|
-
change.status
|
|
7125
|
-
|
|
7449
|
+
const reconcile = (result) => {
|
|
7450
|
+
for (const file of result.files) {
|
|
7451
|
+
for (const change of file.changes) {
|
|
7452
|
+
if (change.status === "manual" && alreadyGuarded.has(change.key)) {
|
|
7453
|
+
change.status = "already";
|
|
7454
|
+
delete change.reason;
|
|
7455
|
+
}
|
|
7126
7456
|
}
|
|
7127
7457
|
}
|
|
7458
|
+
return result;
|
|
7459
|
+
};
|
|
7460
|
+
let plan = reconcile(planRewrite(root, sourceFiles, toolKeys));
|
|
7461
|
+
const changeable = [
|
|
7462
|
+
...new Set(
|
|
7463
|
+
plan.files.flatMap(
|
|
7464
|
+
(file) => file.changes.filter((change) => change.status !== "already").map((change) => change.key)
|
|
7465
|
+
)
|
|
7466
|
+
)
|
|
7467
|
+
];
|
|
7468
|
+
const selected = await chooseTools(changeable, plan, {
|
|
7469
|
+
requested: options.tools,
|
|
7470
|
+
interactive,
|
|
7471
|
+
quiet: options.quiet === true
|
|
7472
|
+
});
|
|
7473
|
+
const skipped = changeable.filter((key) => !selected.includes(key));
|
|
7474
|
+
if (skipped.length > 0) {
|
|
7475
|
+
plan = reconcile(
|
|
7476
|
+
planRewrite(
|
|
7477
|
+
root,
|
|
7478
|
+
sourceFiles,
|
|
7479
|
+
toolKeys.filter((key) => !skipped.includes(key))
|
|
7480
|
+
)
|
|
7481
|
+
);
|
|
7128
7482
|
}
|
|
7129
7483
|
let manual = plan.files.flatMap(
|
|
7130
7484
|
(file) => file.changes.filter((change) => change.status === "manual").map((change) => ({
|
|
@@ -7270,7 +7624,15 @@ async function integrate(root, toolKeys, options) {
|
|
|
7270
7624
|
reportSdkReadiness(root, [.../* @__PURE__ */ new Set([...guardedFiles, ...dispatchGuardFiles])]);
|
|
7271
7625
|
}
|
|
7272
7626
|
const unguarded2 = reportProtection(root, sourceFiles, toolKeys, quiet);
|
|
7273
|
-
return {
|
|
7627
|
+
return {
|
|
7628
|
+
status: "nothing-to-do",
|
|
7629
|
+
plan,
|
|
7630
|
+
checks: [],
|
|
7631
|
+
backupId: null,
|
|
7632
|
+
manual,
|
|
7633
|
+
unguarded: unguarded2,
|
|
7634
|
+
skipped
|
|
7635
|
+
};
|
|
7274
7636
|
}
|
|
7275
7637
|
if (options.dryRun) {
|
|
7276
7638
|
out(style.grey("Dry run: nothing was written."));
|
|
@@ -7280,7 +7642,8 @@ async function integrate(root, toolKeys, options) {
|
|
|
7280
7642
|
checks: [],
|
|
7281
7643
|
backupId: null,
|
|
7282
7644
|
manual,
|
|
7283
|
-
unguarded: reportProtection(root, sourceFiles, toolKeys, true)
|
|
7645
|
+
unguarded: reportProtection(root, sourceFiles, toolKeys, true),
|
|
7646
|
+
skipped
|
|
7284
7647
|
};
|
|
7285
7648
|
}
|
|
7286
7649
|
if (!options.quiet) {
|
|
@@ -7301,7 +7664,8 @@ async function integrate(root, toolKeys, options) {
|
|
|
7301
7664
|
checks: [],
|
|
7302
7665
|
backupId: null,
|
|
7303
7666
|
manual,
|
|
7304
|
-
unguarded: reportProtection(root, sourceFiles, toolKeys, true)
|
|
7667
|
+
unguarded: reportProtection(root, sourceFiles, toolKeys, true),
|
|
7668
|
+
skipped
|
|
7305
7669
|
};
|
|
7306
7670
|
}
|
|
7307
7671
|
const jsReady = languages.has("js") && sdkInstalled(root, "js");
|
|
@@ -7318,12 +7682,14 @@ async function integrate(root, toolKeys, options) {
|
|
|
7318
7682
|
checks: [],
|
|
7319
7683
|
backupId: null,
|
|
7320
7684
|
manual,
|
|
7321
|
-
unguarded: reportProtection(root, sourceFiles, toolKeys, true)
|
|
7685
|
+
unguarded: reportProtection(root, sourceFiles, toolKeys, true),
|
|
7686
|
+
skipped
|
|
7322
7687
|
};
|
|
7323
7688
|
}
|
|
7324
7689
|
}
|
|
7325
7690
|
const backup = createBackup(root, toWrite);
|
|
7326
7691
|
for (const file of toWrite) writeFilePreservingMode(path10.join(root, file.file), file.after);
|
|
7692
|
+
forgetIndex(root);
|
|
7327
7693
|
out(
|
|
7328
7694
|
`${symbol.pass} Updated ${toWrite.length} file${toWrite.length === 1 ? "" : "s"} ${style.grey(`(backup ${backup.id})`)}`
|
|
7329
7695
|
);
|
|
@@ -7385,7 +7751,8 @@ async function integrate(root, toolKeys, options) {
|
|
|
7385
7751
|
checks,
|
|
7386
7752
|
backupId: null,
|
|
7387
7753
|
manual,
|
|
7388
|
-
unguarded: reportProtection(root, sourceFiles, toolKeys, true)
|
|
7754
|
+
unguarded: reportProtection(root, sourceFiles, toolKeys, true),
|
|
7755
|
+
skipped
|
|
7389
7756
|
};
|
|
7390
7757
|
}
|
|
7391
7758
|
for (const language of languages) {
|
|
@@ -7395,9 +7762,14 @@ async function integrate(root, toolKeys, options) {
|
|
|
7395
7762
|
);
|
|
7396
7763
|
}
|
|
7397
7764
|
}
|
|
7765
|
+
if (skipped.length > 0 && !options.quiet) {
|
|
7766
|
+
out(
|
|
7767
|
+
`${symbol.info} Left out of this run, as you chose: ${style.bold(skipped.join(", "))} ${style.grey("(no file was changed for these)")}`
|
|
7768
|
+
);
|
|
7769
|
+
}
|
|
7398
7770
|
const unguarded = reportProtection(root, sourceFiles, toolKeys, options.quiet === true);
|
|
7399
7771
|
out(style.grey(" Undo at any time with `veefore undo`."));
|
|
7400
|
-
return { status: "applied", plan, checks, backupId: backup.id, manual, unguarded };
|
|
7772
|
+
return { status: "applied", plan, checks, backupId: backup.id, manual, unguarded, skipped };
|
|
7401
7773
|
}
|
|
7402
7774
|
function reportProtection(root, files, toolKeys, quiet) {
|
|
7403
7775
|
const guarded = guardedToolKeys(readSources(root, files), toolKeys);
|
|
@@ -7560,6 +7932,43 @@ function stillManual(items, plan) {
|
|
|
7560
7932
|
return true;
|
|
7561
7933
|
});
|
|
7562
7934
|
}
|
|
7935
|
+
async function chooseTools(changeable, plan, options) {
|
|
7936
|
+
if (options.requested) {
|
|
7937
|
+
const asked = new Set(options.requested);
|
|
7938
|
+
const unknown = options.requested.filter((key) => !changeable.includes(key));
|
|
7939
|
+
if (unknown.length > 0 && !options.quiet) {
|
|
7940
|
+
out(
|
|
7941
|
+
`${symbol.warn} Nothing to change for: ${unknown.join(", ")} ${style.grey("(already protected, or not found in the code)")}`
|
|
7942
|
+
);
|
|
7943
|
+
}
|
|
7944
|
+
return changeable.filter((key) => asked.has(key));
|
|
7945
|
+
}
|
|
7946
|
+
if (!options.interactive || changeable.length < 2) return changeable;
|
|
7947
|
+
const where = /* @__PURE__ */ new Map();
|
|
7948
|
+
for (const file of plan.files) {
|
|
7949
|
+
for (const change of file.changes) {
|
|
7950
|
+
if (change.status === "already" || where.has(change.key)) continue;
|
|
7951
|
+
where.set(change.key, { line: change.line, file: file.file, status: change.status });
|
|
7952
|
+
}
|
|
7953
|
+
}
|
|
7954
|
+
try {
|
|
7955
|
+
return await selectMany(
|
|
7956
|
+
"Which tools should Veefore decide for?",
|
|
7957
|
+
changeable.map((key) => {
|
|
7958
|
+
const at = where.get(key);
|
|
7959
|
+
return {
|
|
7960
|
+
label: key,
|
|
7961
|
+
value: key,
|
|
7962
|
+
hint: at ? `${at.status === "wrapped" ? "guard here" : "needs a change"} \xB7 ${at.file}:${at.line}` : ""
|
|
7963
|
+
};
|
|
7964
|
+
}),
|
|
7965
|
+
{ help: "All of them unless you say otherwise. Only what is ticked gets changed." }
|
|
7966
|
+
);
|
|
7967
|
+
} catch (error) {
|
|
7968
|
+
if (error instanceof MenuCancelled) return [];
|
|
7969
|
+
throw error;
|
|
7970
|
+
}
|
|
7971
|
+
}
|
|
7563
7972
|
function checkpointUsable(root, quiet) {
|
|
7564
7973
|
const sdks = installedSdks(root).filter((sdk) => sdk.language === "js");
|
|
7565
7974
|
const stale = sdks.filter((sdk) => !sdk.hasCheckpoint);
|
|
@@ -7632,7 +8041,13 @@ function guardedToolKeys(sources, toolKeys) {
|
|
|
7632
8041
|
return new Map([...guarded].filter(([key]) => toolKeys.includes(key)));
|
|
7633
8042
|
}
|
|
7634
8043
|
function checkIntegration(root, toolKeys) {
|
|
7635
|
-
const
|
|
8044
|
+
const index = indexFiles(root);
|
|
8045
|
+
const candidates = shortlist(root, index, [
|
|
8046
|
+
...toolKeyAnchors(toolKeys),
|
|
8047
|
+
...TOOL_ANCHORS,
|
|
8048
|
+
...GUARD_ANCHORS
|
|
8049
|
+
]);
|
|
8050
|
+
const guarded = guardedToolKeys(readSources(root, candidates.files), toolKeys);
|
|
7636
8051
|
heading("Protection check");
|
|
7637
8052
|
for (const key of toolKeys) {
|
|
7638
8053
|
const file = guarded.get(key);
|
|
@@ -7934,10 +8349,20 @@ async function integrateCommand(args, flags) {
|
|
|
7934
8349
|
let assist;
|
|
7935
8350
|
let dryRun = false;
|
|
7936
8351
|
let check = false;
|
|
8352
|
+
let tools;
|
|
7937
8353
|
for (const arg of args) {
|
|
7938
8354
|
if (arg === "--dry-run") dryRun = true;
|
|
7939
8355
|
else if (arg === "--check") check = true;
|
|
7940
|
-
else if (arg
|
|
8356
|
+
else if (arg === "--tools=all") tools = void 0;
|
|
8357
|
+
else if (arg.startsWith("--tools=")) {
|
|
8358
|
+
tools = arg.slice("--tools=".length).split(",").map((key) => key.trim()).filter((key) => key !== "");
|
|
8359
|
+
if (tools.length === 0) {
|
|
8360
|
+
throw new CliError(
|
|
8361
|
+
"`--tools` takes a comma-separated list of tool keys, or `all`.",
|
|
8362
|
+
"For example: --tools=refund_order,adjust_stock"
|
|
8363
|
+
);
|
|
8364
|
+
}
|
|
8365
|
+
} else if (arg.startsWith("--assist=")) {
|
|
7941
8366
|
const value = ASSIST_VALUES[arg.slice("--assist=".length)];
|
|
7942
8367
|
if (!value) {
|
|
7943
8368
|
throw new CliError(
|
|
@@ -7980,7 +8405,8 @@ async function integrateCommand(args, flags) {
|
|
|
7980
8405
|
dryRun,
|
|
7981
8406
|
quiet: flags.json,
|
|
7982
8407
|
assist,
|
|
7983
|
-
api
|
|
8408
|
+
api,
|
|
8409
|
+
...tools ? { tools } : {}
|
|
7984
8410
|
});
|
|
7985
8411
|
if (flags.json) {
|
|
7986
8412
|
out(
|
|
@@ -7992,6 +8418,7 @@ async function integrateCommand(args, flags) {
|
|
|
7992
8418
|
not_found: outcome.plan.notFound,
|
|
7993
8419
|
manual: outcome.manual,
|
|
7994
8420
|
unguarded: outcome.unguarded,
|
|
8421
|
+
skipped: outcome.skipped,
|
|
7995
8422
|
checks: outcome.checks
|
|
7996
8423
|
},
|
|
7997
8424
|
null,
|
|
@@ -8394,7 +8821,7 @@ import { homedir as homedir2 } from "node:os";
|
|
|
8394
8821
|
import path12 from "node:path";
|
|
8395
8822
|
|
|
8396
8823
|
// src/version.ts
|
|
8397
|
-
var CLI_VERSION = "1.
|
|
8824
|
+
var CLI_VERSION = "1.6.0";
|
|
8398
8825
|
|
|
8399
8826
|
// src/panel.ts
|
|
8400
8827
|
function welcomeSource(flags = {}) {
|
|
@@ -8518,12 +8945,12 @@ function printScan(scan) {
|
|
|
8518
8945
|
`${symbol.warn} Your code reads ${service.length} credential${service.length === 1 ? "" : "s"}: ${service.map((s) => s.name).join(", ")} ${style.grey("(names only; values were not read)")}`
|
|
8519
8946
|
);
|
|
8520
8947
|
}
|
|
8521
|
-
printGaps(scan
|
|
8948
|
+
printGaps(scan);
|
|
8522
8949
|
}
|
|
8523
|
-
function printGaps(
|
|
8950
|
+
function printGaps(scan) {
|
|
8524
8951
|
let discovery;
|
|
8525
8952
|
try {
|
|
8526
|
-
discovery = discoverAgents(root);
|
|
8953
|
+
discovery = discoverAgents(scan.root, scan);
|
|
8527
8954
|
} catch {
|
|
8528
8955
|
return;
|
|
8529
8956
|
}
|