eminiar 0.13.2 → 0.13.4
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 +109 -54
- package/dist/{hook-async-3TLK23ZY.js → hook-async-7GK6H3OO.js} +3 -3
- package/dist/{hook-chunk-ZLRLOPLR.js → hook-chunk-6EFFDI3S.js} +24 -31
- package/dist/{hook-chunk-MVSMFCKL.js → hook-chunk-77LASZ3C.js} +2 -2
- package/dist/{hook-chunk-ETYHCPPH.js → hook-chunk-G7UP4ZVA.js} +36 -4
- package/dist/{hook-hooks-5U2AI6W7.js → hook-hooks-YGXLOZ6H.js} +4 -4
- package/dist/hook.js +698 -74
- package/dist/server.js +120 -67
- package/eminiar.config.schema.json +3 -0
- package/package.json +3 -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-6EFFDI3S.js";
|
|
39
39
|
import {
|
|
40
40
|
GUIDE_ORDER,
|
|
41
41
|
classifyAssistant,
|
|
@@ -50,9 +50,10 @@ import {
|
|
|
50
50
|
getStorage,
|
|
51
51
|
init_esm_shims,
|
|
52
52
|
projectDir,
|
|
53
|
+
sessionContext,
|
|
53
54
|
setStorageBusyTimeout,
|
|
54
55
|
withPriority
|
|
55
|
-
} from "./hook-chunk-
|
|
56
|
+
} from "./hook-chunk-G7UP4ZVA.js";
|
|
56
57
|
|
|
57
58
|
// src/hook.ts
|
|
58
59
|
init_esm_shims();
|
|
@@ -1834,7 +1835,7 @@ function persist(db, sessionId, events) {
|
|
|
1834
1835
|
}
|
|
1835
1836
|
}
|
|
1836
1837
|
function open(input) {
|
|
1837
|
-
const ctx =
|
|
1838
|
+
const ctx = sessionContext(input.cwd);
|
|
1838
1839
|
const db = getStorage(ctx, { vectors: false });
|
|
1839
1840
|
const sessionId = input.session_id || "unknown";
|
|
1840
1841
|
db.sessionUpsert({
|
|
@@ -1847,7 +1848,7 @@ function open(input) {
|
|
|
1847
1848
|
var usageFailureLogged = false;
|
|
1848
1849
|
function ingestUsage(db, input, sessionId) {
|
|
1849
1850
|
try {
|
|
1850
|
-
const ctx =
|
|
1851
|
+
const ctx = sessionContext(input.cwd);
|
|
1851
1852
|
if (!usageEnabled(ctx)) return;
|
|
1852
1853
|
const overrides = ctx.config.usage.pricing;
|
|
1853
1854
|
ingestSession(db, ctx.root, sessionId, input.transcript_path, { overrides });
|
|
@@ -1864,7 +1865,7 @@ function ingestUsage(db, input, sessionId) {
|
|
|
1864
1865
|
}
|
|
1865
1866
|
var userPromptSubmit = async (input) => {
|
|
1866
1867
|
if (!input.prompt?.trim()) return void 0;
|
|
1867
|
-
const ctx =
|
|
1868
|
+
const ctx = sessionContext(input.cwd);
|
|
1868
1869
|
const db = getStorage(ctx, { vectors: false });
|
|
1869
1870
|
const sessionId = input.session_id || "unknown";
|
|
1870
1871
|
try {
|
|
@@ -1901,7 +1902,7 @@ var userPromptSubmit = async (input) => {
|
|
|
1901
1902
|
let systemMessage;
|
|
1902
1903
|
if (ctx.config.adjudicator.enabled && input.client !== "cursor" && hasBudget(ADJUDICATOR_NEEDS_MS)) {
|
|
1903
1904
|
try {
|
|
1904
|
-
const { onPrompt } = await import("./hook-hooks-
|
|
1905
|
+
const { onPrompt } = await import("./hook-hooks-YGXLOZ6H.js");
|
|
1905
1906
|
const a = await onPrompt(ctx, db, input, sessionId);
|
|
1906
1907
|
if (a?.context) parts.push(a.context);
|
|
1907
1908
|
systemMessage = a?.systemMessage;
|
|
@@ -1936,9 +1937,9 @@ function detectLoop(db, sessionId, key, summary) {
|
|
|
1936
1937
|
}
|
|
1937
1938
|
var postToolUse = async (input) => {
|
|
1938
1939
|
if (!input.tool_name) return void 0;
|
|
1939
|
-
if (input.agent_id) swarmActivity(
|
|
1940
|
+
if (input.agent_id) swarmActivity(sessionContext(input.cwd), input, false);
|
|
1940
1941
|
const isAgent = input.tool_name === "Agent" || input.tool_name === "Task";
|
|
1941
|
-
if (isAgent) swarmAgentReturned(
|
|
1942
|
+
if (isAgent) swarmAgentReturned(sessionContext(input.cwd), input);
|
|
1942
1943
|
const { db, root, sessionId } = open(input);
|
|
1943
1944
|
const drafts = classifyToolCall({
|
|
1944
1945
|
toolName: input.tool_name,
|
|
@@ -1956,10 +1957,10 @@ var postToolUse = async (input) => {
|
|
|
1956
1957
|
}
|
|
1957
1958
|
}
|
|
1958
1959
|
if (isAgent && input.client !== "cursor") {
|
|
1959
|
-
const ctx =
|
|
1960
|
+
const ctx = sessionContext(input.cwd);
|
|
1960
1961
|
if (ctx.config.adjudicator.enabled) {
|
|
1961
1962
|
try {
|
|
1962
|
-
const { onPostAgent } = await import("./hook-hooks-
|
|
1963
|
+
const { onPostAgent } = await import("./hook-hooks-YGXLOZ6H.js");
|
|
1963
1964
|
onPostAgent(ctx, db, input);
|
|
1964
1965
|
} catch {
|
|
1965
1966
|
}
|
|
@@ -1988,7 +1989,7 @@ function bestEffortWrites(db) {
|
|
|
1988
1989
|
}
|
|
1989
1990
|
var postToolUseFailure = async (input) => {
|
|
1990
1991
|
if (!input.tool_name) return void 0;
|
|
1991
|
-
const ctx =
|
|
1992
|
+
const ctx = sessionContext(input.cwd);
|
|
1992
1993
|
if (input.tool_name === "Agent" || input.tool_name === "Task") swarmAgentReturned(ctx, input);
|
|
1993
1994
|
const db = getStorage(ctx, { vectors: false });
|
|
1994
1995
|
const sessionId = input.session_id || "unknown";
|
|
@@ -2028,7 +2029,7 @@ var postToolUseFailure = async (input) => {
|
|
|
2028
2029
|
return recalled ? { hookSpecificOutput: { hookEventName: "PostToolUseFailure", additionalContext: recalled } } : void 0;
|
|
2029
2030
|
};
|
|
2030
2031
|
var subagentStop = (input) => {
|
|
2031
|
-
if (input.agent_id) swarmSubagentStop(
|
|
2032
|
+
if (input.agent_id) swarmSubagentStop(sessionContext(input.cwd), input);
|
|
2032
2033
|
const { db, sessionId } = open(input);
|
|
2033
2034
|
const msg = input.last_assistant_message ?? "";
|
|
2034
2035
|
if (msg.trim()) {
|
|
@@ -2054,7 +2055,7 @@ var stop = (input) => {
|
|
|
2054
2055
|
return void 0;
|
|
2055
2056
|
};
|
|
2056
2057
|
var sessionEnd = async (input) => {
|
|
2057
|
-
const ctx =
|
|
2058
|
+
const ctx = sessionContext(input.cwd);
|
|
2058
2059
|
swarmSessionEnd(ctx, input);
|
|
2059
2060
|
const { db, sessionId } = open(input);
|
|
2060
2061
|
db.sessionUpsert({ id: sessionId, ended_at: Date.now() });
|
|
@@ -2064,7 +2065,7 @@ var sessionEnd = async (input) => {
|
|
|
2064
2065
|
ingestUsage(db, input, sessionId);
|
|
2065
2066
|
if (ctx.config.adjudicator.enabled && input.client !== "cursor") {
|
|
2066
2067
|
try {
|
|
2067
|
-
const { onSessionEnd } = await import("./hook-hooks-
|
|
2068
|
+
const { onSessionEnd } = await import("./hook-hooks-YGXLOZ6H.js");
|
|
2068
2069
|
onSessionEnd(ctx, db, input);
|
|
2069
2070
|
} catch {
|
|
2070
2071
|
}
|
|
@@ -2186,7 +2187,7 @@ function renderGuide(db, sessionId, budget = 2048) {
|
|
|
2186
2187
|
// src/hooks/pre-compact.ts
|
|
2187
2188
|
var SNAPSHOT_BUDGET = 2048;
|
|
2188
2189
|
var preCompact = (input) => {
|
|
2189
|
-
const ctx =
|
|
2190
|
+
const ctx = sessionContext(input.cwd);
|
|
2190
2191
|
const db = getStorage(ctx, { vectors: false });
|
|
2191
2192
|
const sessionId = input.session_id || "unknown";
|
|
2192
2193
|
const body = renderGuide(db, sessionId, SNAPSHOT_BUDGET);
|
|
@@ -2197,9 +2198,9 @@ var preCompact = (input) => {
|
|
|
2197
2198
|
|
|
2198
2199
|
// src/hooks/routing.ts
|
|
2199
2200
|
init_esm_shims();
|
|
2200
|
-
import { realpathSync as
|
|
2201
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, realpathSync as realpathSync3, statSync as statSync3 } from "fs";
|
|
2201
2202
|
import { homedir as homedir2 } from "os";
|
|
2202
|
-
import { extname, join as join7, relative, resolve as
|
|
2203
|
+
import { dirname as dirname3, extname, join as join7, relative as relative2, resolve as resolve3 } from "path";
|
|
2203
2204
|
|
|
2204
2205
|
// src/permissions/settings.ts
|
|
2205
2206
|
init_esm_shims();
|
|
@@ -2364,6 +2365,36 @@ function checkReadPath(rules, abs) {
|
|
|
2364
2365
|
return more.length ? checkRead(rules, more) : asWritten;
|
|
2365
2366
|
}
|
|
2366
2367
|
|
|
2368
|
+
// src/sandbox/file.ts
|
|
2369
|
+
init_esm_shims();
|
|
2370
|
+
import { existsSync as existsSync4, realpathSync as realpathSync2 } from "fs";
|
|
2371
|
+
import { isAbsolute, relative, resolve as resolve2 } from "path";
|
|
2372
|
+
function confinePath(root, target, given) {
|
|
2373
|
+
const abs = resolve2(root, target);
|
|
2374
|
+
const rules = given.root ? given : { ...given, root };
|
|
2375
|
+
refuse(checkRead(rules, [abs]), target);
|
|
2376
|
+
let real;
|
|
2377
|
+
try {
|
|
2378
|
+
real = realpathSync2(abs);
|
|
2379
|
+
} catch {
|
|
2380
|
+
throw new Error(`file not found: ${target}`);
|
|
2381
|
+
}
|
|
2382
|
+
refuse(checkReadPath(rules, abs), target);
|
|
2383
|
+
const rel = relative(root, real);
|
|
2384
|
+
const inside = rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
2385
|
+
if (inside) return real;
|
|
2386
|
+
if (readMatcher(rules.allow, rules.root).test(real)) return real;
|
|
2387
|
+
throw new Error(
|
|
2388
|
+
`${target} is outside the project root; add a Read(/${real}) rule to permissions.allow to opt it in`
|
|
2389
|
+
);
|
|
2390
|
+
}
|
|
2391
|
+
function refusal(check, target) {
|
|
2392
|
+
return `refused by permission rule ${check.rule} (${check.kind}) on path: ${target}`;
|
|
2393
|
+
}
|
|
2394
|
+
function refuse(check, target) {
|
|
2395
|
+
if (!check.allowed) throw new Error(refusal(check, target));
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2367
2398
|
// src/session/routing-stats.ts
|
|
2368
2399
|
init_esm_shims();
|
|
2369
2400
|
var ROUTING_CATEGORY = "routing";
|
|
@@ -2386,7 +2417,7 @@ function recordRouting(db, sessionId, e) {
|
|
|
2386
2417
|
|
|
2387
2418
|
// src/session/server-alive.ts
|
|
2388
2419
|
init_esm_shims();
|
|
2389
|
-
import { existsSync as
|
|
2420
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync as readdirSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
2390
2421
|
import { join as join4 } from "path";
|
|
2391
2422
|
function markersDir(ctx) {
|
|
2392
2423
|
return join4(projectDir(ctx.id), "servers");
|
|
@@ -2401,7 +2432,7 @@ function processAlive(pid) {
|
|
|
2401
2432
|
}
|
|
2402
2433
|
function liveServers(ctx) {
|
|
2403
2434
|
const dir = markersDir(ctx);
|
|
2404
|
-
if (!
|
|
2435
|
+
if (!existsSync5(dir)) return [];
|
|
2405
2436
|
const live = [];
|
|
2406
2437
|
for (const name of readdirSync2(dir)) {
|
|
2407
2438
|
const pid = Number(name);
|
|
@@ -2424,7 +2455,7 @@ import { join as join6 } from "path";
|
|
|
2424
2455
|
init_esm_shims();
|
|
2425
2456
|
import {
|
|
2426
2457
|
copyFileSync,
|
|
2427
|
-
existsSync as
|
|
2458
|
+
existsSync as existsSync6,
|
|
2428
2459
|
mkdirSync as mkdirSync3,
|
|
2429
2460
|
readdirSync as readdirSync3,
|
|
2430
2461
|
readFileSync as readFileSync3,
|
|
@@ -2469,7 +2500,11 @@ function literalOf(pattern) {
|
|
|
2469
2500
|
function symbolHints(db, pattern) {
|
|
2470
2501
|
const lit = literalOf(pattern);
|
|
2471
2502
|
if (!lit) return void 0;
|
|
2472
|
-
const nodes = db.graphFindNodes({
|
|
2503
|
+
const nodes = db.graphFindNodes({
|
|
2504
|
+
query: lit,
|
|
2505
|
+
kinds: SYMBOL_KINDS,
|
|
2506
|
+
limit: 5
|
|
2507
|
+
});
|
|
2473
2508
|
if (!nodes.length) return void 0;
|
|
2474
2509
|
const list = nodes.map((n) => `${n.kind} ${n.name} (${n.path}:${n.start_line ?? "?"})`).join("; ");
|
|
2475
2510
|
return `eminiar graph: symbols matching "${lit}": ${list}. em_symbols {name_path, include_body: true} reads one; {mode:"references"} lists its callers.`;
|
|
@@ -2479,13 +2514,13 @@ function coverageHint(db, rel) {
|
|
|
2479
2514
|
if (!row || row.status === "indexed") return void 0;
|
|
2480
2515
|
return `eminiar graph: ${rel} is ${row.status}${row.reason ? ` (${row.reason})` : ""}; graph answers about it may be incomplete.`;
|
|
2481
2516
|
}
|
|
2482
|
-
var GIT_HISTORY = /(?:^|[;&|]\s*)git\s+(?:(?:-C|--git-dir|--work-tree)[= ]\S+\s+|--no-pager\s+)*(log|blame|shortlog|show|whatchanged|reflog)\b([^;&|]*)/;
|
|
2517
|
+
var GIT_HISTORY = /(?:^|[;&|]\s*)git\s+((?:(?:-C|--git-dir|--work-tree)[= ]\S+\s+|--no-pager\s+)*)(log|blame|shortlog|show|whatchanged|reflog)\b([^;&|]*)/;
|
|
2483
2518
|
var ONE_COMMIT = /(^|\s)(-1|-n\s*1|-n1|--max-count[= ]1)(?=\s|$)/;
|
|
2484
2519
|
function historyCall(cmd) {
|
|
2485
2520
|
const m = GIT_HISTORY.exec(cmd);
|
|
2486
2521
|
if (!m) return void 0;
|
|
2487
|
-
const sub = m[
|
|
2488
|
-
const rest = (m[
|
|
2522
|
+
const sub = m[2];
|
|
2523
|
+
const rest = (m[3] ?? "").trim();
|
|
2489
2524
|
const args = rest.split(/\s+/).filter(Boolean);
|
|
2490
2525
|
const q = (s) => JSON.stringify(s.replace(/^['"]|['"]$/g, ""));
|
|
2491
2526
|
const positional = (a) => !a.startsWith("-");
|
|
@@ -2520,6 +2555,66 @@ function historyCall(cmd) {
|
|
|
2520
2555
|
if (sub === "shortlog") return `{action:"timeline"}`;
|
|
2521
2556
|
return `{action:"overview"}`;
|
|
2522
2557
|
}
|
|
2558
|
+
function historyDir(segments, cwd) {
|
|
2559
|
+
let dir = cwd;
|
|
2560
|
+
let steps = 0;
|
|
2561
|
+
const step = (arg) => {
|
|
2562
|
+
dir = ++steps > MAX_STEPS ? void 0 : dir && changeDir(dir, arg);
|
|
2563
|
+
};
|
|
2564
|
+
for (const segment of segments) {
|
|
2565
|
+
const m = GIT_HISTORY.exec(segment);
|
|
2566
|
+
if (m) {
|
|
2567
|
+
const opts = m[1] ?? "";
|
|
2568
|
+
if (/--(git-dir|work-tree)\b/.test(opts)) return void 0;
|
|
2569
|
+
for (const c of opts.matchAll(/-C[= ](\S+)/g)) {
|
|
2570
|
+
step(c[1]);
|
|
2571
|
+
if (!dir) return void 0;
|
|
2572
|
+
}
|
|
2573
|
+
return dir;
|
|
2574
|
+
}
|
|
2575
|
+
const cd = CD.exec(segment);
|
|
2576
|
+
if (cd) step(cd[1]);
|
|
2577
|
+
if (!dir) return void 0;
|
|
2578
|
+
}
|
|
2579
|
+
return dir;
|
|
2580
|
+
}
|
|
2581
|
+
var CD = /^\(?\s*(?:cd|pushd)\b(.*)$/;
|
|
2582
|
+
var MAX_STEPS = 16;
|
|
2583
|
+
var MAX_RAW = 64 * 1024;
|
|
2584
|
+
var MAX_CLASSIFIED = 8 * 1024;
|
|
2585
|
+
var MAX_OPERANDS = 64;
|
|
2586
|
+
var MAX_SEGMENTS = 64;
|
|
2587
|
+
function changeDir(dir, arg) {
|
|
2588
|
+
const a = arg.trim().replace(/^(['"])(.*)\1$/, "$2") || "~";
|
|
2589
|
+
if (/^-|^~[^/]|[\s$`*?[\]{}()\\]/.test(a)) return void 0;
|
|
2590
|
+
return a === "~" || a.startsWith("~/") ? join7(homedir2(), a.slice(1)) : resolve3(dir, a);
|
|
2591
|
+
}
|
|
2592
|
+
function inRootRepo(root, dir) {
|
|
2593
|
+
try {
|
|
2594
|
+
const top = realpathSync3(root);
|
|
2595
|
+
let inside = false;
|
|
2596
|
+
for (let d = realpathSync3(dir); ; d = dirname3(d)) {
|
|
2597
|
+
if (d === top) inside = true;
|
|
2598
|
+
if (existsSync7(join7(d, ".git"))) return inside;
|
|
2599
|
+
if (dirname3(d) === d) return false;
|
|
2600
|
+
}
|
|
2601
|
+
} catch {
|
|
2602
|
+
return false;
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
function repoOf(dir) {
|
|
2606
|
+
try {
|
|
2607
|
+
for (let d = realpathSync3(dir); ; d = dirname3(d)) {
|
|
2608
|
+
if (existsSync7(join7(d, ".git"))) return d;
|
|
2609
|
+
if (dirname3(d) === d) return void 0;
|
|
2610
|
+
}
|
|
2611
|
+
} catch {
|
|
2612
|
+
return void 0;
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
function sameRepo(root, dir) {
|
|
2616
|
+
return repoOf(root) === repoOf(dir);
|
|
2617
|
+
}
|
|
2523
2618
|
function docsSources(root) {
|
|
2524
2619
|
try {
|
|
2525
2620
|
return readManifestAt(join7(root, ".eminiar", "docs.json"), root).sources;
|
|
@@ -2562,7 +2657,34 @@ var BIG_TEXT_EXT = /* @__PURE__ */ new Set([
|
|
|
2562
2657
|
]);
|
|
2563
2658
|
var VISUAL_EXT = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".pdf"]);
|
|
2564
2659
|
function withoutHeredocs(cmd) {
|
|
2565
|
-
|
|
2660
|
+
if (!cmd.includes("<<")) return cmd;
|
|
2661
|
+
const head = /<<-?\s*(['"]?)(\w+)\1/y;
|
|
2662
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2663
|
+
const terminator = (word, from) => {
|
|
2664
|
+
const c = seen.get(word);
|
|
2665
|
+
if (c && from >= c.from && (c.at === -1 || from <= c.at)) return c.end;
|
|
2666
|
+
const re = new RegExp(String.raw`\n[^\S\n]*${word}\s*(?=\n|$)`, "g");
|
|
2667
|
+
re.lastIndex = from;
|
|
2668
|
+
const m = re.exec(cmd);
|
|
2669
|
+
const rec = m ? { from, at: m.index, end: m.index + m[0].length } : { from, at: -1, end: -1 };
|
|
2670
|
+
seen.set(word, rec);
|
|
2671
|
+
return rec.end;
|
|
2672
|
+
};
|
|
2673
|
+
let out = "";
|
|
2674
|
+
let copied = 0;
|
|
2675
|
+
for (let k = cmd.indexOf("<<"); k !== -1; k = cmd.indexOf("<<", Math.max(k + 1, copied))) {
|
|
2676
|
+
head.lastIndex = k;
|
|
2677
|
+
const m = head.exec(cmd);
|
|
2678
|
+
if (!m) continue;
|
|
2679
|
+
const word = m[2];
|
|
2680
|
+
const eol = cmd.indexOf("\n", head.lastIndex);
|
|
2681
|
+
if (eol === -1) break;
|
|
2682
|
+
const end = terminator(word, eol + 1);
|
|
2683
|
+
if (end === -1) continue;
|
|
2684
|
+
out += `${cmd.slice(copied, k)}<<${word}${cmd.slice(head.lastIndex, eol)}`;
|
|
2685
|
+
copied = end;
|
|
2686
|
+
}
|
|
2687
|
+
return out + cmd.slice(copied);
|
|
2566
2688
|
}
|
|
2567
2689
|
function splitUnquoted(s, sep2) {
|
|
2568
2690
|
const out = [];
|
|
@@ -2570,13 +2692,23 @@ function splitUnquoted(s, sep2) {
|
|
|
2570
2692
|
let quote;
|
|
2571
2693
|
for (let i = 0; i < s.length; i++) {
|
|
2572
2694
|
const ch = s[i];
|
|
2695
|
+
if (quote === "'") {
|
|
2696
|
+
cur += ch;
|
|
2697
|
+
if (ch === "'") quote = void 0;
|
|
2698
|
+
continue;
|
|
2699
|
+
}
|
|
2700
|
+
if (ch === "\\") {
|
|
2701
|
+
cur += ch + (s[i + 1] ?? "");
|
|
2702
|
+
i++;
|
|
2703
|
+
continue;
|
|
2704
|
+
}
|
|
2573
2705
|
if (quote) {
|
|
2574
2706
|
cur += ch;
|
|
2575
|
-
if (ch === quote
|
|
2707
|
+
if (ch === quote[quote.length - 1]) quote = void 0;
|
|
2576
2708
|
continue;
|
|
2577
2709
|
}
|
|
2578
2710
|
if (ch === "'" || ch === '"') {
|
|
2579
|
-
quote = ch;
|
|
2711
|
+
quote = ch === "'" && s[i - 1] === "$" ? "$'" : ch;
|
|
2580
2712
|
cur += ch;
|
|
2581
2713
|
continue;
|
|
2582
2714
|
}
|
|
@@ -2597,9 +2729,44 @@ function commandSegments(cmd) {
|
|
|
2597
2729
|
const ch = s[i];
|
|
2598
2730
|
if (ch === ";" || ch === "\n") return 1;
|
|
2599
2731
|
if ((ch === "&" || ch === "|") && s[i + 1] === ch) return 2;
|
|
2732
|
+
if (ch === "&" && !/[&<>|]/.test(s[i - 1] ?? "") && !/[&>]/.test(s[i + 1] ?? "")) return 1;
|
|
2600
2733
|
return 0;
|
|
2601
2734
|
});
|
|
2602
2735
|
}
|
|
2736
|
+
function shellWords(s) {
|
|
2737
|
+
const words = [];
|
|
2738
|
+
let cur = "";
|
|
2739
|
+
let inWord = false;
|
|
2740
|
+
for (let i = 0; i < s.length; i++) {
|
|
2741
|
+
const ch = s[i];
|
|
2742
|
+
if (ch === " " || ch === " " || ch === "\n") {
|
|
2743
|
+
if (inWord) words.push(cur);
|
|
2744
|
+
cur = "";
|
|
2745
|
+
inWord = false;
|
|
2746
|
+
continue;
|
|
2747
|
+
}
|
|
2748
|
+
inWord = true;
|
|
2749
|
+
if (ch === "\\") {
|
|
2750
|
+
cur += s[++i] ?? "";
|
|
2751
|
+
} else if (ch === "'" && s[i - 1] !== "$") {
|
|
2752
|
+
const end = s.indexOf("'", i + 1);
|
|
2753
|
+
const stop2 = end === -1 ? s.length : end;
|
|
2754
|
+
cur += s.slice(i + 1, stop2);
|
|
2755
|
+
i = stop2;
|
|
2756
|
+
} else if (ch === '"' || ch === "$" && s[i + 1] === "'") {
|
|
2757
|
+
const close = ch === '"' ? '"' : "'";
|
|
2758
|
+
if (ch === "$") i++;
|
|
2759
|
+
for (i++; i < s.length && s[i] !== close; i++) {
|
|
2760
|
+
if (s[i] === "\\" && i + 1 < s.length) i++;
|
|
2761
|
+
cur += s[i];
|
|
2762
|
+
}
|
|
2763
|
+
} else {
|
|
2764
|
+
cur += ch;
|
|
2765
|
+
}
|
|
2766
|
+
}
|
|
2767
|
+
if (inWord) words.push(cur);
|
|
2768
|
+
return words;
|
|
2769
|
+
}
|
|
2603
2770
|
function pipeStages(segment) {
|
|
2604
2771
|
return splitUnquoted(segment, (s, i) => s[i] === "|" && s[i + 1] !== "|" ? 1 : 0);
|
|
2605
2772
|
}
|
|
@@ -2613,10 +2780,416 @@ function bounds(stage) {
|
|
|
2613
2780
|
if (!/^\s*(head|tail)\b/.test(stage) || /(^|\s)-c\b|--bytes\b/.test(stage)) return false;
|
|
2614
2781
|
return headLines(stage) <= BOUNDED_LINES;
|
|
2615
2782
|
}
|
|
2616
|
-
var
|
|
2617
|
-
var
|
|
2618
|
-
|
|
2619
|
-
|
|
2783
|
+
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-])/;
|
|
2784
|
+
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-])/;
|
|
2785
|
+
var MUTATING_WORDS = /* @__PURE__ */ new Set([
|
|
2786
|
+
"npm",
|
|
2787
|
+
"pnpm",
|
|
2788
|
+
"yarn",
|
|
2789
|
+
"pip",
|
|
2790
|
+
"pip3",
|
|
2791
|
+
"cargo",
|
|
2792
|
+
"brew",
|
|
2793
|
+
"gh",
|
|
2794
|
+
"terraform",
|
|
2795
|
+
"helm",
|
|
2796
|
+
"mvn",
|
|
2797
|
+
"mvnw",
|
|
2798
|
+
"gradle",
|
|
2799
|
+
"gradlew",
|
|
2800
|
+
"rm",
|
|
2801
|
+
"mv",
|
|
2802
|
+
"cp",
|
|
2803
|
+
"mkdir",
|
|
2804
|
+
"rmdir",
|
|
2805
|
+
"touch",
|
|
2806
|
+
"chmod",
|
|
2807
|
+
"chown",
|
|
2808
|
+
"ln",
|
|
2809
|
+
"sed",
|
|
2810
|
+
"tee",
|
|
2811
|
+
"eval",
|
|
2812
|
+
"sh",
|
|
2813
|
+
"bash",
|
|
2814
|
+
"zsh",
|
|
2815
|
+
"find"
|
|
2816
|
+
]);
|
|
2817
|
+
var MUTATING = {
|
|
2818
|
+
test(cmd) {
|
|
2819
|
+
const word = /^[A-Za-z0-9]*/.exec(cmd)[0];
|
|
2820
|
+
if (word === "git") return GIT_MUTATING.test(cmd);
|
|
2821
|
+
return MUTATING_WORDS.has(word) && OTHER_MUTATING.test(cmd);
|
|
2822
|
+
}
|
|
2823
|
+
};
|
|
2824
|
+
var longOpts = (valued) => String.raw`--(?:${valued})\s+[^-\s]\S*|--(?!(?:${valued})(?:\s|$))\S*`;
|
|
2825
|
+
var RUNNERS = [
|
|
2826
|
+
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")}))*`,
|
|
2827
|
+
String.raw`env(?:\s+(?:-[uC]\s*[^-\s]\S*|-(?![uC-])\S+|${longOpts("unset|chdir|split-string")}))*`,
|
|
2828
|
+
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")}))*`,
|
|
2829
|
+
String.raw`timeout(?:\s+(?:-[sk]\s*[^-\s]\S*|-(?![sk-])\S+|${longOpts("signal|kill-after")}))*\s+\d\S*`,
|
|
2830
|
+
String.raw`nice(?:\s+-n\s*\S+|\s+-\d+|\s+--adjustment(?:=|\s+)\S+)?`,
|
|
2831
|
+
String.raw`stdbuf(?:\s+-[ioe]\s*\S+)*`,
|
|
2832
|
+
String.raw`(?:time|command)(?:\s+-p)?`,
|
|
2833
|
+
"nohup",
|
|
2834
|
+
String.raw`[A-Z_][A-Z0-9_]*=\S*`
|
|
2835
|
+
];
|
|
2836
|
+
var SHELL_WORDS = /^(?:\s*(?:[({!]|(?:then|do|else|exec|if|elif|while|until)\s))*\s*/;
|
|
2837
|
+
var RUNNER = new RegExp(String.raw`^\s*(?:(?:${RUNNERS.join("|")})\s+)*`);
|
|
2838
|
+
var RUNNER_HEAD = /^\s*(?:sudo|env|xargs|parallel|timeout|nice|stdbuf|time|command|nohup|[A-Z_])/;
|
|
2839
|
+
var stripRunners = (s) => RUNNER_HEAD.test(s) ? s.replace(RUNNER, "") : s.replace(/^\s+/, "");
|
|
2840
|
+
var WRITES_FILE = /(?:^|[\s\d&])>{1,2}\s*(?!&)(?!\/dev\/null)\S/;
|
|
2841
|
+
var GLUED_WRITE = /(?<![-=>])>{1,2}\s*(?!&)(?!\/dev\/null)\S/;
|
|
2842
|
+
var RUNS_STDIN = /^(python3?|node|perl|ruby|deno|bun)(\s+-\S*)*$/;
|
|
2843
|
+
var FOLLOWS = /^((kubectl|docker)(\s.*)?\s(-f|-w|--follow)(?=[\s=]|$)|tail(\s.*)?\s(-[a-zA-Z]*[fF]|--follow)|.*\s--watch(All)?(?=[\s=]|$))/;
|
|
2844
|
+
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))/;
|
|
2845
|
+
var WGET_PRINTS = /\s(?:-[a-zA-Z]*O\s*-|--output-document[= ]-|--spider)(?=\s|$)/;
|
|
2846
|
+
var WGET_SENDS = /\s(?:--(post-data|post-file|body-data|body-file|method|output-file|append-output|execute|config)\b|-[a-zA-Z]*[oae])/;
|
|
2847
|
+
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/;
|
|
2848
|
+
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/;
|
|
2849
|
+
var CHECK_SCRIPT = /^(t|test|lint|type-?check|check)(?=$|[:.-])(?![\w:.-]*(fix|write|update|install|deploy|publish|push|release|clean))/;
|
|
2850
|
+
var FIX_ARGS = /(?:^|\s)(--fix|--write|--apply(-unsafe)?|-u|--update(-?[sS]napshots?)?)(?=[\s=]|$)/;
|
|
2851
|
+
var NPX_CHECKS = /^(vitest|jest|tsc|eslint|biome|prettier)(?:@[\w.^~-]*)?$/;
|
|
2852
|
+
var PM_READS = /^(ls|list|outdated|why|info|view|audit|licenses|bin|root|help)$/;
|
|
2853
|
+
var VALUE_OPTS = {
|
|
2854
|
+
kubectl: /* @__PURE__ */ new Set([
|
|
2855
|
+
"-n",
|
|
2856
|
+
"--namespace",
|
|
2857
|
+
"--context",
|
|
2858
|
+
"--cluster",
|
|
2859
|
+
"--kubeconfig",
|
|
2860
|
+
"--user",
|
|
2861
|
+
"-s",
|
|
2862
|
+
"--server",
|
|
2863
|
+
"--token",
|
|
2864
|
+
"--as",
|
|
2865
|
+
"--as-group",
|
|
2866
|
+
"--as-uid",
|
|
2867
|
+
"--cache-dir",
|
|
2868
|
+
"--certificate-authority",
|
|
2869
|
+
"--client-certificate",
|
|
2870
|
+
"--client-key",
|
|
2871
|
+
"--password",
|
|
2872
|
+
"--username",
|
|
2873
|
+
"--profile",
|
|
2874
|
+
"--profile-output",
|
|
2875
|
+
"--request-timeout",
|
|
2876
|
+
"--tls-server-name",
|
|
2877
|
+
"--log-dir",
|
|
2878
|
+
"--log-file",
|
|
2879
|
+
"--log-file-max-size",
|
|
2880
|
+
"--vmodule",
|
|
2881
|
+
"-v",
|
|
2882
|
+
"--v"
|
|
2883
|
+
]),
|
|
2884
|
+
docker: /* @__PURE__ */ new Set([
|
|
2885
|
+
"-H",
|
|
2886
|
+
"--host",
|
|
2887
|
+
"-c",
|
|
2888
|
+
"--context",
|
|
2889
|
+
"--config",
|
|
2890
|
+
"-l",
|
|
2891
|
+
"--log-level",
|
|
2892
|
+
"--tlscacert",
|
|
2893
|
+
"--tlscert",
|
|
2894
|
+
"--tlskey"
|
|
2895
|
+
]),
|
|
2896
|
+
npm: /* @__PURE__ */ new Set(["--prefix", "-w", "--workspace", "--userconfig", "--registry", "--cache"]),
|
|
2897
|
+
pnpm: /* @__PURE__ */ new Set(["-C", "--dir", "--filter", "-F", "--workspace-dir"]),
|
|
2898
|
+
yarn: /* @__PURE__ */ new Set(["--cwd", "--cache-folder", "--registry"])
|
|
2899
|
+
};
|
|
2900
|
+
var FLAG_OPTS = {
|
|
2901
|
+
kubectl: /* @__PURE__ */ new Set([
|
|
2902
|
+
"--insecure-skip-tls-verify",
|
|
2903
|
+
"--match-server-version",
|
|
2904
|
+
"--warnings-as-errors",
|
|
2905
|
+
"--disable-compression",
|
|
2906
|
+
"--alsologtostderr",
|
|
2907
|
+
"--logtostderr",
|
|
2908
|
+
"--add-dir-header",
|
|
2909
|
+
"--skip-headers",
|
|
2910
|
+
"--skip-log-headers",
|
|
2911
|
+
"--one-output",
|
|
2912
|
+
"-h",
|
|
2913
|
+
"--help"
|
|
2914
|
+
]),
|
|
2915
|
+
docker: /* @__PURE__ */ new Set(["-D", "--debug", "--tls", "--tlsverify", "-v", "--version", "--help"])
|
|
2916
|
+
};
|
|
2917
|
+
var MAX_READINGS = 4;
|
|
2918
|
+
function afterOptions(tool, args) {
|
|
2919
|
+
const words = args.trim().split(/\s+/);
|
|
2920
|
+
const valued = VALUE_OPTS[tool];
|
|
2921
|
+
let i = 0;
|
|
2922
|
+
while (words[i]?.startsWith("-")) i += valued?.has(words[i]) ? 2 : 1;
|
|
2923
|
+
return words.slice(i);
|
|
2924
|
+
}
|
|
2925
|
+
function subcommands(tool, args) {
|
|
2926
|
+
const words = args.trim().split(/\s+/);
|
|
2927
|
+
const valued = VALUE_OPTS[tool];
|
|
2928
|
+
const flags = FLAG_OPTS[tool];
|
|
2929
|
+
const starts = /* @__PURE__ */ new Set();
|
|
2930
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2931
|
+
const todo = [0];
|
|
2932
|
+
while (todo.length) {
|
|
2933
|
+
const i = todo.pop();
|
|
2934
|
+
if (seen.has(i)) continue;
|
|
2935
|
+
seen.add(i);
|
|
2936
|
+
const w = words[i];
|
|
2937
|
+
if (w === void 0 || !w.startsWith("-")) {
|
|
2938
|
+
starts.add(i);
|
|
2939
|
+
if (starts.size > MAX_READINGS) return void 0;
|
|
2940
|
+
} else if (valued?.has(w)) todo.push(i + 2);
|
|
2941
|
+
else if (flags?.has(w) || w.includes("=") || /^-[^-]./.test(w)) todo.push(i + 1);
|
|
2942
|
+
else todo.push(i + 1, i + 2);
|
|
2943
|
+
}
|
|
2944
|
+
const tail = /\s--no-stream\b/.test(args) ? " --no-stream" : "";
|
|
2945
|
+
return [...starts].map((i) => words.slice(i, i + 3).join(" ") + tail);
|
|
2946
|
+
}
|
|
2947
|
+
function verbChanges(tool, reads, args) {
|
|
2948
|
+
const subs = subcommands(tool, args);
|
|
2949
|
+
return !subs || subs.some((s) => !reads.test(s));
|
|
2950
|
+
}
|
|
2951
|
+
function runsScript(tool, args) {
|
|
2952
|
+
const words = afterOptions(tool, args);
|
|
2953
|
+
let script = words.shift();
|
|
2954
|
+
if (script === "run" || script === "run-script") script = words.shift();
|
|
2955
|
+
else if (script === void 0 || PM_READS.test(script) || tool === "npm" && script !== "test")
|
|
2956
|
+
return false;
|
|
2957
|
+
if (script === void 0) return false;
|
|
2958
|
+
return !CHECK_SCRIPT.test(script) || FIX_ARGS.test(words.join(" "));
|
|
2959
|
+
}
|
|
2960
|
+
function makeChanges(args) {
|
|
2961
|
+
const words = shellWords(args);
|
|
2962
|
+
const targets = [];
|
|
2963
|
+
for (let i = 0; i < words.length; i++) {
|
|
2964
|
+
const w = words[i];
|
|
2965
|
+
if (/^(-E|--eval)/.test(w)) return true;
|
|
2966
|
+
if (w.startsWith("-")) {
|
|
2967
|
+
if (/^(-[CfIoW]|--(directory|file|makefile|include-dir|old-file|assume-old|what-if|new-file|assume-new))$/.test(
|
|
2968
|
+
w
|
|
2969
|
+
) || /^(-[jl]|--jobs|--load-average|--max-load)$/.test(w) && /^\d+(\.\d+)?$/.test(words[i + 1] ?? ""))
|
|
2970
|
+
i++;
|
|
2971
|
+
continue;
|
|
2972
|
+
}
|
|
2973
|
+
if (!w.includes("=")) targets.push(w);
|
|
2974
|
+
}
|
|
2975
|
+
return !targets.length || !targets.every((t) => CHECK_SCRIPT.test(t)) || FIX_ARGS.test(args);
|
|
2976
|
+
}
|
|
2977
|
+
var GH_API_VALUED = /^(-[FfHXpqt]|--(method|field|raw-field|input|header|jq|template|preview|hostname|cache))$/;
|
|
2978
|
+
function ghApiSends(args) {
|
|
2979
|
+
const words = shellWords(args);
|
|
2980
|
+
if (words[0] !== "api") return false;
|
|
2981
|
+
let method;
|
|
2982
|
+
let fields = false;
|
|
2983
|
+
for (let i = 1; i < words.length; i++) {
|
|
2984
|
+
const w = words[i];
|
|
2985
|
+
const long = /^--(method|field|raw-field|input)(?:=|$)/.exec(w)?.[1];
|
|
2986
|
+
if (long) {
|
|
2987
|
+
if (long === "method")
|
|
2988
|
+
method = w.includes("=") ? w.slice(w.indexOf("=") + 1) : words[i + 1] ?? "";
|
|
2989
|
+
else fields = true;
|
|
2990
|
+
} else if (/^-[a-zA-Z]/.test(w)) {
|
|
2991
|
+
const c = w.slice(1).replace(/^i+/, "")[0];
|
|
2992
|
+
const value = w.slice(w.indexOf(c ?? "") + 1) || (words[i + 1] ?? "");
|
|
2993
|
+
if (c === "X") method = value;
|
|
2994
|
+
else if (c === "f" || c === "F") fields = true;
|
|
2995
|
+
}
|
|
2996
|
+
if (GH_API_VALUED.test(w)) i++;
|
|
2997
|
+
}
|
|
2998
|
+
if (method !== void 0) return method.toUpperCase() !== "GET";
|
|
2999
|
+
if (words.includes("graphql")) return /\bmutation\b/.test(words.join(" "));
|
|
3000
|
+
return fields;
|
|
3001
|
+
}
|
|
3002
|
+
function npxChanges(args, cwd) {
|
|
3003
|
+
const words = shellWords(args);
|
|
3004
|
+
let i = 0;
|
|
3005
|
+
for (; words[i]?.startsWith("-"); i++)
|
|
3006
|
+
if (/^(-p|--package|-c|--call)(=|$)/.test(words[i])) return true;
|
|
3007
|
+
const [name = "", ...rest] = words.slice(i);
|
|
3008
|
+
const tool = NPX_CHECKS.exec(name)?.[1];
|
|
3009
|
+
if (!tool || FIX_ARGS.test(args)) return true;
|
|
3010
|
+
const has = (re) => rest.some((w) => re.test(w));
|
|
3011
|
+
if (has(/^--(output-?file|outputFile|out-?dir|outFile|generateTrace)(=|$)/i)) return true;
|
|
3012
|
+
if (tool === "eslint" && has(/^-o/)) return true;
|
|
3013
|
+
if (tool === "prettier" && has(/^-[a-zA-Z]*w$/)) return true;
|
|
3014
|
+
return tool === "tsc" && !tscEmitsNothing(rest, cwd);
|
|
3015
|
+
}
|
|
3016
|
+
function tscEmitsNothing(words, cwd) {
|
|
3017
|
+
if (words.some((w) => /^(-b|--build|--generateTrace)(=|$)/i.test(w))) return false;
|
|
3018
|
+
const noEmit = words.findIndex((w) => /^--noEmit(=true)?$/i.test(w));
|
|
3019
|
+
if (noEmit !== -1) return words[noEmit + 1] !== "false";
|
|
3020
|
+
let project = "tsconfig.json";
|
|
3021
|
+
for (let i = 0; i < words.length; i++) {
|
|
3022
|
+
const w = words[i];
|
|
3023
|
+
if (/^(-p|--project)$/i.test(w)) project = words[++i] ?? "";
|
|
3024
|
+
else if (!w.startsWith("-")) return false;
|
|
3025
|
+
}
|
|
3026
|
+
try {
|
|
3027
|
+
let file2 = resolve3(cwd, project);
|
|
3028
|
+
if (statSync3(file2).isDirectory()) file2 = join7(file2, "tsconfig.json");
|
|
3029
|
+
const st = statSync3(file2);
|
|
3030
|
+
if (st.size > 256 * 1024) return false;
|
|
3031
|
+
return /"noEmit"\s*:\s*true\b/.test(readFileSync5(file2, "utf8"));
|
|
3032
|
+
} catch {
|
|
3033
|
+
return false;
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
var CHANGES = /* @__PURE__ */ new Map([
|
|
3037
|
+
["curl", (a) => CURL_SENDS.test(a)],
|
|
3038
|
+
["wget", (a) => !WGET_PRINTS.test(a) || WGET_SENDS.test(a)],
|
|
3039
|
+
["kubectl", (a) => verbChanges("kubectl", KUBECTL_READS, a)],
|
|
3040
|
+
["docker", (a) => verbChanges("docker", DOCKER_READS, a)],
|
|
3041
|
+
["gh", ghApiSends],
|
|
3042
|
+
["make", makeChanges],
|
|
3043
|
+
["npx", npxChanges],
|
|
3044
|
+
["npm", (a) => runsScript("npm", a)],
|
|
3045
|
+
["pnpm", (a) => runsScript("pnpm", a)],
|
|
3046
|
+
["yarn", (a) => runsScript("yarn", a)]
|
|
3047
|
+
]);
|
|
3048
|
+
function operands(words, valued) {
|
|
3049
|
+
let n = 0;
|
|
3050
|
+
for (let i = 0; i < words.length; i++) {
|
|
3051
|
+
const w = words[i];
|
|
3052
|
+
if (w === "--") return n + words.length - i - 1;
|
|
3053
|
+
if (w.startsWith("-") && w.length > 1) {
|
|
3054
|
+
if (valued.test(w)) i++;
|
|
3055
|
+
} else n++;
|
|
3056
|
+
}
|
|
3057
|
+
return n;
|
|
3058
|
+
}
|
|
3059
|
+
function awkRuns(words) {
|
|
3060
|
+
let program;
|
|
3061
|
+
for (let i = 0; i < words.length && program === void 0; i++) {
|
|
3062
|
+
const w = words[i];
|
|
3063
|
+
if (w === "--") program = words[i + 1];
|
|
3064
|
+
else if (/^(-[fiEl]|--(file|include|load|exec))/.test(w)) return true;
|
|
3065
|
+
else if (/^(-e|--source)$/.test(w)) program = words[i + 1];
|
|
3066
|
+
else if (/^(-[FvW]|--(field-separator|assign))$/.test(w)) i++;
|
|
3067
|
+
else if (!w.startsWith("-")) program = w;
|
|
3068
|
+
}
|
|
3069
|
+
return program !== void 0 && /\bsystem\s*\(|\|\s*&?\s*getline|\bprintf?\b[^;{}\n]*[>|]|@(load|include)\b/.test(program);
|
|
3070
|
+
}
|
|
3071
|
+
function sedWrites(words) {
|
|
3072
|
+
const scripts = [];
|
|
3073
|
+
let first;
|
|
3074
|
+
for (let i = 0; i < words.length; i++) {
|
|
3075
|
+
const w = words[i];
|
|
3076
|
+
if (w === "--") {
|
|
3077
|
+
first ??= words[i + 1];
|
|
3078
|
+
break;
|
|
3079
|
+
}
|
|
3080
|
+
if (w.startsWith("--")) {
|
|
3081
|
+
const [name, value] = w.split(/=(.*)/s);
|
|
3082
|
+
if (name === "--in-place" || name === "--file") return true;
|
|
3083
|
+
if (name === "--expression") scripts.push(value ?? words[++i] ?? "");
|
|
3084
|
+
else if (name === "--line-length" && value === void 0) i++;
|
|
3085
|
+
} else if (/^-./.test(w)) {
|
|
3086
|
+
for (let k = 1; k < w.length; k++) {
|
|
3087
|
+
const c = w[k];
|
|
3088
|
+
if (c === "i" || c === "f") return true;
|
|
3089
|
+
if (c === "e" || c === "l") {
|
|
3090
|
+
const value = w.slice(k + 1) || words[++i] || "";
|
|
3091
|
+
if (c === "e") scripts.push(value);
|
|
3092
|
+
break;
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
3095
|
+
} else if (first === void 0) first = w;
|
|
3096
|
+
}
|
|
3097
|
+
if (!scripts.length && first !== void 0) scripts.push(first);
|
|
3098
|
+
return scripts.some(sedScriptWrites);
|
|
3099
|
+
}
|
|
3100
|
+
function sedScriptWrites(script) {
|
|
3101
|
+
if (script.length > 2048) return true;
|
|
3102
|
+
let flagged = false;
|
|
3103
|
+
const t = script.replace(
|
|
3104
|
+
/(^|[^a-zA-Z\\])([sy])([^\\\n\sa-zA-Z0-9])((?:\\[\s\S]|(?!\3)[^\\])*)\3((?:\\[\s\S]|(?!\3)[^\\])*)\3([a-zA-Z0-9]*)/g,
|
|
3105
|
+
(_m, pre, cmd, _d, _a, _b, flags) => {
|
|
3106
|
+
if (cmd === "s" && /[we]/.test(flags)) flagged = true;
|
|
3107
|
+
return `${pre};`;
|
|
3108
|
+
}
|
|
3109
|
+
).replace(/\/(?:\\[\s\S]|[^/\\\n])*\//g, ";");
|
|
3110
|
+
return flagged || /(?:^|[^a-zA-Z_.-])(?:[wW]|e(?![a-zA-Z]))/.test(t);
|
|
3111
|
+
}
|
|
3112
|
+
var FLAG_WRITES = /* @__PURE__ */ new Map([
|
|
3113
|
+
// `--pre cmd` runs cmd on every file searched
|
|
3114
|
+
["rg", (w) => w.some((x) => /^--pre(-glob)?(=|$)/.test(x))],
|
|
3115
|
+
["git", gitRunsOrWrites],
|
|
3116
|
+
["find", (w) => w.some((x) => /^-(delete|exec|execdir|ok|okdir|fprint0?|fprintf|fls)$/.test(x))],
|
|
3117
|
+
["tree", (w) => w.some((x) => /^(-[a-zA-Z]*o|--output)/.test(x))],
|
|
3118
|
+
["sed", sedWrites],
|
|
3119
|
+
["gsed", sedWrites],
|
|
3120
|
+
["awk", awkRuns],
|
|
3121
|
+
["gawk", awkRuns],
|
|
3122
|
+
["mawk", awkRuns],
|
|
3123
|
+
["nawk", awkRuns],
|
|
3124
|
+
["sort", (w) => w.some((x) => /^(-[a-zA-Z]*o|--output(=|$))/.test(x))],
|
|
3125
|
+
// `uniq IN OUT` and `xxd IN OUT` write OUT
|
|
3126
|
+
["uniq", (w) => operands(w, /^(-[fsw]|--(skip-fields|skip-chars|check-chars))$/) > 1],
|
|
3127
|
+
["xxd", (w) => operands(w, /^-(c|g|l|o|s|n|cols|groupsize|len|offset|seek|name)$/) > 1],
|
|
3128
|
+
["yq", (w) => w.some((x) => /^(-[a-zA-Z]*i[a-zA-Z]*|--inplace)(=|$)/.test(x))]
|
|
3129
|
+
]);
|
|
3130
|
+
function gitRunsOrWrites(words) {
|
|
3131
|
+
let i = 0;
|
|
3132
|
+
for (; i < words.length && words[i].startsWith("-"); i++) {
|
|
3133
|
+
const w = words[i];
|
|
3134
|
+
if (/^(-c|--config-env|--exec-path=)/.test(w)) return true;
|
|
3135
|
+
if (/^(-C|--git-dir|--work-tree|--namespace|--super-prefix)$/.test(w)) i++;
|
|
3136
|
+
}
|
|
3137
|
+
const sub = words[i];
|
|
3138
|
+
const rest = words.slice(i + 1);
|
|
3139
|
+
if (sub === "difftool" || sub === "mergetool") return true;
|
|
3140
|
+
if (rest.some((w) => /^--(output|ext-diff)(=|$)/.test(w))) return true;
|
|
3141
|
+
return sub === "grep" && rest.some((w) => /^(-[a-zA-Z]*O|--open-files-in-pager)/.test(w));
|
|
3142
|
+
}
|
|
3143
|
+
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)=/;
|
|
3144
|
+
var ARITHMETIC = /\$\(\(([^()`]*)\)\)/g;
|
|
3145
|
+
function substitutions(segment) {
|
|
3146
|
+
const found = [];
|
|
3147
|
+
const inner = /[$<>]\(([^()]*)\)|`([^`]*)`/g;
|
|
3148
|
+
let s = segment.replace(ARITHMETIC, "_");
|
|
3149
|
+
for (let level = 0; ; level++) {
|
|
3150
|
+
const next = s.replace(inner, (_, a, b) => {
|
|
3151
|
+
found.push(a ?? b ?? "");
|
|
3152
|
+
return "_";
|
|
3153
|
+
});
|
|
3154
|
+
if (next === s) break;
|
|
3155
|
+
if (level >= MAX_STEPS) return void 0;
|
|
3156
|
+
s = next;
|
|
3157
|
+
}
|
|
3158
|
+
return /[$<>]\(/.test(s) ? void 0 : found;
|
|
3159
|
+
}
|
|
3160
|
+
function staysNative(segment, cwd) {
|
|
3161
|
+
if (WRITES_FILE.test(segment)) return true;
|
|
3162
|
+
if (GLUED_WRITE.test(segment.replace(/'[^']*'|"(?:[^"\\]|\\.)*"/g, "''"))) return true;
|
|
3163
|
+
return pipeStages(segment).some((stage, i) => {
|
|
3164
|
+
const plain = stage.replace(SHELL_WORDS, "");
|
|
3165
|
+
if (plain.includes("GIT_") && GIT_ENV.test(plain)) return true;
|
|
3166
|
+
const cmd = stripRunners(plain).replace(/^\S*\//, "");
|
|
3167
|
+
const [, quoted = "", args = ""] = /^(\S+)([\s\S]*)$/.exec(cmd) ?? [];
|
|
3168
|
+
const tool = quoted.replace(/['"\\]/g, "");
|
|
3169
|
+
const named = tool + args;
|
|
3170
|
+
const bare = /^(npm|pnpm|yarn)$/.test(tool) ? `${tool} ${afterOptions(tool, args).join(" ")}` : named;
|
|
3171
|
+
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;
|
|
3172
|
+
});
|
|
3173
|
+
}
|
|
3174
|
+
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-])/;
|
|
3175
|
+
var TOOL_READS = {
|
|
3176
|
+
gh: (a) => /^\s+(?:api|(?:pr|issue|run|search|repo|release)\s+(?:list|view|diff|checks|status))\b/.test(a),
|
|
3177
|
+
npm: (a) => /^(?:test|t|run|run-script|ls|ll|list|outdated|view|show|info|why|explain|audit|root|bin|prefix|help)$/.test(
|
|
3178
|
+
afterOptions("npm", a)[0] ?? ""
|
|
3179
|
+
),
|
|
3180
|
+
pnpm: (a) => afterOptions("pnpm", a).length > 0,
|
|
3181
|
+
yarn: (a) => afterOptions("yarn", a).length > 0
|
|
3182
|
+
// bare `yarn` installs
|
|
3183
|
+
};
|
|
3184
|
+
function readsOnly(segment, cwd) {
|
|
3185
|
+
return pipeStages(segment).every((stage) => {
|
|
3186
|
+
const cmd = stripRunners(stage.replace(SHELL_WORDS, "")).replace(/^\S*\//, "");
|
|
3187
|
+
const [, tool = "", args = ""] = /^(\S+)([\s\S]*)$/.exec(cmd) ?? [];
|
|
3188
|
+
if (/(?:^|\s)--output(?=[\s=]|$)/.test(args) || tool === "sort" && /\s-\w*o\b/.test(args))
|
|
3189
|
+
return false;
|
|
3190
|
+
if (CHANGES.has(tool)) return TOOL_READS[tool]?.(args) ?? !CHANGES.get(tool)(args, cwd);
|
|
3191
|
+
return READ_ONLY.test(cmd);
|
|
3192
|
+
});
|
|
2620
3193
|
}
|
|
2621
3194
|
var READ_LIMIT = 200 * 1024;
|
|
2622
3195
|
var BIG_TEXT_LIMIT = 20 * 1024;
|
|
@@ -2665,13 +3238,13 @@ function fileOperands(stage, cwd) {
|
|
|
2665
3238
|
}
|
|
2666
3239
|
files.push(t);
|
|
2667
3240
|
}
|
|
2668
|
-
if (!files.length) return void 0;
|
|
3241
|
+
if (!files.length || files.length > MAX_OPERANDS) return void 0;
|
|
2669
3242
|
const out = [];
|
|
2670
3243
|
for (let f of files) {
|
|
2671
3244
|
if (/[*?[\]{}$`\\]/.test(f)) return void 0;
|
|
2672
3245
|
f = f.replace(/^['"]|['"]$/g, "");
|
|
2673
3246
|
if (f.startsWith("~/")) f = join7(homedir2(), f.slice(2));
|
|
2674
|
-
out.push(
|
|
3247
|
+
out.push(resolve3(cwd, f));
|
|
2675
3248
|
}
|
|
2676
3249
|
return out;
|
|
2677
3250
|
}
|
|
@@ -2709,66 +3282,99 @@ function firstStageBound(first, cwd) {
|
|
|
2709
3282
|
const bytes = stageBytes(first, cwd);
|
|
2710
3283
|
if (bytes === void 0) return void 0;
|
|
2711
3284
|
if (bytes <= READ_LIMIT) return true;
|
|
2712
|
-
return {
|
|
3285
|
+
return {
|
|
3286
|
+
rule: "big-read",
|
|
3287
|
+
text: `reads about ${Math.round(bytes / 1024)} KB`
|
|
3288
|
+
};
|
|
2713
3289
|
}
|
|
2714
3290
|
return void 0;
|
|
2715
3291
|
}
|
|
3292
|
+
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/;
|
|
3293
|
+
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/;
|
|
3294
|
+
var UNBOUNDED_FILTERS = /^\s*(grep|rg|sort|uniq|jq|less)\b/;
|
|
2716
3295
|
function heavySegment(segment, cwd) {
|
|
2717
3296
|
if (TINY_BASH.test(segment)) return void 0;
|
|
2718
3297
|
const stages = pipeStages(segment);
|
|
2719
3298
|
const first = stages[0];
|
|
2720
3299
|
if (!first) return void 0;
|
|
2721
3300
|
if (stages.length > 1 && bounds(stages[stages.length - 1])) return void 0;
|
|
3301
|
+
const rest = stages.slice(1);
|
|
3302
|
+
if (!rest.every((s) => FILTERS.test(s))) return void 0;
|
|
2722
3303
|
const bound = firstStageBound(first, cwd);
|
|
2723
3304
|
if (bound === true) return void 0;
|
|
2724
3305
|
if (bound) return bound;
|
|
2725
3306
|
if (HEAVY_BASH.test(first)) return { rule: "heavy", text: "usually prints a lot" };
|
|
2726
|
-
if (
|
|
2727
|
-
return {
|
|
3307
|
+
if (READ_SOURCE.test(first) && rest.some((s) => UNBOUNDED_FILTERS.test(s)))
|
|
3308
|
+
return {
|
|
3309
|
+
rule: "pipe",
|
|
3310
|
+
text: "streams an unbounded output through a filter"
|
|
3311
|
+
};
|
|
2728
3312
|
return void 0;
|
|
2729
3313
|
}
|
|
2730
3314
|
function headOf(segment, n = 60) {
|
|
2731
3315
|
const one = segment.replace(/\s+/g, " ").trim();
|
|
2732
3316
|
return one.length > n ? `${one.slice(0, n - 1)}\u2026` : one;
|
|
2733
3317
|
}
|
|
2734
|
-
function bashSuggestion(raw, cwd) {
|
|
3318
|
+
function bashSuggestion(raw, root, cwd) {
|
|
3319
|
+
if (raw.length > MAX_RAW) return void 0;
|
|
2735
3320
|
const cmd = withoutHeredocs(raw);
|
|
2736
3321
|
if (!cmd.trim()) return void 0;
|
|
3322
|
+
if (cmd.length > MAX_CLASSIFIED) return void 0;
|
|
2737
3323
|
const segments = commandSegments(cmd);
|
|
2738
|
-
if (segments.
|
|
3324
|
+
if (segments.length > MAX_SEGMENTS) return void 0;
|
|
3325
|
+
const subs = substitutions(cmd);
|
|
3326
|
+
if (!subs) return void 0;
|
|
3327
|
+
if ([...segments, ...subs.flatMap(commandSegments)].some((s) => staysNative(s, cwd)))
|
|
3328
|
+
return void 0;
|
|
3329
|
+
if (segments.length > 1 && !segments.every((s) => readsOnly(s, cwd))) return void 0;
|
|
2739
3330
|
const history = historyCall(cmd);
|
|
2740
|
-
|
|
3331
|
+
const dir = history && historyDir(segments, cwd);
|
|
3332
|
+
if (history && dir && !sameRepo(root, dir)) return void 0;
|
|
3333
|
+
if (history && dir && inRootRepo(root, dir)) {
|
|
2741
3334
|
return {
|
|
2742
3335
|
tool: "em_history",
|
|
2743
3336
|
rule: "history",
|
|
2744
3337
|
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`
|
|
2745
3338
|
};
|
|
2746
3339
|
}
|
|
3340
|
+
let at = cwd;
|
|
3341
|
+
let steps = 0;
|
|
2747
3342
|
for (const segment of segments) {
|
|
2748
|
-
const heavy = heavySegment(segment, cwd);
|
|
2749
|
-
if (
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
3343
|
+
const heavy = heavySegment(segment, at ?? cwd);
|
|
3344
|
+
if (heavy)
|
|
3345
|
+
return {
|
|
3346
|
+
tool: "em_execute",
|
|
3347
|
+
rule: heavy.rule,
|
|
3348
|
+
reason: `\`${headOf(segment)}\` ${heavy.text}; em_execute {language:"shell", code, intent} keeps the output out of context and returns only matching sections`
|
|
3349
|
+
};
|
|
3350
|
+
const cd = CD.exec(segment);
|
|
3351
|
+
if (cd) at = ++steps > MAX_STEPS ? void 0 : at && changeDir(at, cd[1]);
|
|
2755
3352
|
}
|
|
2756
3353
|
return void 0;
|
|
2757
3354
|
}
|
|
3355
|
+
function executeFileTakes(root, fp) {
|
|
3356
|
+
try {
|
|
3357
|
+
const real = realpathSync3(root);
|
|
3358
|
+
confinePath(real, resolve3(root, fp), loadPermissionRules(real));
|
|
3359
|
+
return true;
|
|
3360
|
+
} catch {
|
|
3361
|
+
return false;
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
2758
3364
|
function suggest(toolName, input, root, extra = {}) {
|
|
2759
3365
|
switch (toolName) {
|
|
2760
3366
|
case "Bash":
|
|
2761
|
-
return bashSuggestion(String(input.command ?? ""), extra.cwd ?? root);
|
|
3367
|
+
return bashSuggestion(String(input.command ?? ""), root, extra.cwd ?? root);
|
|
2762
3368
|
case "Read": {
|
|
2763
3369
|
const fp = String(input.file_path ?? "");
|
|
2764
3370
|
if (!fp) return void 0;
|
|
3371
|
+
if (input.offset != null || input.limit != null) return void 0;
|
|
2765
3372
|
const ext = extname(fp).toLowerCase();
|
|
2766
3373
|
if (VISUAL_EXT.has(ext)) return void 0;
|
|
2767
3374
|
try {
|
|
2768
|
-
const
|
|
2769
|
-
const st = statSync3(abs);
|
|
3375
|
+
const st = statSync3(resolve3(root, fp));
|
|
2770
3376
|
if (st.size > 200 * 1024 || BIG_TEXT_EXT.has(ext) && st.size > 20 * 1024) {
|
|
2771
|
-
if (!
|
|
3377
|
+
if (!executeFileTakes(root, fp)) return void 0;
|
|
2772
3378
|
return {
|
|
2773
3379
|
tool: "em_execute_file",
|
|
2774
3380
|
rule: "big-read",
|
|
@@ -2835,12 +3441,12 @@ function subagentPrefix(shell) {
|
|
|
2835
3441
|
}
|
|
2836
3442
|
function projectRel(ctx, fp) {
|
|
2837
3443
|
if (!fp) return void 0;
|
|
2838
|
-
let abs =
|
|
3444
|
+
let abs = resolve3(ctx.root, fp);
|
|
2839
3445
|
try {
|
|
2840
|
-
abs =
|
|
3446
|
+
abs = realpathSync3(abs);
|
|
2841
3447
|
} catch {
|
|
2842
3448
|
}
|
|
2843
|
-
const rel =
|
|
3449
|
+
const rel = relative2(ctx.root, abs).split("\\").join("/");
|
|
2844
3450
|
return rel.startsWith("..") ? void 0 : rel;
|
|
2845
3451
|
}
|
|
2846
3452
|
function graphContext(ctx, input) {
|
|
@@ -2871,7 +3477,11 @@ function fileKnowledge(ctx, input) {
|
|
|
2871
3477
|
const repeatAfter = ctx.config.recall.repeatAfter;
|
|
2872
3478
|
const edit = EDIT_TOOLS.has(input.tool_name ?? "");
|
|
2873
3479
|
const results = [
|
|
2874
|
-
memoriesForFile(db, rel, {
|
|
3480
|
+
memoriesForFile(db, rel, {
|
|
3481
|
+
sessionId,
|
|
3482
|
+
symbols: fileSymbols(db, rel),
|
|
3483
|
+
repeatAfter
|
|
3484
|
+
}),
|
|
2875
3485
|
fileContext(db, rel, { sessionId, edit, repeatAfter })
|
|
2876
3486
|
];
|
|
2877
3487
|
const shown = results.filter((r) => r.text);
|
|
@@ -2888,12 +3498,14 @@ function fileKnowledge(ctx, input) {
|
|
|
2888
3498
|
return void 0;
|
|
2889
3499
|
}
|
|
2890
3500
|
}
|
|
2891
|
-
function commitFiles(root, cmd) {
|
|
2892
|
-
const
|
|
3501
|
+
function commitFiles(root, cwd, cmd) {
|
|
3502
|
+
const top = git(cwd, ["rev-parse", "--show-toplevel"], { allowFail: true }).trim();
|
|
3503
|
+
if (!top) return [];
|
|
3504
|
+
const list = (args) => git(cwd, [...args, "-z"], { allowFail: true }).split("\0").filter(Boolean);
|
|
2893
3505
|
const files = new Set(list(["diff", "--cached", "--name-only"]));
|
|
2894
3506
|
if (/\s(-[a-zA-Z]*a[a-zA-Z]*|--all)\b/.test(cmd))
|
|
2895
3507
|
for (const f of list(["diff", "--name-only"])) files.add(f);
|
|
2896
|
-
return [...files];
|
|
3508
|
+
return [...files].map((f) => relative2(root, join7(top, f)));
|
|
2897
3509
|
}
|
|
2898
3510
|
function commitKnowledge(ctx, input) {
|
|
2899
3511
|
if (!ctx.config.recall.onCommit || input.tool_name !== "Bash") return void 0;
|
|
@@ -2901,7 +3513,7 @@ function commitKnowledge(ctx, input) {
|
|
|
2901
3513
|
if (!/(^|[;&|]\s*)git\s+commit\b/.test(cmd)) return void 0;
|
|
2902
3514
|
try {
|
|
2903
3515
|
const db = getStorage(ctx, { vectors: false });
|
|
2904
|
-
return commitContext(db, commitFiles(ctx.root, cmd)) || void 0;
|
|
3516
|
+
return commitContext(db, commitFiles(ctx.root, input.cwd || ctx.root, cmd)) || void 0;
|
|
2905
3517
|
} catch {
|
|
2906
3518
|
return void 0;
|
|
2907
3519
|
}
|
|
@@ -2921,7 +3533,11 @@ async function webKnowledge(ctx, input) {
|
|
|
2921
3533
|
if (!text.trim()) return void 0;
|
|
2922
3534
|
try {
|
|
2923
3535
|
const db = getStorage(ctx, { vectors: false });
|
|
2924
|
-
const r = await recallForText(db, text, {
|
|
3536
|
+
const r = await recallForText(db, text, {
|
|
3537
|
+
maxMemories: 0,
|
|
3538
|
+
maxDocs: 3,
|
|
3539
|
+
path: "web"
|
|
3540
|
+
});
|
|
2925
3541
|
if (!r.text) return void 0;
|
|
2926
3542
|
const pages = r.text.split("\n").filter((l) => l.startsWith("- docs:"));
|
|
2927
3543
|
return [
|
|
@@ -2939,7 +3555,12 @@ function joinHints(...hints) {
|
|
|
2939
3555
|
function withContext(out, extra) {
|
|
2940
3556
|
if (!extra) return out;
|
|
2941
3557
|
if (!out)
|
|
2942
|
-
return {
|
|
3558
|
+
return {
|
|
3559
|
+
hookSpecificOutput: {
|
|
3560
|
+
hookEventName: "PreToolUse",
|
|
3561
|
+
additionalContext: extra
|
|
3562
|
+
}
|
|
3563
|
+
};
|
|
2943
3564
|
const cur = out.hookSpecificOutput?.additionalContext;
|
|
2944
3565
|
return {
|
|
2945
3566
|
...out,
|
|
@@ -2954,14 +3575,14 @@ ${extra}` : extra
|
|
|
2954
3575
|
var preToolUse = async (input) => {
|
|
2955
3576
|
if (!input.tool_name || !input.tool_input) return void 0;
|
|
2956
3577
|
if (isSwarmTool(input.tool_name)) return swarmStampCaller(input);
|
|
2957
|
-
const ctx =
|
|
3578
|
+
const ctx = sessionContext(input.cwd);
|
|
2958
3579
|
if (input.agent_id) swarmTouch(ctx, input);
|
|
2959
3580
|
const spawn = swarmPreAgent(ctx, input);
|
|
2960
3581
|
if (spawn?.kind === "deny") return spawn.output;
|
|
2961
3582
|
const out = await route(ctx, input, spawn?.kind === "worker");
|
|
2962
3583
|
if (!ctx.config.adjudicator.enabled || input.client === "cursor") return out;
|
|
2963
3584
|
try {
|
|
2964
|
-
const { afterRouting } = await import("./hook-hooks-
|
|
3585
|
+
const { afterRouting } = await import("./hook-hooks-YGXLOZ6H.js");
|
|
2965
3586
|
return await afterRouting(ctx, input, spawn, out);
|
|
2966
3587
|
} catch {
|
|
2967
3588
|
return out;
|
|
@@ -2981,7 +3602,10 @@ async function route(ctx, input, swarmWorker) {
|
|
|
2981
3602
|
const graph = graphContext(ctx, input);
|
|
2982
3603
|
const hint = joinHints(graph, memo);
|
|
2983
3604
|
const symbol = input.tool_name === "Grep" && graph ? literalOf(String(input.tool_input.pattern ?? "")) : void 0;
|
|
2984
|
-
const s = suggest(input.tool_name, input.tool_input, ctx.root, {
|
|
3605
|
+
const s = suggest(input.tool_name, input.tool_input, ctx.root, {
|
|
3606
|
+
symbol,
|
|
3607
|
+
cwd: input.cwd
|
|
3608
|
+
});
|
|
2985
3609
|
const record = routingRecorder(ctx, input, s);
|
|
2986
3610
|
if (!s) {
|
|
2987
3611
|
record("allow");
|
|
@@ -3101,12 +3725,12 @@ function annotate(s, note = "") {
|
|
|
3101
3725
|
|
|
3102
3726
|
// src/hooks/session-start.ts
|
|
3103
3727
|
init_esm_shims();
|
|
3104
|
-
import { appendFileSync, existsSync as
|
|
3728
|
+
import { appendFileSync, existsSync as existsSync8, readdirSync as readdirSync4 } from "fs";
|
|
3105
3729
|
import { join as join9 } from "path";
|
|
3106
3730
|
|
|
3107
3731
|
// src/session/current.ts
|
|
3108
3732
|
init_esm_shims();
|
|
3109
|
-
import { readFileSync as
|
|
3733
|
+
import { readFileSync as readFileSync6, statSync as statSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
3110
3734
|
import { join as join8 } from "path";
|
|
3111
3735
|
function file(ctx) {
|
|
3112
3736
|
return join8(projectDir(ctx.id), "current-session");
|
|
@@ -3143,7 +3767,7 @@ function routingGuidance(mode) {
|
|
|
3143
3767
|
].join("\n");
|
|
3144
3768
|
}
|
|
3145
3769
|
var sessionStart = async (input) => {
|
|
3146
|
-
const ctx =
|
|
3770
|
+
const ctx = sessionContext(input.cwd);
|
|
3147
3771
|
const sessionId = input.session_id || "unknown";
|
|
3148
3772
|
setCurrentSession(ctx, sessionId);
|
|
3149
3773
|
const db = getStorage(ctx, { vectors: false });
|
|
@@ -3198,7 +3822,7 @@ ${guide}`);
|
|
|
3198
3822
|
if (swarm) parts.push(swarm);
|
|
3199
3823
|
if (ctx.config.adjudicator.enabled && input.client !== "cursor") {
|
|
3200
3824
|
try {
|
|
3201
|
-
const { onSessionStart } = await import("./hook-hooks-
|
|
3825
|
+
const { onSessionStart } = await import("./hook-hooks-YGXLOZ6H.js");
|
|
3202
3826
|
const line2 = onSessionStart(ctx, db, input);
|
|
3203
3827
|
if (line2) parts.push(line2);
|
|
3204
3828
|
} catch {
|
|
@@ -3220,7 +3844,7 @@ ${guide}`);
|
|
|
3220
3844
|
}
|
|
3221
3845
|
try {
|
|
3222
3846
|
const onboarding = join9(memoriesDir(ctx), "onboarding");
|
|
3223
|
-
const onboarded =
|
|
3847
|
+
const onboarded = existsSync8(onboarding) && readdirSync4(onboarding).some((f) => f.endsWith(".md"));
|
|
3224
3848
|
if (!onboarded)
|
|
3225
3849
|
parts.push(
|
|
3226
3850
|
"No onboarding memories yet: run /eminiar:em-onboard once so future sessions start with the project's structure, build and test commands."
|
|
@@ -3234,7 +3858,7 @@ ${guide}`);
|
|
|
3234
3858
|
} catch {
|
|
3235
3859
|
}
|
|
3236
3860
|
try {
|
|
3237
|
-
if (
|
|
3861
|
+
if (existsSync8(join9(ctx.root, ".git")) && !db.metaGet("history_files"))
|
|
3238
3862
|
parts.push(
|
|
3239
3863
|
'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.'
|
|
3240
3864
|
);
|
|
@@ -3279,8 +3903,8 @@ function ranLate() {
|
|
|
3279
3903
|
}
|
|
3280
3904
|
async function runWithin(handler, input, ms) {
|
|
3281
3905
|
let timer;
|
|
3282
|
-
const deadline = new Promise((
|
|
3283
|
-
timer = setTimeout(() =>
|
|
3906
|
+
const deadline = new Promise((resolve4) => {
|
|
3907
|
+
timer = setTimeout(() => resolve4("late"), ms);
|
|
3284
3908
|
});
|
|
3285
3909
|
try {
|
|
3286
3910
|
const r = await Promise.race([handler(input), deadline]);
|
|
@@ -3335,7 +3959,7 @@ function failClosed(input, err) {
|
|
|
3335
3959
|
if (input.hook_event_name !== "PreToolUse" || input.client === "cursor" || input.tool_name !== "Agent" && input.tool_name !== "Task" || !rawEnforced({ cwd: input.cwd }))
|
|
3336
3960
|
return void 0;
|
|
3337
3961
|
try {
|
|
3338
|
-
swarmAgentReturned(
|
|
3962
|
+
swarmAgentReturned(sessionContext(input.cwd), input);
|
|
3339
3963
|
} catch {
|
|
3340
3964
|
}
|
|
3341
3965
|
return JSON.stringify({
|
|
@@ -3367,7 +3991,7 @@ async function runCursor(parsed, budgetMs2) {
|
|
|
3367
3991
|
}
|
|
3368
3992
|
let root = input.cwd;
|
|
3369
3993
|
try {
|
|
3370
|
-
root =
|
|
3994
|
+
root = sessionContext(input.cwd).root;
|
|
3371
3995
|
} catch {
|
|
3372
3996
|
}
|
|
3373
3997
|
try {
|
|
@@ -3391,7 +4015,7 @@ async function main() {
|
|
|
3391
4015
|
return;
|
|
3392
4016
|
}
|
|
3393
4017
|
if (process.argv.includes("--adjudicate")) {
|
|
3394
|
-
const { ASYNC_JOB_MAX_MS, runAsyncJob } = await import("./hook-async-
|
|
4018
|
+
const { ASYNC_JOB_MAX_MS, runAsyncJob } = await import("./hook-async-7GK6H3OO.js");
|
|
3395
4019
|
setTimeout(() => process.exit(0), ASYNC_JOB_MAX_MS).unref();
|
|
3396
4020
|
await runAsyncJob(raw, { log: (line2) => hookLog("info", line2) });
|
|
3397
4021
|
return;
|