eminiar 0.13.1 → 0.13.3
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/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/README.md +9 -6
- package/dist/cli.js +8775 -8325
- package/dist/{hook-async-J3UKDO62.js → hook-async-3TLK23ZY.js} +3 -3
- package/dist/{hook-chunk-BECSRJFD.js → hook-chunk-ETYHCPPH.js} +27 -15
- package/dist/{hook-chunk-RYZAFUTT.js → hook-chunk-MVSMFCKL.js} +2 -2
- package/dist/{hook-chunk-DSLVDGOB.js → hook-chunk-ZLRLOPLR.js} +43 -23
- package/dist/{hook-hooks-IDTGXCFD.js → hook-hooks-5U2AI6W7.js} +4 -4
- package/dist/hook.js +761 -73
- package/dist/server.js +4350 -4048
- package/eminiar.config.schema.json +2 -1
- package/package.json +1 -1
package/dist/hook.js
CHANGED
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
swarmSubagentStop,
|
|
36
36
|
swarmTouch,
|
|
37
37
|
workerLacksTool
|
|
38
|
-
} from "./hook-chunk-
|
|
38
|
+
} from "./hook-chunk-ZLRLOPLR.js";
|
|
39
39
|
import {
|
|
40
40
|
GUIDE_ORDER,
|
|
41
41
|
classifyAssistant,
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
projectDir,
|
|
53
53
|
setStorageBusyTimeout,
|
|
54
54
|
withPriority
|
|
55
|
-
} from "./hook-chunk-
|
|
55
|
+
} from "./hook-chunk-ETYHCPPH.js";
|
|
56
56
|
|
|
57
57
|
// src/hook.ts
|
|
58
58
|
init_esm_shims();
|
|
@@ -1901,7 +1901,7 @@ var userPromptSubmit = async (input) => {
|
|
|
1901
1901
|
let systemMessage;
|
|
1902
1902
|
if (ctx.config.adjudicator.enabled && input.client !== "cursor" && hasBudget(ADJUDICATOR_NEEDS_MS)) {
|
|
1903
1903
|
try {
|
|
1904
|
-
const { onPrompt } = await import("./hook-hooks-
|
|
1904
|
+
const { onPrompt } = await import("./hook-hooks-5U2AI6W7.js");
|
|
1905
1905
|
const a = await onPrompt(ctx, db, input, sessionId);
|
|
1906
1906
|
if (a?.context) parts.push(a.context);
|
|
1907
1907
|
systemMessage = a?.systemMessage;
|
|
@@ -1959,7 +1959,7 @@ var postToolUse = async (input) => {
|
|
|
1959
1959
|
const ctx = getContext(input.cwd);
|
|
1960
1960
|
if (ctx.config.adjudicator.enabled) {
|
|
1961
1961
|
try {
|
|
1962
|
-
const { onPostAgent } = await import("./hook-hooks-
|
|
1962
|
+
const { onPostAgent } = await import("./hook-hooks-5U2AI6W7.js");
|
|
1963
1963
|
onPostAgent(ctx, db, input);
|
|
1964
1964
|
} catch {
|
|
1965
1965
|
}
|
|
@@ -2064,7 +2064,7 @@ var sessionEnd = async (input) => {
|
|
|
2064
2064
|
ingestUsage(db, input, sessionId);
|
|
2065
2065
|
if (ctx.config.adjudicator.enabled && input.client !== "cursor") {
|
|
2066
2066
|
try {
|
|
2067
|
-
const { onSessionEnd } = await import("./hook-hooks-
|
|
2067
|
+
const { onSessionEnd } = await import("./hook-hooks-5U2AI6W7.js");
|
|
2068
2068
|
onSessionEnd(ctx, db, input);
|
|
2069
2069
|
} catch {
|
|
2070
2070
|
}
|
|
@@ -2197,9 +2197,9 @@ var preCompact = (input) => {
|
|
|
2197
2197
|
|
|
2198
2198
|
// src/hooks/routing.ts
|
|
2199
2199
|
init_esm_shims();
|
|
2200
|
-
import { realpathSync as
|
|
2200
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync3 } from "fs";
|
|
2201
2201
|
import { homedir as homedir2 } from "os";
|
|
2202
|
-
import { extname, join as join7, relative, resolve as
|
|
2202
|
+
import { dirname as dirname3, extname, join as join7, relative as relative2, resolve as resolve3 } from "path";
|
|
2203
2203
|
|
|
2204
2204
|
// src/permissions/settings.ts
|
|
2205
2205
|
init_esm_shims();
|
|
@@ -2208,6 +2208,7 @@ import { join as join3 } from "path";
|
|
|
2208
2208
|
|
|
2209
2209
|
// src/permissions/rules.ts
|
|
2210
2210
|
init_esm_shims();
|
|
2211
|
+
import { posix } from "path";
|
|
2211
2212
|
function parseRule(raw) {
|
|
2212
2213
|
const m = /^([A-Za-z][\w-]*)(?:\((.*)\))?$/s.exec(raw.trim());
|
|
2213
2214
|
if (!m) return void 0;
|
|
@@ -2216,24 +2217,83 @@ function parseRule(raw) {
|
|
|
2216
2217
|
function escapeRe(s) {
|
|
2217
2218
|
return s.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
2218
2219
|
}
|
|
2220
|
+
function globSource(glob) {
|
|
2221
|
+
return glob.split("/**/").map(
|
|
2222
|
+
(part) => part.split("**").map(
|
|
2223
|
+
(seg) => seg.split("*").map((s) => s.split("?").map(escapeRe).join("[^/]")).join("[^/]*")
|
|
2224
|
+
).join(".*")
|
|
2225
|
+
).join("/(?:.*/)?");
|
|
2226
|
+
}
|
|
2227
|
+
function anchored(prefix, rest) {
|
|
2228
|
+
const full = posix.normalize(`${prefix}${rest}`);
|
|
2229
|
+
if (prefix && (full === prefix || full.startsWith(`${prefix}/`))) {
|
|
2230
|
+
return literal(prefix) + globSource(full.slice(prefix.length));
|
|
2231
|
+
}
|
|
2232
|
+
return globSource(full);
|
|
2233
|
+
}
|
|
2234
|
+
function literal(s) {
|
|
2235
|
+
return escapeRe(s).replace(/\*/g, "\\*");
|
|
2236
|
+
}
|
|
2237
|
+
function readSource(pattern, root, home) {
|
|
2238
|
+
const dir = pattern.endsWith("/") && pattern.length > 1;
|
|
2239
|
+
const p = dir ? pattern.slice(0, -1) : pattern;
|
|
2240
|
+
let body;
|
|
2241
|
+
if (p.startsWith("**")) body = globSource(p);
|
|
2242
|
+
else if (p.startsWith("//")) body = anchored("", p.slice(1));
|
|
2243
|
+
else if (p === "~" || p.startsWith("~/")) body = anchored(home, p.slice(1));
|
|
2244
|
+
else if (p.startsWith("/")) body = `(?:${anchored(root, p)}|${globSource(posix.normalize(p))})`;
|
|
2245
|
+
else if (p.startsWith("./")) body = anchored(root, p.slice(1));
|
|
2246
|
+
else if (p.includes("/")) body = anchored(root, `/${p}`);
|
|
2247
|
+
else body = `${literal(root)}/(?:.*/)?${globSource(p)}`;
|
|
2248
|
+
return `^${body}(?:/.*)?$`;
|
|
2249
|
+
}
|
|
2219
2250
|
var readRegexCache = /* @__PURE__ */ new Map();
|
|
2220
|
-
function readRegex(pattern) {
|
|
2221
|
-
|
|
2251
|
+
function readRegex(pattern, root, home) {
|
|
2252
|
+
const key = `${root}\0${home}\0${pattern}`;
|
|
2253
|
+
let re = readRegexCache.get(key);
|
|
2222
2254
|
if (!re) {
|
|
2223
|
-
if (readRegexCache.size >=
|
|
2224
|
-
re = new RegExp(
|
|
2225
|
-
|
|
2226
|
-
);
|
|
2227
|
-
readRegexCache.set(pattern, re);
|
|
2255
|
+
if (readRegexCache.size >= 4096) readRegexCache.clear();
|
|
2256
|
+
re = new RegExp(readSource(pattern, root, home));
|
|
2257
|
+
readRegexCache.set(key, re);
|
|
2228
2258
|
}
|
|
2229
2259
|
return re;
|
|
2230
2260
|
}
|
|
2231
|
-
function
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2261
|
+
function defaultRoot() {
|
|
2262
|
+
return process.cwd();
|
|
2263
|
+
}
|
|
2264
|
+
function needleOf(pattern) {
|
|
2265
|
+
const p = pattern.endsWith("/") && pattern.length > 1 ? pattern.slice(0, -1) : pattern;
|
|
2266
|
+
if (p.includes("..")) return "";
|
|
2267
|
+
let tail;
|
|
2268
|
+
if (p.startsWith("**") || !p.includes("/")) tail = p;
|
|
2269
|
+
else if (p.startsWith("//")) tail = posix.normalize(p.slice(1));
|
|
2270
|
+
else if (p === "~" || p.startsWith("~/")) tail = posix.normalize(p.slice(1) || "/");
|
|
2271
|
+
else if (p.startsWith("./")) tail = posix.normalize(p.slice(1));
|
|
2272
|
+
else tail = posix.normalize(p.startsWith("/") ? p : `/${p}`);
|
|
2273
|
+
let best = "";
|
|
2274
|
+
for (const piece of tail.split(/[*?]/)) if (piece.length > best.length) best = piece;
|
|
2275
|
+
return best;
|
|
2276
|
+
}
|
|
2277
|
+
var listCache = /* @__PURE__ */ new WeakMap();
|
|
2278
|
+
function readMatcher(rules, root) {
|
|
2279
|
+
const r = root ?? defaultRoot();
|
|
2280
|
+
const home = process.env.HOME ?? "";
|
|
2281
|
+
const key = `${r}\0${home}\0${rules.length}`;
|
|
2282
|
+
const hit = listCache.get(rules);
|
|
2283
|
+
if (hit && hit.key === key) return hit.matcher;
|
|
2284
|
+
const compiled = rules.filter((x) => x.tool === "Read").map(
|
|
2285
|
+
(rule) => rule.pattern === void 0 ? { rule, needle: "", re: /^/ } : { rule, needle: needleOf(rule.pattern), re: readRegex(rule.pattern, r, home) }
|
|
2286
|
+
);
|
|
2287
|
+
const which = (p) => {
|
|
2288
|
+
for (const c of compiled) {
|
|
2289
|
+
if (c.needle && !p.includes(c.needle)) continue;
|
|
2290
|
+
if (c.re.test(p)) return c.rule;
|
|
2291
|
+
}
|
|
2292
|
+
return void 0;
|
|
2293
|
+
};
|
|
2294
|
+
const matcher = { test: (p) => which(p) !== void 0, which };
|
|
2295
|
+
listCache.set(rules, { key, matcher });
|
|
2296
|
+
return matcher;
|
|
2237
2297
|
}
|
|
2238
2298
|
|
|
2239
2299
|
// src/permissions/split.ts
|
|
@@ -2267,7 +2327,7 @@ function loadPermissionRules(root, extraDeny = []) {
|
|
|
2267
2327
|
const stamps = files.map(stamp);
|
|
2268
2328
|
const hit = cache.get(key);
|
|
2269
2329
|
if (hit?.stamps.every((s, i) => s === stamps[i])) return hit.rules;
|
|
2270
|
-
const out = { allow: [], deny: [], ask: [] };
|
|
2330
|
+
const out = { allow: [], deny: [], ask: [], root };
|
|
2271
2331
|
const push = (list, raws) => {
|
|
2272
2332
|
for (const raw of raws ?? []) {
|
|
2273
2333
|
const r = parseRule(raw);
|
|
@@ -2287,14 +2347,51 @@ function loadPermissionRules(root, extraDeny = []) {
|
|
|
2287
2347
|
}
|
|
2288
2348
|
function checkRead(rules, absPaths) {
|
|
2289
2349
|
for (const kind of ["deny", "ask"]) {
|
|
2290
|
-
|
|
2291
|
-
|
|
2350
|
+
const m = readMatcher(rules[kind], rules.root);
|
|
2351
|
+
for (const p of absPaths) {
|
|
2352
|
+
const r = m.which(p);
|
|
2353
|
+
if (r) return { allowed: false, rule: r.raw, kind };
|
|
2292
2354
|
}
|
|
2293
2355
|
}
|
|
2294
2356
|
return { allowed: true };
|
|
2295
2357
|
}
|
|
2296
2358
|
function checkReadPath(rules, abs) {
|
|
2297
|
-
|
|
2359
|
+
const asWritten = checkRead(rules, [abs]);
|
|
2360
|
+
if (!asWritten.allowed) return asWritten;
|
|
2361
|
+
const real = realpathSync(abs);
|
|
2362
|
+
const native = realpathSync.native(abs);
|
|
2363
|
+
const more = [real, native].filter((p, i, a) => p !== abs && a.indexOf(p) === i);
|
|
2364
|
+
return more.length ? checkRead(rules, more) : asWritten;
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
// src/sandbox/file.ts
|
|
2368
|
+
init_esm_shims();
|
|
2369
|
+
import { existsSync as existsSync4, realpathSync as realpathSync2 } from "fs";
|
|
2370
|
+
import { isAbsolute, relative, resolve as resolve2 } from "path";
|
|
2371
|
+
function confinePath(root, target, given) {
|
|
2372
|
+
const abs = resolve2(root, target);
|
|
2373
|
+
const rules = given.root ? given : { ...given, root };
|
|
2374
|
+
refuse(checkRead(rules, [abs]), target);
|
|
2375
|
+
let real;
|
|
2376
|
+
try {
|
|
2377
|
+
real = realpathSync2(abs);
|
|
2378
|
+
} catch {
|
|
2379
|
+
throw new Error(`file not found: ${target}`);
|
|
2380
|
+
}
|
|
2381
|
+
refuse(checkReadPath(rules, abs), target);
|
|
2382
|
+
const rel = relative(root, real);
|
|
2383
|
+
const inside = rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
2384
|
+
if (inside) return real;
|
|
2385
|
+
if (readMatcher(rules.allow, rules.root).test(real)) return real;
|
|
2386
|
+
throw new Error(
|
|
2387
|
+
`${target} is outside the project root; add a Read(/${real}) rule to permissions.allow to opt it in`
|
|
2388
|
+
);
|
|
2389
|
+
}
|
|
2390
|
+
function refusal(check, target) {
|
|
2391
|
+
return `refused by permission rule ${check.rule} (${check.kind}) on path: ${target}`;
|
|
2392
|
+
}
|
|
2393
|
+
function refuse(check, target) {
|
|
2394
|
+
if (!check.allowed) throw new Error(refusal(check, target));
|
|
2298
2395
|
}
|
|
2299
2396
|
|
|
2300
2397
|
// src/session/routing-stats.ts
|
|
@@ -2319,7 +2416,7 @@ function recordRouting(db, sessionId, e) {
|
|
|
2319
2416
|
|
|
2320
2417
|
// src/session/server-alive.ts
|
|
2321
2418
|
init_esm_shims();
|
|
2322
|
-
import { existsSync as
|
|
2419
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync as readdirSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2323
2420
|
import { join as join4 } from "path";
|
|
2324
2421
|
function markersDir(ctx) {
|
|
2325
2422
|
return join4(projectDir(ctx.id), "servers");
|
|
@@ -2334,7 +2431,7 @@ function processAlive(pid) {
|
|
|
2334
2431
|
}
|
|
2335
2432
|
function liveServers(ctx) {
|
|
2336
2433
|
const dir = markersDir(ctx);
|
|
2337
|
-
if (!
|
|
2434
|
+
if (!existsSync5(dir)) return [];
|
|
2338
2435
|
const live = [];
|
|
2339
2436
|
for (const name of readdirSync2(dir)) {
|
|
2340
2437
|
const pid = Number(name);
|
|
@@ -2357,7 +2454,7 @@ import { join as join6 } from "path";
|
|
|
2357
2454
|
init_esm_shims();
|
|
2358
2455
|
import {
|
|
2359
2456
|
copyFileSync,
|
|
2360
|
-
existsSync as
|
|
2457
|
+
existsSync as existsSync6,
|
|
2361
2458
|
mkdirSync as mkdirSync3,
|
|
2362
2459
|
readdirSync as readdirSync3,
|
|
2363
2460
|
readFileSync as readFileSync3,
|
|
@@ -2402,7 +2499,11 @@ function literalOf(pattern) {
|
|
|
2402
2499
|
function symbolHints(db, pattern) {
|
|
2403
2500
|
const lit = literalOf(pattern);
|
|
2404
2501
|
if (!lit) return void 0;
|
|
2405
|
-
const nodes = db.graphFindNodes({
|
|
2502
|
+
const nodes = db.graphFindNodes({
|
|
2503
|
+
query: lit,
|
|
2504
|
+
kinds: SYMBOL_KINDS,
|
|
2505
|
+
limit: 5
|
|
2506
|
+
});
|
|
2406
2507
|
if (!nodes.length) return void 0;
|
|
2407
2508
|
const list = nodes.map((n) => `${n.kind} ${n.name} (${n.path}:${n.start_line ?? "?"})`).join("; ");
|
|
2408
2509
|
return `eminiar graph: symbols matching "${lit}": ${list}. em_symbols {name_path, include_body: true} reads one; {mode:"references"} lists its callers.`;
|
|
@@ -2412,13 +2513,13 @@ function coverageHint(db, rel) {
|
|
|
2412
2513
|
if (!row || row.status === "indexed") return void 0;
|
|
2413
2514
|
return `eminiar graph: ${rel} is ${row.status}${row.reason ? ` (${row.reason})` : ""}; graph answers about it may be incomplete.`;
|
|
2414
2515
|
}
|
|
2415
|
-
var GIT_HISTORY = /(?:^|[;&|]\s*)git\s+(?:(?:-C|--git-dir|--work-tree)[= ]\S+\s+|--no-pager\s+)*(log|blame|shortlog|show|whatchanged|reflog)\b([^;&|]*)/;
|
|
2516
|
+
var GIT_HISTORY = /(?:^|[;&|]\s*)git\s+((?:(?:-C|--git-dir|--work-tree)[= ]\S+\s+|--no-pager\s+)*)(log|blame|shortlog|show|whatchanged|reflog)\b([^;&|]*)/;
|
|
2416
2517
|
var ONE_COMMIT = /(^|\s)(-1|-n\s*1|-n1|--max-count[= ]1)(?=\s|$)/;
|
|
2417
2518
|
function historyCall(cmd) {
|
|
2418
2519
|
const m = GIT_HISTORY.exec(cmd);
|
|
2419
2520
|
if (!m) return void 0;
|
|
2420
|
-
const sub = m[
|
|
2421
|
-
const rest = (m[
|
|
2521
|
+
const sub = m[2];
|
|
2522
|
+
const rest = (m[3] ?? "").trim();
|
|
2422
2523
|
const args = rest.split(/\s+/).filter(Boolean);
|
|
2423
2524
|
const q = (s) => JSON.stringify(s.replace(/^['"]|['"]$/g, ""));
|
|
2424
2525
|
const positional = (a) => !a.startsWith("-");
|
|
@@ -2453,6 +2554,66 @@ function historyCall(cmd) {
|
|
|
2453
2554
|
if (sub === "shortlog") return `{action:"timeline"}`;
|
|
2454
2555
|
return `{action:"overview"}`;
|
|
2455
2556
|
}
|
|
2557
|
+
function historyDir(segments, cwd) {
|
|
2558
|
+
let dir = cwd;
|
|
2559
|
+
let steps = 0;
|
|
2560
|
+
const step = (arg) => {
|
|
2561
|
+
dir = ++steps > MAX_STEPS ? void 0 : dir && changeDir(dir, arg);
|
|
2562
|
+
};
|
|
2563
|
+
for (const segment of segments) {
|
|
2564
|
+
const m = GIT_HISTORY.exec(segment);
|
|
2565
|
+
if (m) {
|
|
2566
|
+
const opts = m[1] ?? "";
|
|
2567
|
+
if (/--(git-dir|work-tree)\b/.test(opts)) return void 0;
|
|
2568
|
+
for (const c of opts.matchAll(/-C[= ](\S+)/g)) {
|
|
2569
|
+
step(c[1]);
|
|
2570
|
+
if (!dir) return void 0;
|
|
2571
|
+
}
|
|
2572
|
+
return dir;
|
|
2573
|
+
}
|
|
2574
|
+
const cd = CD.exec(segment);
|
|
2575
|
+
if (cd) step(cd[1]);
|
|
2576
|
+
if (!dir) return void 0;
|
|
2577
|
+
}
|
|
2578
|
+
return dir;
|
|
2579
|
+
}
|
|
2580
|
+
var CD = /^\(?\s*(?:cd|pushd)\b(.*)$/;
|
|
2581
|
+
var MAX_STEPS = 16;
|
|
2582
|
+
var MAX_RAW = 64 * 1024;
|
|
2583
|
+
var MAX_CLASSIFIED = 8 * 1024;
|
|
2584
|
+
var MAX_OPERANDS = 64;
|
|
2585
|
+
var MAX_SEGMENTS = 64;
|
|
2586
|
+
function changeDir(dir, arg) {
|
|
2587
|
+
const a = arg.trim().replace(/^(['"])(.*)\1$/, "$2") || "~";
|
|
2588
|
+
if (/^-|^~[^/]|[\s$`*?[\]{}()\\]/.test(a)) return void 0;
|
|
2589
|
+
return a === "~" || a.startsWith("~/") ? join7(homedir2(), a.slice(1)) : resolve3(dir, a);
|
|
2590
|
+
}
|
|
2591
|
+
function inRootRepo(root, dir) {
|
|
2592
|
+
try {
|
|
2593
|
+
const top = realpathSync3(root);
|
|
2594
|
+
let inside = false;
|
|
2595
|
+
for (let d = realpathSync3(dir); ; d = dirname3(d)) {
|
|
2596
|
+
if (d === top) inside = true;
|
|
2597
|
+
if (existsSync7(join7(d, ".git"))) return inside;
|
|
2598
|
+
if (dirname3(d) === d) return false;
|
|
2599
|
+
}
|
|
2600
|
+
} catch {
|
|
2601
|
+
return false;
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
function repoOf(dir) {
|
|
2605
|
+
try {
|
|
2606
|
+
for (let d = realpathSync3(dir); ; d = dirname3(d)) {
|
|
2607
|
+
if (existsSync7(join7(d, ".git"))) return d;
|
|
2608
|
+
if (dirname3(d) === d) return void 0;
|
|
2609
|
+
}
|
|
2610
|
+
} catch {
|
|
2611
|
+
return void 0;
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
function sameRepo(root, dir) {
|
|
2615
|
+
return repoOf(root) === repoOf(dir);
|
|
2616
|
+
}
|
|
2456
2617
|
function docsSources(root) {
|
|
2457
2618
|
try {
|
|
2458
2619
|
return readManifestAt(join7(root, ".eminiar", "docs.json"), root).sources;
|
|
@@ -2495,7 +2656,34 @@ var BIG_TEXT_EXT = /* @__PURE__ */ new Set([
|
|
|
2495
2656
|
]);
|
|
2496
2657
|
var VISUAL_EXT = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".pdf"]);
|
|
2497
2658
|
function withoutHeredocs(cmd) {
|
|
2498
|
-
|
|
2659
|
+
if (!cmd.includes("<<")) return cmd;
|
|
2660
|
+
const head = /<<-?\s*(['"]?)(\w+)\1/y;
|
|
2661
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2662
|
+
const terminator = (word, from) => {
|
|
2663
|
+
const c = seen.get(word);
|
|
2664
|
+
if (c && from >= c.from && (c.at === -1 || from <= c.at)) return c.end;
|
|
2665
|
+
const re = new RegExp(String.raw`\n[^\S\n]*${word}\s*(?=\n|$)`, "g");
|
|
2666
|
+
re.lastIndex = from;
|
|
2667
|
+
const m = re.exec(cmd);
|
|
2668
|
+
const rec = m ? { from, at: m.index, end: m.index + m[0].length } : { from, at: -1, end: -1 };
|
|
2669
|
+
seen.set(word, rec);
|
|
2670
|
+
return rec.end;
|
|
2671
|
+
};
|
|
2672
|
+
let out = "";
|
|
2673
|
+
let copied = 0;
|
|
2674
|
+
for (let k = cmd.indexOf("<<"); k !== -1; k = cmd.indexOf("<<", Math.max(k + 1, copied))) {
|
|
2675
|
+
head.lastIndex = k;
|
|
2676
|
+
const m = head.exec(cmd);
|
|
2677
|
+
if (!m) continue;
|
|
2678
|
+
const word = m[2];
|
|
2679
|
+
const eol = cmd.indexOf("\n", head.lastIndex);
|
|
2680
|
+
if (eol === -1) break;
|
|
2681
|
+
const end = terminator(word, eol + 1);
|
|
2682
|
+
if (end === -1) continue;
|
|
2683
|
+
out += `${cmd.slice(copied, k)}<<${word}${cmd.slice(head.lastIndex, eol)}`;
|
|
2684
|
+
copied = end;
|
|
2685
|
+
}
|
|
2686
|
+
return out + cmd.slice(copied);
|
|
2499
2687
|
}
|
|
2500
2688
|
function splitUnquoted(s, sep2) {
|
|
2501
2689
|
const out = [];
|
|
@@ -2503,13 +2691,23 @@ function splitUnquoted(s, sep2) {
|
|
|
2503
2691
|
let quote;
|
|
2504
2692
|
for (let i = 0; i < s.length; i++) {
|
|
2505
2693
|
const ch = s[i];
|
|
2694
|
+
if (quote === "'") {
|
|
2695
|
+
cur += ch;
|
|
2696
|
+
if (ch === "'") quote = void 0;
|
|
2697
|
+
continue;
|
|
2698
|
+
}
|
|
2699
|
+
if (ch === "\\") {
|
|
2700
|
+
cur += ch + (s[i + 1] ?? "");
|
|
2701
|
+
i++;
|
|
2702
|
+
continue;
|
|
2703
|
+
}
|
|
2506
2704
|
if (quote) {
|
|
2507
2705
|
cur += ch;
|
|
2508
|
-
if (ch === quote
|
|
2706
|
+
if (ch === quote[quote.length - 1]) quote = void 0;
|
|
2509
2707
|
continue;
|
|
2510
2708
|
}
|
|
2511
2709
|
if (ch === "'" || ch === '"') {
|
|
2512
|
-
quote = ch;
|
|
2710
|
+
quote = ch === "'" && s[i - 1] === "$" ? "$'" : ch;
|
|
2513
2711
|
cur += ch;
|
|
2514
2712
|
continue;
|
|
2515
2713
|
}
|
|
@@ -2530,9 +2728,44 @@ function commandSegments(cmd) {
|
|
|
2530
2728
|
const ch = s[i];
|
|
2531
2729
|
if (ch === ";" || ch === "\n") return 1;
|
|
2532
2730
|
if ((ch === "&" || ch === "|") && s[i + 1] === ch) return 2;
|
|
2731
|
+
if (ch === "&" && !/[&<>|]/.test(s[i - 1] ?? "") && !/[&>]/.test(s[i + 1] ?? "")) return 1;
|
|
2533
2732
|
return 0;
|
|
2534
2733
|
});
|
|
2535
2734
|
}
|
|
2735
|
+
function shellWords(s) {
|
|
2736
|
+
const words = [];
|
|
2737
|
+
let cur = "";
|
|
2738
|
+
let inWord = false;
|
|
2739
|
+
for (let i = 0; i < s.length; i++) {
|
|
2740
|
+
const ch = s[i];
|
|
2741
|
+
if (ch === " " || ch === " " || ch === "\n") {
|
|
2742
|
+
if (inWord) words.push(cur);
|
|
2743
|
+
cur = "";
|
|
2744
|
+
inWord = false;
|
|
2745
|
+
continue;
|
|
2746
|
+
}
|
|
2747
|
+
inWord = true;
|
|
2748
|
+
if (ch === "\\") {
|
|
2749
|
+
cur += s[++i] ?? "";
|
|
2750
|
+
} else if (ch === "'" && s[i - 1] !== "$") {
|
|
2751
|
+
const end = s.indexOf("'", i + 1);
|
|
2752
|
+
const stop2 = end === -1 ? s.length : end;
|
|
2753
|
+
cur += s.slice(i + 1, stop2);
|
|
2754
|
+
i = stop2;
|
|
2755
|
+
} else if (ch === '"' || ch === "$" && s[i + 1] === "'") {
|
|
2756
|
+
const close = ch === '"' ? '"' : "'";
|
|
2757
|
+
if (ch === "$") i++;
|
|
2758
|
+
for (i++; i < s.length && s[i] !== close; i++) {
|
|
2759
|
+
if (s[i] === "\\" && i + 1 < s.length) i++;
|
|
2760
|
+
cur += s[i];
|
|
2761
|
+
}
|
|
2762
|
+
} else {
|
|
2763
|
+
cur += ch;
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
if (inWord) words.push(cur);
|
|
2767
|
+
return words;
|
|
2768
|
+
}
|
|
2536
2769
|
function pipeStages(segment) {
|
|
2537
2770
|
return splitUnquoted(segment, (s, i) => s[i] === "|" && s[i + 1] !== "|" ? 1 : 0);
|
|
2538
2771
|
}
|
|
@@ -2546,10 +2779,416 @@ function bounds(stage) {
|
|
|
2546
2779
|
if (!/^\s*(head|tail)\b/.test(stage) || /(^|\s)-c\b|--bytes\b/.test(stage)) return false;
|
|
2547
2780
|
return headLines(stage) <= BOUNDED_LINES;
|
|
2548
2781
|
}
|
|
2549
|
-
var
|
|
2550
|
-
var
|
|
2551
|
-
|
|
2552
|
-
|
|
2782
|
+
var GIT_MUTATING = /^(git\s+(?:(?:-[Cc]\s*[^-\s]\S*|--(?:git-dir|work-tree)[= ]\S+|--no-pager)\s+)*(add|commit|checkout|switch|push|pull|fetch|merge|rebase|stash|reset|restore|cherry-pick|revert|tag|init|clone|worktree|mv|rm|am|apply|submodule|clean|branch\s(?:.*\s)?(-[a-zA-Z]*[dDmM][a-zA-Z]*|--delete|--move)|config(?:\s+--(?:global|local|system|worktree))*\s+(?:set|unset|--(?:unset|unset-all|add|replace-all|rename-section|remove-section|edit)|-e|\w[\w.-]*\s+\S+)))(?![\w-])/;
|
|
2783
|
+
var OTHER_MUTATING = /^(npm\s+(install|i|ci|uninstall|un|link|publish|version|update|up|dedupe|audit\s+fix)|pnpm\s+(install|i|add|remove|rm|update|up|link|audit\s.*--fix)|yarn\s+(add|remove|install|upgrade)|pip3?\s+(install|uninstall)|cargo\s+(add|install)|brew\s+(install|uninstall|upgrade)|gh\s+(pr|issue|release|repo)\s+(create|edit|merge|close|comment|delete|ready)|terraform\s+(?:-\S+\s+)*(apply|destroy|import|taint|untaint|state\s+(rm|mv|push))|helm\s+(?:-\S+\s+(?:[^-\s]\S*\s+)?)*(install|upgrade|uninstall|rollback|delete)|mvnw?(?:\s.*)?\s(deploy|install|release:\S*)|gradlew?(?:\s.*)?[\s:](publish|upload)\w*|rm|mv|cp|mkdir|rmdir|touch|chmod|chown|ln|sed\s+(?:-\S+\s+)*-i|tee|eval|sh|bash|zsh|find\s.*\s-(delete|exec|execdir|ok|okdir))(?![\w-])/;
|
|
2784
|
+
var MUTATING_WORDS = /* @__PURE__ */ new Set([
|
|
2785
|
+
"npm",
|
|
2786
|
+
"pnpm",
|
|
2787
|
+
"yarn",
|
|
2788
|
+
"pip",
|
|
2789
|
+
"pip3",
|
|
2790
|
+
"cargo",
|
|
2791
|
+
"brew",
|
|
2792
|
+
"gh",
|
|
2793
|
+
"terraform",
|
|
2794
|
+
"helm",
|
|
2795
|
+
"mvn",
|
|
2796
|
+
"mvnw",
|
|
2797
|
+
"gradle",
|
|
2798
|
+
"gradlew",
|
|
2799
|
+
"rm",
|
|
2800
|
+
"mv",
|
|
2801
|
+
"cp",
|
|
2802
|
+
"mkdir",
|
|
2803
|
+
"rmdir",
|
|
2804
|
+
"touch",
|
|
2805
|
+
"chmod",
|
|
2806
|
+
"chown",
|
|
2807
|
+
"ln",
|
|
2808
|
+
"sed",
|
|
2809
|
+
"tee",
|
|
2810
|
+
"eval",
|
|
2811
|
+
"sh",
|
|
2812
|
+
"bash",
|
|
2813
|
+
"zsh",
|
|
2814
|
+
"find"
|
|
2815
|
+
]);
|
|
2816
|
+
var MUTATING = {
|
|
2817
|
+
test(cmd) {
|
|
2818
|
+
const word = /^[A-Za-z0-9]*/.exec(cmd)[0];
|
|
2819
|
+
if (word === "git") return GIT_MUTATING.test(cmd);
|
|
2820
|
+
return MUTATING_WORDS.has(word) && OTHER_MUTATING.test(cmd);
|
|
2821
|
+
}
|
|
2822
|
+
};
|
|
2823
|
+
var longOpts = (valued) => String.raw`--(?:${valued})\s+[^-\s]\S*|--(?!(?:${valued})(?:\s|$))\S*`;
|
|
2824
|
+
var RUNNERS = [
|
|
2825
|
+
String.raw`sudo(?:\s+(?:-[ugCDhpRrtTU]\s*[^-\s]\S*|-(?![ugCDhpRrtTU-])\S+|${longOpts("user|group|chdir|host|prompt|close-from|role|type|other-user|chroot|command-timeout")}))*`,
|
|
2826
|
+
String.raw`env(?:\s+(?:-[uC]\s*[^-\s]\S*|-(?![uC-])\S+|${longOpts("unset|chdir|split-string")}))*`,
|
|
2827
|
+
String.raw`(?:xargs|parallel)(?:\s+(?:-[nILPsdEaj]\s*[^-\s]\S*|-(?![nILPsdEaj-])\S+|${longOpts("max-args|max-procs|max-lines|max-chars|delimiter|arg-file|eof|jobs")}))*`,
|
|
2828
|
+
String.raw`timeout(?:\s+(?:-[sk]\s*[^-\s]\S*|-(?![sk-])\S+|${longOpts("signal|kill-after")}))*\s+\d\S*`,
|
|
2829
|
+
String.raw`nice(?:\s+-n\s*\S+|\s+-\d+|\s+--adjustment(?:=|\s+)\S+)?`,
|
|
2830
|
+
String.raw`stdbuf(?:\s+-[ioe]\s*\S+)*`,
|
|
2831
|
+
String.raw`(?:time|command)(?:\s+-p)?`,
|
|
2832
|
+
"nohup",
|
|
2833
|
+
String.raw`[A-Z_][A-Z0-9_]*=\S*`
|
|
2834
|
+
];
|
|
2835
|
+
var SHELL_WORDS = /^(?:\s*(?:[({!]|(?:then|do|else|exec|if|elif|while|until)\s))*\s*/;
|
|
2836
|
+
var RUNNER = new RegExp(String.raw`^\s*(?:(?:${RUNNERS.join("|")})\s+)*`);
|
|
2837
|
+
var RUNNER_HEAD = /^\s*(?:sudo|env|xargs|parallel|timeout|nice|stdbuf|time|command|nohup|[A-Z_])/;
|
|
2838
|
+
var stripRunners = (s) => RUNNER_HEAD.test(s) ? s.replace(RUNNER, "") : s.replace(/^\s+/, "");
|
|
2839
|
+
var WRITES_FILE = /(?:^|[\s\d&])>{1,2}\s*(?!&)(?!\/dev\/null)\S/;
|
|
2840
|
+
var GLUED_WRITE = /(?<![-=>])>{1,2}\s*(?!&)(?!\/dev\/null)\S/;
|
|
2841
|
+
var RUNS_STDIN = /^(python3?|node|perl|ruby|deno|bun)(\s+-\S*)*$/;
|
|
2842
|
+
var FOLLOWS = /^((kubectl|docker)(\s.*)?\s(-f|-w|--follow)(?=[\s=]|$)|tail(\s.*)?\s(-[a-zA-Z]*[fF]|--follow)|.*\s--watch(All)?(?=[\s=]|$))/;
|
|
2843
|
+
var CURL_SENDS = /\s(?:-(?:(?!X)[a-zA-Z])*[dFTOoKDc]|(?:-[a-zA-Z]*X|--request)(?![\s=]*['"]?(?:GET|HEAD)\b)|--(?:data|json|form|upload-file|output|remote-name|config|dump-header|cookie-jar|trace|stderr|create-dirs|libcurl|etag-save|hsts|alt-svc))/;
|
|
2844
|
+
var WGET_PRINTS = /\s(?:-[a-zA-Z]*O\s*-|--output-document[= ]-|--spider)(?=\s|$)/;
|
|
2845
|
+
var WGET_SENDS = /\s(?:--(post-data|post-file|body-data|body-file|method|output-file|append-output|execute|config)\b|-[a-zA-Z]*[oae])/;
|
|
2846
|
+
var KUBECTL_READS = /^(get|describe|logs|top|explain|api-resources|api-versions|events|version|cluster-info|config\s+(view|get-contexts|current-context)|rollout\s+(status|history)|auth\s+can-i)\b/;
|
|
2847
|
+
var DOCKER_READS = /^(ps|logs|images|inspect|history|version|info|stats\s.*--no-stream|(container|image|network|volume)\s+(ls|inspect)|compose\s+(ps|logs|config|images|ls)|system\s+df)\b/;
|
|
2848
|
+
var CHECK_SCRIPT = /^(t|test|lint|type-?check|check)(?=$|[:.-])(?![\w:.-]*(fix|write|update|install|deploy|publish|push|release|clean))/;
|
|
2849
|
+
var FIX_ARGS = /(?:^|\s)(--fix|--write|--apply(-unsafe)?|-u|--update(-?[sS]napshots?)?)(?=[\s=]|$)/;
|
|
2850
|
+
var NPX_CHECKS = /^(vitest|jest|tsc|eslint|biome|prettier)(?:@[\w.^~-]*)?$/;
|
|
2851
|
+
var PM_READS = /^(ls|list|outdated|why|info|view|audit|licenses|bin|root|help)$/;
|
|
2852
|
+
var VALUE_OPTS = {
|
|
2853
|
+
kubectl: /* @__PURE__ */ new Set([
|
|
2854
|
+
"-n",
|
|
2855
|
+
"--namespace",
|
|
2856
|
+
"--context",
|
|
2857
|
+
"--cluster",
|
|
2858
|
+
"--kubeconfig",
|
|
2859
|
+
"--user",
|
|
2860
|
+
"-s",
|
|
2861
|
+
"--server",
|
|
2862
|
+
"--token",
|
|
2863
|
+
"--as",
|
|
2864
|
+
"--as-group",
|
|
2865
|
+
"--as-uid",
|
|
2866
|
+
"--cache-dir",
|
|
2867
|
+
"--certificate-authority",
|
|
2868
|
+
"--client-certificate",
|
|
2869
|
+
"--client-key",
|
|
2870
|
+
"--password",
|
|
2871
|
+
"--username",
|
|
2872
|
+
"--profile",
|
|
2873
|
+
"--profile-output",
|
|
2874
|
+
"--request-timeout",
|
|
2875
|
+
"--tls-server-name",
|
|
2876
|
+
"--log-dir",
|
|
2877
|
+
"--log-file",
|
|
2878
|
+
"--log-file-max-size",
|
|
2879
|
+
"--vmodule",
|
|
2880
|
+
"-v",
|
|
2881
|
+
"--v"
|
|
2882
|
+
]),
|
|
2883
|
+
docker: /* @__PURE__ */ new Set([
|
|
2884
|
+
"-H",
|
|
2885
|
+
"--host",
|
|
2886
|
+
"-c",
|
|
2887
|
+
"--context",
|
|
2888
|
+
"--config",
|
|
2889
|
+
"-l",
|
|
2890
|
+
"--log-level",
|
|
2891
|
+
"--tlscacert",
|
|
2892
|
+
"--tlscert",
|
|
2893
|
+
"--tlskey"
|
|
2894
|
+
]),
|
|
2895
|
+
npm: /* @__PURE__ */ new Set(["--prefix", "-w", "--workspace", "--userconfig", "--registry", "--cache"]),
|
|
2896
|
+
pnpm: /* @__PURE__ */ new Set(["-C", "--dir", "--filter", "-F", "--workspace-dir"]),
|
|
2897
|
+
yarn: /* @__PURE__ */ new Set(["--cwd", "--cache-folder", "--registry"])
|
|
2898
|
+
};
|
|
2899
|
+
var FLAG_OPTS = {
|
|
2900
|
+
kubectl: /* @__PURE__ */ new Set([
|
|
2901
|
+
"--insecure-skip-tls-verify",
|
|
2902
|
+
"--match-server-version",
|
|
2903
|
+
"--warnings-as-errors",
|
|
2904
|
+
"--disable-compression",
|
|
2905
|
+
"--alsologtostderr",
|
|
2906
|
+
"--logtostderr",
|
|
2907
|
+
"--add-dir-header",
|
|
2908
|
+
"--skip-headers",
|
|
2909
|
+
"--skip-log-headers",
|
|
2910
|
+
"--one-output",
|
|
2911
|
+
"-h",
|
|
2912
|
+
"--help"
|
|
2913
|
+
]),
|
|
2914
|
+
docker: /* @__PURE__ */ new Set(["-D", "--debug", "--tls", "--tlsverify", "-v", "--version", "--help"])
|
|
2915
|
+
};
|
|
2916
|
+
var MAX_READINGS = 4;
|
|
2917
|
+
function afterOptions(tool, args) {
|
|
2918
|
+
const words = args.trim().split(/\s+/);
|
|
2919
|
+
const valued = VALUE_OPTS[tool];
|
|
2920
|
+
let i = 0;
|
|
2921
|
+
while (words[i]?.startsWith("-")) i += valued?.has(words[i]) ? 2 : 1;
|
|
2922
|
+
return words.slice(i);
|
|
2923
|
+
}
|
|
2924
|
+
function subcommands(tool, args) {
|
|
2925
|
+
const words = args.trim().split(/\s+/);
|
|
2926
|
+
const valued = VALUE_OPTS[tool];
|
|
2927
|
+
const flags = FLAG_OPTS[tool];
|
|
2928
|
+
const starts = /* @__PURE__ */ new Set();
|
|
2929
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2930
|
+
const todo = [0];
|
|
2931
|
+
while (todo.length) {
|
|
2932
|
+
const i = todo.pop();
|
|
2933
|
+
if (seen.has(i)) continue;
|
|
2934
|
+
seen.add(i);
|
|
2935
|
+
const w = words[i];
|
|
2936
|
+
if (w === void 0 || !w.startsWith("-")) {
|
|
2937
|
+
starts.add(i);
|
|
2938
|
+
if (starts.size > MAX_READINGS) return void 0;
|
|
2939
|
+
} else if (valued?.has(w)) todo.push(i + 2);
|
|
2940
|
+
else if (flags?.has(w) || w.includes("=") || /^-[^-]./.test(w)) todo.push(i + 1);
|
|
2941
|
+
else todo.push(i + 1, i + 2);
|
|
2942
|
+
}
|
|
2943
|
+
const tail = /\s--no-stream\b/.test(args) ? " --no-stream" : "";
|
|
2944
|
+
return [...starts].map((i) => words.slice(i, i + 3).join(" ") + tail);
|
|
2945
|
+
}
|
|
2946
|
+
function verbChanges(tool, reads, args) {
|
|
2947
|
+
const subs = subcommands(tool, args);
|
|
2948
|
+
return !subs || subs.some((s) => !reads.test(s));
|
|
2949
|
+
}
|
|
2950
|
+
function runsScript(tool, args) {
|
|
2951
|
+
const words = afterOptions(tool, args);
|
|
2952
|
+
let script = words.shift();
|
|
2953
|
+
if (script === "run" || script === "run-script") script = words.shift();
|
|
2954
|
+
else if (script === void 0 || PM_READS.test(script) || tool === "npm" && script !== "test")
|
|
2955
|
+
return false;
|
|
2956
|
+
if (script === void 0) return false;
|
|
2957
|
+
return !CHECK_SCRIPT.test(script) || FIX_ARGS.test(words.join(" "));
|
|
2958
|
+
}
|
|
2959
|
+
function makeChanges(args) {
|
|
2960
|
+
const words = shellWords(args);
|
|
2961
|
+
const targets = [];
|
|
2962
|
+
for (let i = 0; i < words.length; i++) {
|
|
2963
|
+
const w = words[i];
|
|
2964
|
+
if (/^(-E|--eval)/.test(w)) return true;
|
|
2965
|
+
if (w.startsWith("-")) {
|
|
2966
|
+
if (/^(-[CfIoW]|--(directory|file|makefile|include-dir|old-file|assume-old|what-if|new-file|assume-new))$/.test(
|
|
2967
|
+
w
|
|
2968
|
+
) || /^(-[jl]|--jobs|--load-average|--max-load)$/.test(w) && /^\d+(\.\d+)?$/.test(words[i + 1] ?? ""))
|
|
2969
|
+
i++;
|
|
2970
|
+
continue;
|
|
2971
|
+
}
|
|
2972
|
+
if (!w.includes("=")) targets.push(w);
|
|
2973
|
+
}
|
|
2974
|
+
return !targets.length || !targets.every((t) => CHECK_SCRIPT.test(t)) || FIX_ARGS.test(args);
|
|
2975
|
+
}
|
|
2976
|
+
var GH_API_VALUED = /^(-[FfHXpqt]|--(method|field|raw-field|input|header|jq|template|preview|hostname|cache))$/;
|
|
2977
|
+
function ghApiSends(args) {
|
|
2978
|
+
const words = shellWords(args);
|
|
2979
|
+
if (words[0] !== "api") return false;
|
|
2980
|
+
let method;
|
|
2981
|
+
let fields = false;
|
|
2982
|
+
for (let i = 1; i < words.length; i++) {
|
|
2983
|
+
const w = words[i];
|
|
2984
|
+
const long = /^--(method|field|raw-field|input)(?:=|$)/.exec(w)?.[1];
|
|
2985
|
+
if (long) {
|
|
2986
|
+
if (long === "method")
|
|
2987
|
+
method = w.includes("=") ? w.slice(w.indexOf("=") + 1) : words[i + 1] ?? "";
|
|
2988
|
+
else fields = true;
|
|
2989
|
+
} else if (/^-[a-zA-Z]/.test(w)) {
|
|
2990
|
+
const c = w.slice(1).replace(/^i+/, "")[0];
|
|
2991
|
+
const value = w.slice(w.indexOf(c ?? "") + 1) || (words[i + 1] ?? "");
|
|
2992
|
+
if (c === "X") method = value;
|
|
2993
|
+
else if (c === "f" || c === "F") fields = true;
|
|
2994
|
+
}
|
|
2995
|
+
if (GH_API_VALUED.test(w)) i++;
|
|
2996
|
+
}
|
|
2997
|
+
if (method !== void 0) return method.toUpperCase() !== "GET";
|
|
2998
|
+
if (words.includes("graphql")) return /\bmutation\b/.test(words.join(" "));
|
|
2999
|
+
return fields;
|
|
3000
|
+
}
|
|
3001
|
+
function npxChanges(args, cwd) {
|
|
3002
|
+
const words = shellWords(args);
|
|
3003
|
+
let i = 0;
|
|
3004
|
+
for (; words[i]?.startsWith("-"); i++)
|
|
3005
|
+
if (/^(-p|--package|-c|--call)(=|$)/.test(words[i])) return true;
|
|
3006
|
+
const [name = "", ...rest] = words.slice(i);
|
|
3007
|
+
const tool = NPX_CHECKS.exec(name)?.[1];
|
|
3008
|
+
if (!tool || FIX_ARGS.test(args)) return true;
|
|
3009
|
+
const has = (re) => rest.some((w) => re.test(w));
|
|
3010
|
+
if (has(/^--(output-?file|outputFile|out-?dir|outFile|generateTrace)(=|$)/i)) return true;
|
|
3011
|
+
if (tool === "eslint" && has(/^-o/)) return true;
|
|
3012
|
+
if (tool === "prettier" && has(/^-[a-zA-Z]*w$/)) return true;
|
|
3013
|
+
return tool === "tsc" && !tscEmitsNothing(rest, cwd);
|
|
3014
|
+
}
|
|
3015
|
+
function tscEmitsNothing(words, cwd) {
|
|
3016
|
+
if (words.some((w) => /^(-b|--build|--generateTrace)(=|$)/i.test(w))) return false;
|
|
3017
|
+
const noEmit = words.findIndex((w) => /^--noEmit(=true)?$/i.test(w));
|
|
3018
|
+
if (noEmit !== -1) return words[noEmit + 1] !== "false";
|
|
3019
|
+
let project = "tsconfig.json";
|
|
3020
|
+
for (let i = 0; i < words.length; i++) {
|
|
3021
|
+
const w = words[i];
|
|
3022
|
+
if (/^(-p|--project)$/i.test(w)) project = words[++i] ?? "";
|
|
3023
|
+
else if (!w.startsWith("-")) return false;
|
|
3024
|
+
}
|
|
3025
|
+
try {
|
|
3026
|
+
let file2 = resolve3(cwd, project);
|
|
3027
|
+
if (statSync3(file2).isDirectory()) file2 = join7(file2, "tsconfig.json");
|
|
3028
|
+
const st = statSync3(file2);
|
|
3029
|
+
if (st.size > 256 * 1024) return false;
|
|
3030
|
+
return /"noEmit"\s*:\s*true\b/.test(readFileSync5(file2, "utf8"));
|
|
3031
|
+
} catch {
|
|
3032
|
+
return false;
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
var CHANGES = /* @__PURE__ */ new Map([
|
|
3036
|
+
["curl", (a) => CURL_SENDS.test(a)],
|
|
3037
|
+
["wget", (a) => !WGET_PRINTS.test(a) || WGET_SENDS.test(a)],
|
|
3038
|
+
["kubectl", (a) => verbChanges("kubectl", KUBECTL_READS, a)],
|
|
3039
|
+
["docker", (a) => verbChanges("docker", DOCKER_READS, a)],
|
|
3040
|
+
["gh", ghApiSends],
|
|
3041
|
+
["make", makeChanges],
|
|
3042
|
+
["npx", npxChanges],
|
|
3043
|
+
["npm", (a) => runsScript("npm", a)],
|
|
3044
|
+
["pnpm", (a) => runsScript("pnpm", a)],
|
|
3045
|
+
["yarn", (a) => runsScript("yarn", a)]
|
|
3046
|
+
]);
|
|
3047
|
+
function operands(words, valued) {
|
|
3048
|
+
let n = 0;
|
|
3049
|
+
for (let i = 0; i < words.length; i++) {
|
|
3050
|
+
const w = words[i];
|
|
3051
|
+
if (w === "--") return n + words.length - i - 1;
|
|
3052
|
+
if (w.startsWith("-") && w.length > 1) {
|
|
3053
|
+
if (valued.test(w)) i++;
|
|
3054
|
+
} else n++;
|
|
3055
|
+
}
|
|
3056
|
+
return n;
|
|
3057
|
+
}
|
|
3058
|
+
function awkRuns(words) {
|
|
3059
|
+
let program;
|
|
3060
|
+
for (let i = 0; i < words.length && program === void 0; i++) {
|
|
3061
|
+
const w = words[i];
|
|
3062
|
+
if (w === "--") program = words[i + 1];
|
|
3063
|
+
else if (/^(-[fiEl]|--(file|include|load|exec))/.test(w)) return true;
|
|
3064
|
+
else if (/^(-e|--source)$/.test(w)) program = words[i + 1];
|
|
3065
|
+
else if (/^(-[FvW]|--(field-separator|assign))$/.test(w)) i++;
|
|
3066
|
+
else if (!w.startsWith("-")) program = w;
|
|
3067
|
+
}
|
|
3068
|
+
return program !== void 0 && /\bsystem\s*\(|\|\s*&?\s*getline|\bprintf?\b[^;{}\n]*[>|]|@(load|include)\b/.test(program);
|
|
3069
|
+
}
|
|
3070
|
+
function sedWrites(words) {
|
|
3071
|
+
const scripts = [];
|
|
3072
|
+
let first;
|
|
3073
|
+
for (let i = 0; i < words.length; i++) {
|
|
3074
|
+
const w = words[i];
|
|
3075
|
+
if (w === "--") {
|
|
3076
|
+
first ??= words[i + 1];
|
|
3077
|
+
break;
|
|
3078
|
+
}
|
|
3079
|
+
if (w.startsWith("--")) {
|
|
3080
|
+
const [name, value] = w.split(/=(.*)/s);
|
|
3081
|
+
if (name === "--in-place" || name === "--file") return true;
|
|
3082
|
+
if (name === "--expression") scripts.push(value ?? words[++i] ?? "");
|
|
3083
|
+
else if (name === "--line-length" && value === void 0) i++;
|
|
3084
|
+
} else if (/^-./.test(w)) {
|
|
3085
|
+
for (let k = 1; k < w.length; k++) {
|
|
3086
|
+
const c = w[k];
|
|
3087
|
+
if (c === "i" || c === "f") return true;
|
|
3088
|
+
if (c === "e" || c === "l") {
|
|
3089
|
+
const value = w.slice(k + 1) || words[++i] || "";
|
|
3090
|
+
if (c === "e") scripts.push(value);
|
|
3091
|
+
break;
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
} else if (first === void 0) first = w;
|
|
3095
|
+
}
|
|
3096
|
+
if (!scripts.length && first !== void 0) scripts.push(first);
|
|
3097
|
+
return scripts.some(sedScriptWrites);
|
|
3098
|
+
}
|
|
3099
|
+
function sedScriptWrites(script) {
|
|
3100
|
+
if (script.length > 2048) return true;
|
|
3101
|
+
let flagged = false;
|
|
3102
|
+
const t = script.replace(
|
|
3103
|
+
/(^|[^a-zA-Z\\])([sy])([^\\\n\sa-zA-Z0-9])((?:\\[\s\S]|(?!\3)[^\\])*)\3((?:\\[\s\S]|(?!\3)[^\\])*)\3([a-zA-Z0-9]*)/g,
|
|
3104
|
+
(_m, pre, cmd, _d, _a, _b, flags) => {
|
|
3105
|
+
if (cmd === "s" && /[we]/.test(flags)) flagged = true;
|
|
3106
|
+
return `${pre};`;
|
|
3107
|
+
}
|
|
3108
|
+
).replace(/\/(?:\\[\s\S]|[^/\\\n])*\//g, ";");
|
|
3109
|
+
return flagged || /(?:^|[^a-zA-Z_.-])(?:[wW]|e(?![a-zA-Z]))/.test(t);
|
|
3110
|
+
}
|
|
3111
|
+
var FLAG_WRITES = /* @__PURE__ */ new Map([
|
|
3112
|
+
// `--pre cmd` runs cmd on every file searched
|
|
3113
|
+
["rg", (w) => w.some((x) => /^--pre(-glob)?(=|$)/.test(x))],
|
|
3114
|
+
["git", gitRunsOrWrites],
|
|
3115
|
+
["find", (w) => w.some((x) => /^-(delete|exec|execdir|ok|okdir|fprint0?|fprintf|fls)$/.test(x))],
|
|
3116
|
+
["tree", (w) => w.some((x) => /^(-[a-zA-Z]*o|--output)/.test(x))],
|
|
3117
|
+
["sed", sedWrites],
|
|
3118
|
+
["gsed", sedWrites],
|
|
3119
|
+
["awk", awkRuns],
|
|
3120
|
+
["gawk", awkRuns],
|
|
3121
|
+
["mawk", awkRuns],
|
|
3122
|
+
["nawk", awkRuns],
|
|
3123
|
+
["sort", (w) => w.some((x) => /^(-[a-zA-Z]*o|--output(=|$))/.test(x))],
|
|
3124
|
+
// `uniq IN OUT` and `xxd IN OUT` write OUT
|
|
3125
|
+
["uniq", (w) => operands(w, /^(-[fsw]|--(skip-fields|skip-chars|check-chars))$/) > 1],
|
|
3126
|
+
["xxd", (w) => operands(w, /^-(c|g|l|o|s|n|cols|groupsize|len|offset|seek|name)$/) > 1],
|
|
3127
|
+
["yq", (w) => w.some((x) => /^(-[a-zA-Z]*i[a-zA-Z]*|--inplace)(=|$)/.test(x))]
|
|
3128
|
+
]);
|
|
3129
|
+
function gitRunsOrWrites(words) {
|
|
3130
|
+
let i = 0;
|
|
3131
|
+
for (; i < words.length && words[i].startsWith("-"); i++) {
|
|
3132
|
+
const w = words[i];
|
|
3133
|
+
if (/^(-c|--config-env|--exec-path=)/.test(w)) return true;
|
|
3134
|
+
if (/^(-C|--git-dir|--work-tree|--namespace|--super-prefix)$/.test(w)) i++;
|
|
3135
|
+
}
|
|
3136
|
+
const sub = words[i];
|
|
3137
|
+
const rest = words.slice(i + 1);
|
|
3138
|
+
if (sub === "difftool" || sub === "mergetool") return true;
|
|
3139
|
+
if (rest.some((w) => /^--(output|ext-diff)(=|$)/.test(w))) return true;
|
|
3140
|
+
return sub === "grep" && rest.some((w) => /^(-[a-zA-Z]*O|--open-files-in-pager)/.test(w));
|
|
3141
|
+
}
|
|
3142
|
+
var GIT_ENV = /(?:^|\s)GIT_(?:EXTERNAL_DIFF|PAGER|EDITOR|SEQUENCE_EDITOR|SSH|SSH_COMMAND|ASKPASS|PROXY_COMMAND|EXEC_PATH|CONFIG\w*|TRACE\w*|DIFF_OPTS|TEMPLATE_DIR)=/;
|
|
3143
|
+
var ARITHMETIC = /\$\(\(([^()`]*)\)\)/g;
|
|
3144
|
+
function substitutions(segment) {
|
|
3145
|
+
const found = [];
|
|
3146
|
+
const inner = /[$<>]\(([^()]*)\)|`([^`]*)`/g;
|
|
3147
|
+
let s = segment.replace(ARITHMETIC, "_");
|
|
3148
|
+
for (let level = 0; ; level++) {
|
|
3149
|
+
const next = s.replace(inner, (_, a, b) => {
|
|
3150
|
+
found.push(a ?? b ?? "");
|
|
3151
|
+
return "_";
|
|
3152
|
+
});
|
|
3153
|
+
if (next === s) break;
|
|
3154
|
+
if (level >= MAX_STEPS) return void 0;
|
|
3155
|
+
s = next;
|
|
3156
|
+
}
|
|
3157
|
+
return /[$<>]\(/.test(s) ? void 0 : found;
|
|
3158
|
+
}
|
|
3159
|
+
function staysNative(segment, cwd) {
|
|
3160
|
+
if (WRITES_FILE.test(segment)) return true;
|
|
3161
|
+
if (GLUED_WRITE.test(segment.replace(/'[^']*'|"(?:[^"\\]|\\.)*"/g, "''"))) return true;
|
|
3162
|
+
return pipeStages(segment).some((stage, i) => {
|
|
3163
|
+
const plain = stage.replace(SHELL_WORDS, "");
|
|
3164
|
+
if (plain.includes("GIT_") && GIT_ENV.test(plain)) return true;
|
|
3165
|
+
const cmd = stripRunners(plain).replace(/^\S*\//, "");
|
|
3166
|
+
const [, quoted = "", args = ""] = /^(\S+)([\s\S]*)$/.exec(cmd) ?? [];
|
|
3167
|
+
const tool = quoted.replace(/['"\\]/g, "");
|
|
3168
|
+
const named = tool + args;
|
|
3169
|
+
const bare = /^(npm|pnpm|yarn)$/.test(tool) ? `${tool} ${afterOptions(tool, args).join(" ")}` : named;
|
|
3170
|
+
return MUTATING.test(named) || MUTATING.test(bare) || (/^(?:kubectl|docker|tail)(?![^\s])/.test(named) || named.includes("--watch")) && FOLLOWS.test(named) || i > 0 && RUNS_STDIN.test(named) || CHANGES.get(tool)?.(args, cwd) === true || FLAG_WRITES.get(tool)?.(shellWords(args)) === true;
|
|
3171
|
+
});
|
|
3172
|
+
}
|
|
3173
|
+
var READ_ONLY = /^(?:(?:cd|pushd|popd|pwd|true|false|:|test|\[|sleep|echo|printf|ls|tree|cat|head|tail|wc|grep|egrep|fgrep|rg|sort|uniq|cut|tr|nl|column|awk|sed|jq|yq|diff|cmp|du|df|stat|file|which|type|date|basename|dirname|realpath|readlink|ps|lsof|strings|xxd|od|find)|git\s+(?:(?:-[Cc]\s*[^-\s]\S*|--no-pager)\s+)*(?:diff|log|show|status|ls-files|ls-tree|grep|blame|branch(?=\s*$|\s+(?:-[avr]+|--(?:list|show-current|merged|no-merged|contains))(?:\s|$))|rev-list|rev-parse|for-each-ref|cat-file|describe|shortlog|reflog|merge-base|name-rev|config\s+(?:--(?:global|local|system|worktree)\s+)*(?:--get\S*|--list|-l)))(?![\w-])/;
|
|
3174
|
+
var TOOL_READS = {
|
|
3175
|
+
gh: (a) => /^\s+(?:api|(?:pr|issue|run|search|repo|release)\s+(?:list|view|diff|checks|status))\b/.test(a),
|
|
3176
|
+
npm: (a) => /^(?:test|t|run|run-script|ls|ll|list|outdated|view|show|info|why|explain|audit|root|bin|prefix|help)$/.test(
|
|
3177
|
+
afterOptions("npm", a)[0] ?? ""
|
|
3178
|
+
),
|
|
3179
|
+
pnpm: (a) => afterOptions("pnpm", a).length > 0,
|
|
3180
|
+
yarn: (a) => afterOptions("yarn", a).length > 0
|
|
3181
|
+
// bare `yarn` installs
|
|
3182
|
+
};
|
|
3183
|
+
function readsOnly(segment, cwd) {
|
|
3184
|
+
return pipeStages(segment).every((stage) => {
|
|
3185
|
+
const cmd = stripRunners(stage.replace(SHELL_WORDS, "")).replace(/^\S*\//, "");
|
|
3186
|
+
const [, tool = "", args = ""] = /^(\S+)([\s\S]*)$/.exec(cmd) ?? [];
|
|
3187
|
+
if (/(?:^|\s)--output(?=[\s=]|$)/.test(args) || tool === "sort" && /\s-\w*o\b/.test(args))
|
|
3188
|
+
return false;
|
|
3189
|
+
if (CHANGES.has(tool)) return TOOL_READS[tool]?.(args) ?? !CHANGES.get(tool)(args, cwd);
|
|
3190
|
+
return READ_ONLY.test(cmd);
|
|
3191
|
+
});
|
|
2553
3192
|
}
|
|
2554
3193
|
var READ_LIMIT = 200 * 1024;
|
|
2555
3194
|
var BIG_TEXT_LIMIT = 20 * 1024;
|
|
@@ -2598,13 +3237,13 @@ function fileOperands(stage, cwd) {
|
|
|
2598
3237
|
}
|
|
2599
3238
|
files.push(t);
|
|
2600
3239
|
}
|
|
2601
|
-
if (!files.length) return void 0;
|
|
3240
|
+
if (!files.length || files.length > MAX_OPERANDS) return void 0;
|
|
2602
3241
|
const out = [];
|
|
2603
3242
|
for (let f of files) {
|
|
2604
3243
|
if (/[*?[\]{}$`\\]/.test(f)) return void 0;
|
|
2605
3244
|
f = f.replace(/^['"]|['"]$/g, "");
|
|
2606
3245
|
if (f.startsWith("~/")) f = join7(homedir2(), f.slice(2));
|
|
2607
|
-
out.push(
|
|
3246
|
+
out.push(resolve3(cwd, f));
|
|
2608
3247
|
}
|
|
2609
3248
|
return out;
|
|
2610
3249
|
}
|
|
@@ -2642,66 +3281,99 @@ function firstStageBound(first, cwd) {
|
|
|
2642
3281
|
const bytes = stageBytes(first, cwd);
|
|
2643
3282
|
if (bytes === void 0) return void 0;
|
|
2644
3283
|
if (bytes <= READ_LIMIT) return true;
|
|
2645
|
-
return {
|
|
3284
|
+
return {
|
|
3285
|
+
rule: "big-read",
|
|
3286
|
+
text: `reads about ${Math.round(bytes / 1024)} KB`
|
|
3287
|
+
};
|
|
2646
3288
|
}
|
|
2647
3289
|
return void 0;
|
|
2648
3290
|
}
|
|
3291
|
+
var READ_SOURCE = /^\s*(ls|cat|sed|awk|cut|sort|uniq|tr|nl|echo|printf|ps|lsof|jq|yq|diff|strings|xxd|od|git\s+(diff|ls-files|ls-tree|grep|status|show|log|blame|branch|rev-list|for-each-ref|cat-file)|gh\s+(api|pr\s+(list|view|diff|checks)|issue\s+(list|view)|run\s+(list|view)|search))\b/;
|
|
3292
|
+
var FILTERS = /^\s*(grep|egrep|fgrep|rg|sort|uniq|jq|head|tail|wc|cut|tr|column|less|awk|sed|python3?\s+-m\s+json\.tool)\b/;
|
|
3293
|
+
var UNBOUNDED_FILTERS = /^\s*(grep|rg|sort|uniq|jq|less)\b/;
|
|
2649
3294
|
function heavySegment(segment, cwd) {
|
|
2650
3295
|
if (TINY_BASH.test(segment)) return void 0;
|
|
2651
3296
|
const stages = pipeStages(segment);
|
|
2652
3297
|
const first = stages[0];
|
|
2653
3298
|
if (!first) return void 0;
|
|
2654
3299
|
if (stages.length > 1 && bounds(stages[stages.length - 1])) return void 0;
|
|
3300
|
+
const rest = stages.slice(1);
|
|
3301
|
+
if (!rest.every((s) => FILTERS.test(s))) return void 0;
|
|
2655
3302
|
const bound = firstStageBound(first, cwd);
|
|
2656
3303
|
if (bound === true) return void 0;
|
|
2657
3304
|
if (bound) return bound;
|
|
2658
3305
|
if (HEAVY_BASH.test(first)) return { rule: "heavy", text: "usually prints a lot" };
|
|
2659
|
-
if (
|
|
2660
|
-
return {
|
|
3306
|
+
if (READ_SOURCE.test(first) && rest.some((s) => UNBOUNDED_FILTERS.test(s)))
|
|
3307
|
+
return {
|
|
3308
|
+
rule: "pipe",
|
|
3309
|
+
text: "streams an unbounded output through a filter"
|
|
3310
|
+
};
|
|
2661
3311
|
return void 0;
|
|
2662
3312
|
}
|
|
2663
3313
|
function headOf(segment, n = 60) {
|
|
2664
3314
|
const one = segment.replace(/\s+/g, " ").trim();
|
|
2665
3315
|
return one.length > n ? `${one.slice(0, n - 1)}\u2026` : one;
|
|
2666
3316
|
}
|
|
2667
|
-
function bashSuggestion(raw, cwd) {
|
|
3317
|
+
function bashSuggestion(raw, root, cwd) {
|
|
3318
|
+
if (raw.length > MAX_RAW) return void 0;
|
|
2668
3319
|
const cmd = withoutHeredocs(raw);
|
|
2669
3320
|
if (!cmd.trim()) return void 0;
|
|
3321
|
+
if (cmd.length > MAX_CLASSIFIED) return void 0;
|
|
2670
3322
|
const segments = commandSegments(cmd);
|
|
2671
|
-
if (segments.
|
|
3323
|
+
if (segments.length > MAX_SEGMENTS) return void 0;
|
|
3324
|
+
const subs = substitutions(cmd);
|
|
3325
|
+
if (!subs) return void 0;
|
|
3326
|
+
if ([...segments, ...subs.flatMap(commandSegments)].some((s) => staysNative(s, cwd)))
|
|
3327
|
+
return void 0;
|
|
3328
|
+
if (segments.length > 1 && !segments.every((s) => readsOnly(s, cwd))) return void 0;
|
|
2672
3329
|
const history = historyCall(cmd);
|
|
2673
|
-
|
|
3330
|
+
const dir = history && historyDir(segments, cwd);
|
|
3331
|
+
if (history && dir && !sameRepo(root, dir)) return void 0;
|
|
3332
|
+
if (history && dir && inRootRepo(root, dir)) {
|
|
2674
3333
|
return {
|
|
2675
3334
|
tool: "em_history",
|
|
2676
3335
|
rule: "history",
|
|
2677
3336
|
reason: `the git history is already classified (commit kinds, fixes traced to the change that caused them, risk per file); em_history ${history} answers this without the raw log`
|
|
2678
3337
|
};
|
|
2679
3338
|
}
|
|
3339
|
+
let at = cwd;
|
|
3340
|
+
let steps = 0;
|
|
2680
3341
|
for (const segment of segments) {
|
|
2681
|
-
const heavy = heavySegment(segment, cwd);
|
|
2682
|
-
if (
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
3342
|
+
const heavy = heavySegment(segment, at ?? cwd);
|
|
3343
|
+
if (heavy)
|
|
3344
|
+
return {
|
|
3345
|
+
tool: "em_execute",
|
|
3346
|
+
rule: heavy.rule,
|
|
3347
|
+
reason: `\`${headOf(segment)}\` ${heavy.text}; em_execute {language:"shell", code, intent} keeps the output out of context and returns only matching sections`
|
|
3348
|
+
};
|
|
3349
|
+
const cd = CD.exec(segment);
|
|
3350
|
+
if (cd) at = ++steps > MAX_STEPS ? void 0 : at && changeDir(at, cd[1]);
|
|
2688
3351
|
}
|
|
2689
3352
|
return void 0;
|
|
2690
3353
|
}
|
|
3354
|
+
function executeFileTakes(root, fp) {
|
|
3355
|
+
try {
|
|
3356
|
+
const real = realpathSync3(root);
|
|
3357
|
+
confinePath(real, resolve3(root, fp), loadPermissionRules(real));
|
|
3358
|
+
return true;
|
|
3359
|
+
} catch {
|
|
3360
|
+
return false;
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
2691
3363
|
function suggest(toolName, input, root, extra = {}) {
|
|
2692
3364
|
switch (toolName) {
|
|
2693
3365
|
case "Bash":
|
|
2694
|
-
return bashSuggestion(String(input.command ?? ""), extra.cwd ?? root);
|
|
3366
|
+
return bashSuggestion(String(input.command ?? ""), root, extra.cwd ?? root);
|
|
2695
3367
|
case "Read": {
|
|
2696
3368
|
const fp = String(input.file_path ?? "");
|
|
2697
3369
|
if (!fp) return void 0;
|
|
3370
|
+
if (input.offset != null || input.limit != null) return void 0;
|
|
2698
3371
|
const ext = extname(fp).toLowerCase();
|
|
2699
3372
|
if (VISUAL_EXT.has(ext)) return void 0;
|
|
2700
3373
|
try {
|
|
2701
|
-
const
|
|
2702
|
-
const st = statSync3(abs);
|
|
3374
|
+
const st = statSync3(resolve3(root, fp));
|
|
2703
3375
|
if (st.size > 200 * 1024 || BIG_TEXT_EXT.has(ext) && st.size > 20 * 1024) {
|
|
2704
|
-
if (!
|
|
3376
|
+
if (!executeFileTakes(root, fp)) return void 0;
|
|
2705
3377
|
return {
|
|
2706
3378
|
tool: "em_execute_file",
|
|
2707
3379
|
rule: "big-read",
|
|
@@ -2768,12 +3440,12 @@ function subagentPrefix(shell) {
|
|
|
2768
3440
|
}
|
|
2769
3441
|
function projectRel(ctx, fp) {
|
|
2770
3442
|
if (!fp) return void 0;
|
|
2771
|
-
let abs =
|
|
3443
|
+
let abs = resolve3(ctx.root, fp);
|
|
2772
3444
|
try {
|
|
2773
|
-
abs =
|
|
3445
|
+
abs = realpathSync3(abs);
|
|
2774
3446
|
} catch {
|
|
2775
3447
|
}
|
|
2776
|
-
const rel =
|
|
3448
|
+
const rel = relative2(ctx.root, abs).split("\\").join("/");
|
|
2777
3449
|
return rel.startsWith("..") ? void 0 : rel;
|
|
2778
3450
|
}
|
|
2779
3451
|
function graphContext(ctx, input) {
|
|
@@ -2804,7 +3476,11 @@ function fileKnowledge(ctx, input) {
|
|
|
2804
3476
|
const repeatAfter = ctx.config.recall.repeatAfter;
|
|
2805
3477
|
const edit = EDIT_TOOLS.has(input.tool_name ?? "");
|
|
2806
3478
|
const results = [
|
|
2807
|
-
memoriesForFile(db, rel, {
|
|
3479
|
+
memoriesForFile(db, rel, {
|
|
3480
|
+
sessionId,
|
|
3481
|
+
symbols: fileSymbols(db, rel),
|
|
3482
|
+
repeatAfter
|
|
3483
|
+
}),
|
|
2808
3484
|
fileContext(db, rel, { sessionId, edit, repeatAfter })
|
|
2809
3485
|
];
|
|
2810
3486
|
const shown = results.filter((r) => r.text);
|
|
@@ -2854,7 +3530,11 @@ async function webKnowledge(ctx, input) {
|
|
|
2854
3530
|
if (!text.trim()) return void 0;
|
|
2855
3531
|
try {
|
|
2856
3532
|
const db = getStorage(ctx, { vectors: false });
|
|
2857
|
-
const r = await recallForText(db, text, {
|
|
3533
|
+
const r = await recallForText(db, text, {
|
|
3534
|
+
maxMemories: 0,
|
|
3535
|
+
maxDocs: 3,
|
|
3536
|
+
path: "web"
|
|
3537
|
+
});
|
|
2858
3538
|
if (!r.text) return void 0;
|
|
2859
3539
|
const pages = r.text.split("\n").filter((l) => l.startsWith("- docs:"));
|
|
2860
3540
|
return [
|
|
@@ -2872,7 +3552,12 @@ function joinHints(...hints) {
|
|
|
2872
3552
|
function withContext(out, extra) {
|
|
2873
3553
|
if (!extra) return out;
|
|
2874
3554
|
if (!out)
|
|
2875
|
-
return {
|
|
3555
|
+
return {
|
|
3556
|
+
hookSpecificOutput: {
|
|
3557
|
+
hookEventName: "PreToolUse",
|
|
3558
|
+
additionalContext: extra
|
|
3559
|
+
}
|
|
3560
|
+
};
|
|
2876
3561
|
const cur = out.hookSpecificOutput?.additionalContext;
|
|
2877
3562
|
return {
|
|
2878
3563
|
...out,
|
|
@@ -2894,7 +3579,7 @@ var preToolUse = async (input) => {
|
|
|
2894
3579
|
const out = await route(ctx, input, spawn?.kind === "worker");
|
|
2895
3580
|
if (!ctx.config.adjudicator.enabled || input.client === "cursor") return out;
|
|
2896
3581
|
try {
|
|
2897
|
-
const { afterRouting } = await import("./hook-hooks-
|
|
3582
|
+
const { afterRouting } = await import("./hook-hooks-5U2AI6W7.js");
|
|
2898
3583
|
return await afterRouting(ctx, input, spawn, out);
|
|
2899
3584
|
} catch {
|
|
2900
3585
|
return out;
|
|
@@ -2914,7 +3599,10 @@ async function route(ctx, input, swarmWorker) {
|
|
|
2914
3599
|
const graph = graphContext(ctx, input);
|
|
2915
3600
|
const hint = joinHints(graph, memo);
|
|
2916
3601
|
const symbol = input.tool_name === "Grep" && graph ? literalOf(String(input.tool_input.pattern ?? "")) : void 0;
|
|
2917
|
-
const s = suggest(input.tool_name, input.tool_input, ctx.root, {
|
|
3602
|
+
const s = suggest(input.tool_name, input.tool_input, ctx.root, {
|
|
3603
|
+
symbol,
|
|
3604
|
+
cwd: input.cwd
|
|
3605
|
+
});
|
|
2918
3606
|
const record = routingRecorder(ctx, input, s);
|
|
2919
3607
|
if (!s) {
|
|
2920
3608
|
record("allow");
|
|
@@ -3034,12 +3722,12 @@ function annotate(s, note = "") {
|
|
|
3034
3722
|
|
|
3035
3723
|
// src/hooks/session-start.ts
|
|
3036
3724
|
init_esm_shims();
|
|
3037
|
-
import { appendFileSync, existsSync as
|
|
3725
|
+
import { appendFileSync, existsSync as existsSync8, readdirSync as readdirSync4 } from "fs";
|
|
3038
3726
|
import { join as join9 } from "path";
|
|
3039
3727
|
|
|
3040
3728
|
// src/session/current.ts
|
|
3041
3729
|
init_esm_shims();
|
|
3042
|
-
import { readFileSync as
|
|
3730
|
+
import { readFileSync as readFileSync6, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
3043
3731
|
import { join as join8 } from "path";
|
|
3044
3732
|
function file(ctx) {
|
|
3045
3733
|
return join8(projectDir(ctx.id), "current-session");
|
|
@@ -3131,7 +3819,7 @@ ${guide}`);
|
|
|
3131
3819
|
if (swarm) parts.push(swarm);
|
|
3132
3820
|
if (ctx.config.adjudicator.enabled && input.client !== "cursor") {
|
|
3133
3821
|
try {
|
|
3134
|
-
const { onSessionStart } = await import("./hook-hooks-
|
|
3822
|
+
const { onSessionStart } = await import("./hook-hooks-5U2AI6W7.js");
|
|
3135
3823
|
const line2 = onSessionStart(ctx, db, input);
|
|
3136
3824
|
if (line2) parts.push(line2);
|
|
3137
3825
|
} catch {
|
|
@@ -3153,7 +3841,7 @@ ${guide}`);
|
|
|
3153
3841
|
}
|
|
3154
3842
|
try {
|
|
3155
3843
|
const onboarding = join9(memoriesDir(ctx), "onboarding");
|
|
3156
|
-
const onboarded =
|
|
3844
|
+
const onboarded = existsSync8(onboarding) && readdirSync4(onboarding).some((f) => f.endsWith(".md"));
|
|
3157
3845
|
if (!onboarded)
|
|
3158
3846
|
parts.push(
|
|
3159
3847
|
"No onboarding memories yet: run /eminiar:em-onboard once so future sessions start with the project's structure, build and test commands."
|
|
@@ -3167,7 +3855,7 @@ ${guide}`);
|
|
|
3167
3855
|
} catch {
|
|
3168
3856
|
}
|
|
3169
3857
|
try {
|
|
3170
|
-
if (
|
|
3858
|
+
if (existsSync8(join9(ctx.root, ".git")) && !db.metaGet("history_files"))
|
|
3171
3859
|
parts.push(
|
|
3172
3860
|
'Git history is not classified yet: em_history {action:"overview"} builds it (the server also does this in the background shortly after start); bug-prone files are flagged before you read or edit them once it exists.'
|
|
3173
3861
|
);
|
|
@@ -3212,8 +3900,8 @@ function ranLate() {
|
|
|
3212
3900
|
}
|
|
3213
3901
|
async function runWithin(handler, input, ms) {
|
|
3214
3902
|
let timer;
|
|
3215
|
-
const deadline = new Promise((
|
|
3216
|
-
timer = setTimeout(() =>
|
|
3903
|
+
const deadline = new Promise((resolve4) => {
|
|
3904
|
+
timer = setTimeout(() => resolve4("late"), ms);
|
|
3217
3905
|
});
|
|
3218
3906
|
try {
|
|
3219
3907
|
const r = await Promise.race([handler(input), deadline]);
|
|
@@ -3324,7 +4012,7 @@ async function main() {
|
|
|
3324
4012
|
return;
|
|
3325
4013
|
}
|
|
3326
4014
|
if (process.argv.includes("--adjudicate")) {
|
|
3327
|
-
const { ASYNC_JOB_MAX_MS, runAsyncJob } = await import("./hook-async-
|
|
4015
|
+
const { ASYNC_JOB_MAX_MS, runAsyncJob } = await import("./hook-async-3TLK23ZY.js");
|
|
3328
4016
|
setTimeout(() => process.exit(0), ASYNC_JOB_MAX_MS).unref();
|
|
3329
4017
|
await runAsyncJob(raw, { log: (line2) => hookLog("info", line2) });
|
|
3330
4018
|
return;
|