@brainervirus/workit-cursor 8.15.1 → 8.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +1083 -83
- package/dist/mcp-server.js +192 -3
- package/dist/workit-hook.js +1098 -80
- package/package.json +3 -3
- package/skills/workit-fanout/SKILL.md +7 -7
- package/skills/workit-fanout/references/brief.md +3 -1
- package/skills/workit-implement/SKILL.md +8 -1
- package/skills/workit-review/SKILL.md +2 -1
- package/skills/workit-verify-app/SKILL.md +2 -0
package/dist/workit-hook.js
CHANGED
|
@@ -9366,6 +9366,7 @@ var workspaceConfigSchema = object({
|
|
|
9366
9366
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
9367
9367
|
defaultEndpoint: unknown().optional(),
|
|
9368
9368
|
verification: _enum(["self", "independent"]).optional(),
|
|
9369
|
+
delegation: unknown().optional(),
|
|
9369
9370
|
trailers: boolean2().optional(),
|
|
9370
9371
|
liveSessionGuard: boolean2().optional(),
|
|
9371
9372
|
fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
@@ -15071,6 +15072,17 @@ import fs12 from "node:fs";
|
|
|
15071
15072
|
import path16 from "node:path";
|
|
15072
15073
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
15073
15074
|
var CHECKS_FILE = "workit.checks.json";
|
|
15075
|
+
var checksConfigFile = (root) => path16.join(root, CHECKS_FILE);
|
|
15076
|
+
function repoHeavyCommands(root) {
|
|
15077
|
+
try {
|
|
15078
|
+
const value = JSON.parse(fs12.readFileSync(checksConfigFile(root), "utf8"));
|
|
15079
|
+
const resources = isRecord3(value) ? value.resources : undefined;
|
|
15080
|
+
const list = isRecord3(resources) ? resources.heavyCommands : undefined;
|
|
15081
|
+
return Array.isArray(list) ? list.filter((item) => typeof item === "string") : [];
|
|
15082
|
+
} catch {
|
|
15083
|
+
return [];
|
|
15084
|
+
}
|
|
15085
|
+
}
|
|
15074
15086
|
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15075
15087
|
var SHELL_META = /[|&;<>()$`\\*?[\]{}~!#]/u;
|
|
15076
15088
|
function splitCommand(command) {
|
|
@@ -15183,7 +15195,7 @@ function detectedChecks(root) {
|
|
|
15183
15195
|
var GATES = ["testing", "verification"];
|
|
15184
15196
|
function loadCheckConfig(cwd) {
|
|
15185
15197
|
const root = checkRoot(cwd);
|
|
15186
|
-
const file =
|
|
15198
|
+
const file = checksConfigFile(root);
|
|
15187
15199
|
const none = (error = null) => ({
|
|
15188
15200
|
source: error ? CHECKS_FILE : "none",
|
|
15189
15201
|
root,
|
|
@@ -15935,6 +15947,54 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
15935
15947
|
requirementIds: evaluations.map((item) => item.requirementId)
|
|
15936
15948
|
});
|
|
15937
15949
|
}
|
|
15950
|
+
var BRIEF_TEXT = 250;
|
|
15951
|
+
var BRIEF_BLOCKERS = 3;
|
|
15952
|
+
var BRIEF_OPEN = 8;
|
|
15953
|
+
var clip = (value) => {
|
|
15954
|
+
const fits = (text) => Buffer.byteLength(JSON.stringify(text)) <= BRIEF_TEXT;
|
|
15955
|
+
if (fits(value))
|
|
15956
|
+
return value;
|
|
15957
|
+
let length = Math.min(value.length, BRIEF_TEXT);
|
|
15958
|
+
while (length > 0 && !fits(`${value.slice(0, length)}…`))
|
|
15959
|
+
length -= 1;
|
|
15960
|
+
return `${value.slice(0, length)}…`;
|
|
15961
|
+
};
|
|
15962
|
+
function boundTaskSummary(summary) {
|
|
15963
|
+
const rules = new Map((summary.policy?.requirements ?? []).map((item) => [item.id, item]));
|
|
15964
|
+
const unmet = summary.requirements.filter((item) => item.status === "unsatisfied" || item.status === "unavailable");
|
|
15965
|
+
const { progress, closure } = summary;
|
|
15966
|
+
const next = progress.nextAction;
|
|
15967
|
+
const open = unmet.slice(0, BRIEF_OPEN).map((item) => ({
|
|
15968
|
+
dimension: rules.get(item.requirementId)?.dimension ?? "verification",
|
|
15969
|
+
before: rules.get(item.requirementId)?.before ?? "close",
|
|
15970
|
+
status: item.status
|
|
15971
|
+
}));
|
|
15972
|
+
const brief = {
|
|
15973
|
+
id: summary.id,
|
|
15974
|
+
revision: summary.revision,
|
|
15975
|
+
workspaceRevision: summary.workspaceRevision,
|
|
15976
|
+
updatedAt: summary.updatedAt,
|
|
15977
|
+
objective: clip(summary.objective),
|
|
15978
|
+
status: summary.status,
|
|
15979
|
+
closure: closure && {
|
|
15980
|
+
outcome: closure.outcome,
|
|
15981
|
+
at: closure.at,
|
|
15982
|
+
summary: clip(closure.summary)
|
|
15983
|
+
},
|
|
15984
|
+
progress: {
|
|
15985
|
+
summary: clip(progress.summary),
|
|
15986
|
+
nextAction: next && clip(next),
|
|
15987
|
+
blockers: progress.blockers.slice(0, BRIEF_BLOCKERS).map((item) => ({
|
|
15988
|
+
reason: clip(item.reason),
|
|
15989
|
+
dependentAction: clip(item.dependentAction)
|
|
15990
|
+
}))
|
|
15991
|
+
},
|
|
15992
|
+
requirements: { total: summary.requirements.length, unmet: unmet.length, open },
|
|
15993
|
+
truncated: false
|
|
15994
|
+
};
|
|
15995
|
+
brief.truncated = JSON.stringify(brief).includes("…") || progress.blockers.length > BRIEF_BLOCKERS || unmet.length > BRIEF_OPEN;
|
|
15996
|
+
return brief;
|
|
15997
|
+
}
|
|
15938
15998
|
|
|
15939
15999
|
// packages/workit-core/src/core/methods.ts
|
|
15940
16000
|
var METHODS = {
|
|
@@ -16732,6 +16792,15 @@ class WorkitCore {
|
|
|
16732
16792
|
if (!parsed.ok)
|
|
16733
16793
|
return parsed;
|
|
16734
16794
|
const input = parsed.data;
|
|
16795
|
+
if (this.context.readOnlyChild) {
|
|
16796
|
+
if (input.action !== "inspect")
|
|
16797
|
+
return failure2("permission_denied", "child sessions can only inspect the task summary");
|
|
16798
|
+
const task = this.store.readTask(input.taskId);
|
|
16799
|
+
if (!task.ok)
|
|
16800
|
+
return task;
|
|
16801
|
+
const summary = this.summary(task.data);
|
|
16802
|
+
return summary.ok ? { ...summary, data: boundTaskSummary(summary.data) } : summary;
|
|
16803
|
+
}
|
|
16735
16804
|
if ((this.context.workerId ?? null) !== null) {
|
|
16736
16805
|
if (input.action !== "inspect" && input.action !== "list")
|
|
16737
16806
|
return failure2("permission_denied", "helpers cannot control task lifecycle or scope");
|
|
@@ -19175,7 +19244,16 @@ function rawInvocations(command, dialect = "posix", depth = 0) {
|
|
|
19175
19244
|
return found;
|
|
19176
19245
|
}
|
|
19177
19246
|
var RECENT = new Map;
|
|
19178
|
-
function
|
|
19247
|
+
function commandLines(command, dialect = "posix") {
|
|
19248
|
+
const lines = [];
|
|
19249
|
+
try {
|
|
19250
|
+
walk2(command, dialect, 0, lines);
|
|
19251
|
+
} catch (error) {
|
|
19252
|
+
throw error instanceof ShellAnalysisError ? error : new ShellAnalysisError(error instanceof Error ? error.message : String(error));
|
|
19253
|
+
}
|
|
19254
|
+
return lines;
|
|
19255
|
+
}
|
|
19256
|
+
function walk2(command, dialect, depth, seen) {
|
|
19179
19257
|
const found = [];
|
|
19180
19258
|
let dir = null;
|
|
19181
19259
|
const groups = [];
|
|
@@ -19186,7 +19264,8 @@ function walk2(command, dialect, depth) {
|
|
|
19186
19264
|
const nested = (script, inner) => {
|
|
19187
19265
|
if (!script || depth >= MAX_DEPTH)
|
|
19188
19266
|
return;
|
|
19189
|
-
|
|
19267
|
+
const inside = seen ? walk2(script, inner, depth + 1, seen) : rawInvocations(script, inner, depth + 1);
|
|
19268
|
+
for (const invocation of inside)
|
|
19190
19269
|
found.push({ ...invocation, dir: rebase(dir, invocation.dir) });
|
|
19191
19270
|
};
|
|
19192
19271
|
const visit = (input, stdin, hops) => {
|
|
@@ -19198,11 +19277,17 @@ function walk2(command, dialect, depth) {
|
|
|
19198
19277
|
const name = commandName(words[0]);
|
|
19199
19278
|
if (!name)
|
|
19200
19279
|
return;
|
|
19280
|
+
seen?.push(words);
|
|
19281
|
+
if (name === "-c" || name === "--command")
|
|
19282
|
+
return nested(words[1], "posix");
|
|
19201
19283
|
const runs = (lines) => {
|
|
19202
19284
|
if (hops < MAX_DEPTH)
|
|
19203
19285
|
for (const next of lines)
|
|
19204
19286
|
visit(next, undefined, hops + 1);
|
|
19205
19287
|
};
|
|
19288
|
+
const wrapped = seen ? null : workitCheckTail(words);
|
|
19289
|
+
if (wrapped)
|
|
19290
|
+
return "script" in wrapped ? nested(wrapped.script, "posix") : runs([wrapped.argv]);
|
|
19206
19291
|
if (CD.has(name)) {
|
|
19207
19292
|
const target = cdTarget(words);
|
|
19208
19293
|
dir = target === null ? os5.homedir() : joinDir(dir, target);
|
|
@@ -19293,6 +19378,51 @@ var LAUNCHERS = new Set([
|
|
|
19293
19378
|
"command"
|
|
19294
19379
|
]);
|
|
19295
19380
|
var WORKIT_BIN = /(?:^|[\\/])workit(?:\.exe)?$|^@brainervirus\/workit-cli$/;
|
|
19381
|
+
var WORKIT_PACKAGE = /^@brainervirus\/workit-cli(?:@\S*)?$/;
|
|
19382
|
+
var PACKAGE_RUNNERS = new Set(["npx", "bunx", "pnpx"]);
|
|
19383
|
+
function workitCheckTail(words) {
|
|
19384
|
+
let at = 0;
|
|
19385
|
+
if (PACKAGE_RUNNERS.has(commandName(words[0]))) {
|
|
19386
|
+
at = words.findIndex((word, index) => index > 0 && !word.startsWith("-"));
|
|
19387
|
+
if (at < 0 || !WORKIT_PACKAGE.test(words[at]))
|
|
19388
|
+
return null;
|
|
19389
|
+
} else if (!/^workit(?:\.exe|\.cmd)?$/.test(commandName(words[0])))
|
|
19390
|
+
return null;
|
|
19391
|
+
const args = words.slice(at + 1);
|
|
19392
|
+
if (args[0] !== "check" && !(args[0] === "lock" && args[1] === "run"))
|
|
19393
|
+
return null;
|
|
19394
|
+
const split = args.indexOf("--");
|
|
19395
|
+
const tail = split < 0 ? [] : args.slice(split + 1);
|
|
19396
|
+
if (!tail.length)
|
|
19397
|
+
return null;
|
|
19398
|
+
return args.slice(0, split).includes("--shell") ? { script: tail.join(" ") } : { argv: tail };
|
|
19399
|
+
}
|
|
19400
|
+
function simpleCommand(command, dialect = "posix", depth = 0) {
|
|
19401
|
+
let segments;
|
|
19402
|
+
try {
|
|
19403
|
+
segments = segmentsOf(command, dialect);
|
|
19404
|
+
} catch {
|
|
19405
|
+
return null;
|
|
19406
|
+
}
|
|
19407
|
+
if (segments.length !== 1)
|
|
19408
|
+
return null;
|
|
19409
|
+
const [segment] = segments;
|
|
19410
|
+
if (segment.group || segment.pipe || segment.unanalyzable || segment.redirects.length || segment.subs?.length || segment.stdin?.length)
|
|
19411
|
+
return null;
|
|
19412
|
+
const { words } = stripPrefixes(segment.words);
|
|
19413
|
+
const name = commandName(words[0]);
|
|
19414
|
+
if (!name || depth >= MAX_DEPTH)
|
|
19415
|
+
return null;
|
|
19416
|
+
if (SHELLS.has(name)) {
|
|
19417
|
+
const script = shellScript(words, undefined);
|
|
19418
|
+
return script ? simpleCommand(script, "posix", depth + 1) : null;
|
|
19419
|
+
}
|
|
19420
|
+
if (name === "-c" || name === "--command")
|
|
19421
|
+
return words[1] ? simpleCommand(words[1], "posix", depth + 1) : null;
|
|
19422
|
+
if (EVALS.has(name) || POWERSHELLS.has(name) || ["xargs", "find", "parallel", "script", "cmd", "source", "."].includes(name))
|
|
19423
|
+
return null;
|
|
19424
|
+
return words;
|
|
19425
|
+
}
|
|
19296
19426
|
function workitInvocations(command, dialect = "posix", depth = 0) {
|
|
19297
19427
|
const found = [];
|
|
19298
19428
|
for (const { words } of segmentsOf(command, dialect)) {
|
|
@@ -20320,17 +20450,827 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
20320
20450
|
}
|
|
20321
20451
|
return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
|
|
20322
20452
|
};
|
|
20323
|
-
// packages/workit-core/src/
|
|
20324
|
-
import
|
|
20453
|
+
// packages/workit-core/src/core/heavy-rules.ts
|
|
20454
|
+
import fs17 from "node:fs";
|
|
20455
|
+
import os6 from "node:os";
|
|
20456
|
+
import path22 from "node:path";
|
|
20457
|
+
var EMPTY = () => ({ deny: [], allow: [] });
|
|
20458
|
+
var readText2 = (file) => {
|
|
20459
|
+
try {
|
|
20460
|
+
return fs17.readFileSync(file, "utf8");
|
|
20461
|
+
} catch (error) {
|
|
20462
|
+
if (error.code === "ENOENT")
|
|
20463
|
+
return null;
|
|
20464
|
+
throw error;
|
|
20465
|
+
}
|
|
20466
|
+
};
|
|
20467
|
+
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20468
|
+
function parseJsonc(text) {
|
|
20469
|
+
let out = "";
|
|
20470
|
+
for (let index = 0;index < text.length; index += 1) {
|
|
20471
|
+
const char = text[index];
|
|
20472
|
+
if (char === '"') {
|
|
20473
|
+
const end = /"(?:[^"\\]|\\.)*"/y;
|
|
20474
|
+
end.lastIndex = index;
|
|
20475
|
+
const match = end.exec(text);
|
|
20476
|
+
if (!match)
|
|
20477
|
+
throw new Error("unterminated string");
|
|
20478
|
+
out += match[0];
|
|
20479
|
+
index += match[0].length - 1;
|
|
20480
|
+
} else if (char === "/" && text[index + 1] === "/") {
|
|
20481
|
+
while (index < text.length && text[index] !== `
|
|
20482
|
+
`)
|
|
20483
|
+
index += 1;
|
|
20484
|
+
out += `
|
|
20485
|
+
`;
|
|
20486
|
+
} else if (char === "/" && text[index + 1] === "*") {
|
|
20487
|
+
const close = text.indexOf("*/", index + 2);
|
|
20488
|
+
if (close < 0)
|
|
20489
|
+
throw new Error("unterminated comment");
|
|
20490
|
+
index = close + 1;
|
|
20491
|
+
} else
|
|
20492
|
+
out += char;
|
|
20493
|
+
}
|
|
20494
|
+
return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
|
|
20495
|
+
}
|
|
20496
|
+
function gitTop(cwd) {
|
|
20497
|
+
for (let dir = path22.resolve(cwd);; dir = path22.dirname(dir)) {
|
|
20498
|
+
if (fs17.existsSync(path22.join(dir, ".git")))
|
|
20499
|
+
return dir;
|
|
20500
|
+
if (path22.dirname(dir) === dir)
|
|
20501
|
+
return null;
|
|
20502
|
+
}
|
|
20503
|
+
}
|
|
20504
|
+
var projectDirs = (cwd) => {
|
|
20505
|
+
const top = gitTop(cwd);
|
|
20506
|
+
const dirs = [];
|
|
20507
|
+
for (let dir = path22.resolve(cwd);; dir = path22.dirname(dir)) {
|
|
20508
|
+
dirs.push(dir);
|
|
20509
|
+
if (!top || dir === top || path22.dirname(dir) === dir)
|
|
20510
|
+
return dirs;
|
|
20511
|
+
}
|
|
20512
|
+
};
|
|
20513
|
+
var homeOf = (env) => env.HOME || os6.homedir();
|
|
20514
|
+
var CLAUDE_MANAGED = {
|
|
20515
|
+
linux: "/etc/claude-code/managed-settings.json",
|
|
20516
|
+
darwin: "/Library/Application Support/ClaudeCode/managed-settings.json",
|
|
20517
|
+
win32: "C:\\Program Files\\ClaudeCode\\managed-settings.json"
|
|
20518
|
+
};
|
|
20519
|
+
function claudeSettingsFiles(cwd, env, platform = process.platform) {
|
|
20520
|
+
const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
|
|
20521
|
+
const user = env.CLAUDE_CONFIG_DIR || path22.join(homeOf(env), ".claude");
|
|
20522
|
+
return [
|
|
20523
|
+
...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
|
|
20524
|
+
path22.join(user, "settings.json"),
|
|
20525
|
+
path22.join(project, ".claude", "settings.json"),
|
|
20526
|
+
path22.join(project, ".claude", "settings.local.json")
|
|
20527
|
+
];
|
|
20528
|
+
}
|
|
20529
|
+
var bashPattern = (entry) => {
|
|
20530
|
+
const match = /^Bash(?:\((.*)\))?$/su.exec(entry.trim());
|
|
20531
|
+
return match ? match[1] ?? "" : null;
|
|
20532
|
+
};
|
|
20533
|
+
function claudeRules(cwd, env = process.env) {
|
|
20534
|
+
const rules = EMPTY();
|
|
20535
|
+
try {
|
|
20536
|
+
for (const file of claudeSettingsFiles(cwd, env)) {
|
|
20537
|
+
const text = readText2(file);
|
|
20538
|
+
if (text === null)
|
|
20539
|
+
continue;
|
|
20540
|
+
const value = JSON.parse(text);
|
|
20541
|
+
if (!isRecord4(value))
|
|
20542
|
+
return "unreadable";
|
|
20543
|
+
const permissions = value.permissions;
|
|
20544
|
+
if (permissions === undefined)
|
|
20545
|
+
continue;
|
|
20546
|
+
if (!isRecord4(permissions))
|
|
20547
|
+
return "unreadable";
|
|
20548
|
+
for (const [key, bucket] of [
|
|
20549
|
+
["deny", rules.deny],
|
|
20550
|
+
["ask", rules.deny],
|
|
20551
|
+
["allow", rules.allow]
|
|
20552
|
+
]) {
|
|
20553
|
+
const list = permissions[key];
|
|
20554
|
+
if (list === undefined)
|
|
20555
|
+
continue;
|
|
20556
|
+
if (!Array.isArray(list))
|
|
20557
|
+
return "unreadable";
|
|
20558
|
+
for (const entry of list) {
|
|
20559
|
+
if (typeof entry !== "string")
|
|
20560
|
+
return "unreadable";
|
|
20561
|
+
const pattern = bashPattern(entry);
|
|
20562
|
+
if (pattern !== null)
|
|
20563
|
+
bucket.push(pattern);
|
|
20564
|
+
}
|
|
20565
|
+
}
|
|
20566
|
+
}
|
|
20567
|
+
} catch {
|
|
20568
|
+
return "unreadable";
|
|
20569
|
+
}
|
|
20570
|
+
return rules;
|
|
20571
|
+
}
|
|
20572
|
+
var OPENCODE_NAMES = ["opencode.json", "opencode.jsonc"];
|
|
20573
|
+
var opencodePermission = (permission, rules) => {
|
|
20574
|
+
if (permission === undefined)
|
|
20575
|
+
return true;
|
|
20576
|
+
if (typeof permission === "string") {
|
|
20577
|
+
(permission === "allow" ? rules.allow : rules.deny).push("");
|
|
20578
|
+
return ["allow", "ask", "deny"].includes(permission);
|
|
20579
|
+
}
|
|
20580
|
+
if (!isRecord4(permission))
|
|
20581
|
+
return false;
|
|
20582
|
+
for (const key of ["bash", "shell"]) {
|
|
20583
|
+
const value = permission[key];
|
|
20584
|
+
if (value === undefined)
|
|
20585
|
+
continue;
|
|
20586
|
+
if (typeof value === "string") {
|
|
20587
|
+
if (!["allow", "ask", "deny"].includes(value))
|
|
20588
|
+
return false;
|
|
20589
|
+
(value === "allow" ? rules.allow : rules.deny).push("");
|
|
20590
|
+
continue;
|
|
20591
|
+
}
|
|
20592
|
+
if (!isRecord4(value))
|
|
20593
|
+
return false;
|
|
20594
|
+
for (const [pattern, action] of Object.entries(value)) {
|
|
20595
|
+
if (action !== "allow" && action !== "ask" && action !== "deny")
|
|
20596
|
+
return false;
|
|
20597
|
+
(action === "allow" ? rules.allow : rules.deny).push(pattern);
|
|
20598
|
+
}
|
|
20599
|
+
}
|
|
20600
|
+
return true;
|
|
20601
|
+
};
|
|
20602
|
+
var opencodeConfig = (value, rules) => {
|
|
20603
|
+
if (!isRecord4(value))
|
|
20604
|
+
return false;
|
|
20605
|
+
if (!opencodePermission(value.permission, rules))
|
|
20606
|
+
return false;
|
|
20607
|
+
if (value.agent === undefined)
|
|
20608
|
+
return true;
|
|
20609
|
+
if (!isRecord4(value.agent))
|
|
20610
|
+
return false;
|
|
20611
|
+
return Object.values(value.agent).every((agent) => isRecord4(agent) && opencodePermission(agent.permission, rules));
|
|
20612
|
+
};
|
|
20613
|
+
function opencodeRules(cwd, env = process.env) {
|
|
20614
|
+
const rules = EMPTY();
|
|
20615
|
+
try {
|
|
20616
|
+
const config = env.XDG_CONFIG_HOME || path22.join(homeOf(env), ".config");
|
|
20617
|
+
const files = [
|
|
20618
|
+
...OPENCODE_NAMES.map((name) => path22.join(config, "opencode", name)),
|
|
20619
|
+
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path22.join(dir, name), path22.join(dir, ".opencode", name)])),
|
|
20620
|
+
...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
|
|
20621
|
+
];
|
|
20622
|
+
for (const file of files) {
|
|
20623
|
+
const text = readText2(file);
|
|
20624
|
+
if (text !== null && !opencodeConfig(parseJsonc(text), rules))
|
|
20625
|
+
return "unreadable";
|
|
20626
|
+
}
|
|
20627
|
+
if (env.OPENCODE_CONFIG_CONTENT && !opencodeConfig(parseJsonc(env.OPENCODE_CONFIG_CONTENT), rules))
|
|
20628
|
+
return "unreadable";
|
|
20629
|
+
} catch {
|
|
20630
|
+
return "unreadable";
|
|
20631
|
+
}
|
|
20632
|
+
return rules;
|
|
20633
|
+
}
|
|
20634
|
+
var codexRule = (body) => {
|
|
20635
|
+
const open = /\bpattern\s*=\s*\[/u.exec(body);
|
|
20636
|
+
if (!open)
|
|
20637
|
+
return null;
|
|
20638
|
+
const words = [];
|
|
20639
|
+
let index = open.index + open[0].length;
|
|
20640
|
+
for (;; ) {
|
|
20641
|
+
while (/[\s,]/u.test(body[index] ?? ""))
|
|
20642
|
+
index += 1;
|
|
20643
|
+
if (body[index] === "]")
|
|
20644
|
+
break;
|
|
20645
|
+
if (body[index] !== '"')
|
|
20646
|
+
return null;
|
|
20647
|
+
const literal = /"((?:[^"\\]|\\.)*)"/y;
|
|
20648
|
+
literal.lastIndex = index;
|
|
20649
|
+
const match = literal.exec(body);
|
|
20650
|
+
if (!match)
|
|
20651
|
+
return null;
|
|
20652
|
+
words.push(JSON.parse(`"${match[1]}"`));
|
|
20653
|
+
index = literal.lastIndex;
|
|
20654
|
+
}
|
|
20655
|
+
const decision = /\bdecision\s*=\s*"(\w+)"/u.exec(body)?.[1];
|
|
20656
|
+
if (!words.length || !decision || !["allow", "prompt", "forbidden"].includes(decision))
|
|
20657
|
+
return null;
|
|
20658
|
+
return { pattern: words.join(" "), decision };
|
|
20659
|
+
};
|
|
20660
|
+
function codexRules(cwd, env = process.env) {
|
|
20661
|
+
const rules = EMPTY();
|
|
20662
|
+
try {
|
|
20663
|
+
const home = env.CODEX_HOME || path22.join(homeOf(env), ".codex");
|
|
20664
|
+
const top = gitTop(cwd) ?? cwd;
|
|
20665
|
+
for (const dir of [path22.join(home, "rules"), path22.join(top, ".codex", "rules")]) {
|
|
20666
|
+
let files;
|
|
20667
|
+
try {
|
|
20668
|
+
files = fs17.readdirSync(dir).filter((name) => name.endsWith(".rules"));
|
|
20669
|
+
} catch (error) {
|
|
20670
|
+
if (error.code === "ENOENT")
|
|
20671
|
+
continue;
|
|
20672
|
+
return "unreadable";
|
|
20673
|
+
}
|
|
20674
|
+
for (const name of files) {
|
|
20675
|
+
const text = readText2(path22.join(dir, name)) ?? "";
|
|
20676
|
+
for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
|
|
20677
|
+
const rule = codexRule(part);
|
|
20678
|
+
if (!rule)
|
|
20679
|
+
return "unreadable";
|
|
20680
|
+
(rule.decision === "allow" ? rules.allow : rules.deny).push(rule.pattern);
|
|
20681
|
+
}
|
|
20682
|
+
}
|
|
20683
|
+
}
|
|
20684
|
+
} catch {
|
|
20685
|
+
return "unreadable";
|
|
20686
|
+
}
|
|
20687
|
+
return rules;
|
|
20688
|
+
}
|
|
20689
|
+
function cursorRules(cwd, env = process.env) {
|
|
20690
|
+
const rules = EMPTY();
|
|
20691
|
+
try {
|
|
20692
|
+
const top = gitTop(cwd) ?? cwd;
|
|
20693
|
+
for (const file of [
|
|
20694
|
+
path22.join(homeOf(env), ".cursor", "cli-config.json"),
|
|
20695
|
+
path22.join(top, ".cursor", "cli.json")
|
|
20696
|
+
]) {
|
|
20697
|
+
const text = readText2(file);
|
|
20698
|
+
if (text === null)
|
|
20699
|
+
continue;
|
|
20700
|
+
const value = JSON.parse(text);
|
|
20701
|
+
if (!isRecord4(value))
|
|
20702
|
+
return "unreadable";
|
|
20703
|
+
if (value.permissions === undefined)
|
|
20704
|
+
continue;
|
|
20705
|
+
if (!isRecord4(value.permissions))
|
|
20706
|
+
return "unreadable";
|
|
20707
|
+
for (const [key, bucket] of [
|
|
20708
|
+
["deny", rules.deny],
|
|
20709
|
+
["allow", rules.allow]
|
|
20710
|
+
]) {
|
|
20711
|
+
const list = value.permissions[key];
|
|
20712
|
+
if (list === undefined)
|
|
20713
|
+
continue;
|
|
20714
|
+
if (!Array.isArray(list))
|
|
20715
|
+
return "unreadable";
|
|
20716
|
+
for (const entry of list) {
|
|
20717
|
+
if (typeof entry !== "string")
|
|
20718
|
+
return "unreadable";
|
|
20719
|
+
const match = /^Shell(?:\((.*)\))?$/su.exec(entry.trim());
|
|
20720
|
+
if (match)
|
|
20721
|
+
bucket.push(match[1] ?? "");
|
|
20722
|
+
}
|
|
20723
|
+
}
|
|
20724
|
+
}
|
|
20725
|
+
} catch {
|
|
20726
|
+
return "unreadable";
|
|
20727
|
+
}
|
|
20728
|
+
return rules;
|
|
20729
|
+
}
|
|
20730
|
+
var baseName = (word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "");
|
|
20731
|
+
var firstToken = (pattern) => pattern.trim().split(/[:\s]/u)[0] ?? "";
|
|
20732
|
+
function names(pattern, words) {
|
|
20733
|
+
const token = firstToken(pattern);
|
|
20734
|
+
if (!token || token === "*" || token.includes("*"))
|
|
20735
|
+
return true;
|
|
20736
|
+
const wanted = baseName(token);
|
|
20737
|
+
return words.some((word) => baseName(word) === wanted);
|
|
20738
|
+
}
|
|
20739
|
+
var literalPrefix = (pattern) => pattern.trim().replace(/(?::\*| \*|\*)$/u, "");
|
|
20740
|
+
var coversWrapper = (pattern) => {
|
|
20741
|
+
const token = firstToken(pattern);
|
|
20742
|
+
if (!token || token.includes("*"))
|
|
20743
|
+
return true;
|
|
20744
|
+
const words = literalPrefix(pattern).split(/\s+/u);
|
|
20745
|
+
if (baseName(words[0]) !== "workit")
|
|
20746
|
+
return false;
|
|
20747
|
+
return ["check", "--heavy"].slice(0, words.length - 1).every((word, at) => {
|
|
20748
|
+
const given = words[at + 1];
|
|
20749
|
+
return given === word || word.startsWith(given);
|
|
20750
|
+
});
|
|
20751
|
+
};
|
|
20752
|
+
var coversCommand = (pattern, command) => {
|
|
20753
|
+
const prefix = literalPrefix(pattern);
|
|
20754
|
+
if (!prefix)
|
|
20755
|
+
return true;
|
|
20756
|
+
return !prefix.includes("*") && command.trim().startsWith(prefix);
|
|
20757
|
+
};
|
|
20758
|
+
function wrapAllowed(read, words, command, options = {}) {
|
|
20759
|
+
if (read === "unreadable")
|
|
20760
|
+
return { ok: false, why: "named" };
|
|
20761
|
+
if (read.deny.some((pattern) => names(pattern, words)))
|
|
20762
|
+
return { ok: false, why: "named" };
|
|
20763
|
+
if (!options.bypass && read.allow.some(coversWrapper) && !read.allow.some((pattern) => coversCommand(pattern, command)))
|
|
20764
|
+
return { ok: false, why: "allowlisted" };
|
|
20765
|
+
return { ok: true };
|
|
20766
|
+
}
|
|
20767
|
+
|
|
20768
|
+
// packages/workit-core/src/core/heavy.ts
|
|
20769
|
+
import fs18 from "node:fs";
|
|
20325
20770
|
import os7 from "node:os";
|
|
20326
20771
|
import path23 from "node:path";
|
|
20772
|
+
function stateDir(env, platform = process.platform) {
|
|
20773
|
+
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
20774
|
+
return env.WORKFLOW_TOOLKIT_STATE;
|
|
20775
|
+
if (env.XDG_STATE_HOME)
|
|
20776
|
+
return path23.join(env.XDG_STATE_HOME, "workit");
|
|
20777
|
+
const home = env.HOME || os7.homedir();
|
|
20778
|
+
if (platform === "darwin")
|
|
20779
|
+
return path23.join(home, "Library", "Application Support", "workit");
|
|
20780
|
+
if (platform === "win32")
|
|
20781
|
+
return path23.join(env.LOCALAPPDATA ?? path23.join(home, "AppData", "Local"), "workit");
|
|
20782
|
+
return path23.join(home, ".local", "state", "workit");
|
|
20783
|
+
}
|
|
20784
|
+
var HEAVY_COMMANDS_FILE = "heavy-commands.json";
|
|
20785
|
+
var heavyCommandsFile = (env) => path23.join(stateDir(env), HEAVY_COMMANDS_FILE);
|
|
20786
|
+
var HEAVY_SCRIPT = /^(?:test|tests|t|tst|unit|integration|e2e|coverage|cov|lint|eslint|typecheck|type-check|types|tsc|check|verify|validate|build|compile|bundle|ci|sonar|cypress|playwright|jest|vitest|mocha|bench|benchmark|all)(?:$|[:_.-])/iu;
|
|
20787
|
+
var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
|
|
20788
|
+
var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
|
|
20789
|
+
var WATCH_SCRIPT = /(?:^|[:_.-])(?:watch|dev|serve|start|storybook)(?:$|[:_.-])/iu;
|
|
20790
|
+
var sub = (...names) => new Set(names);
|
|
20791
|
+
var TOOLS = {
|
|
20792
|
+
jest: "any",
|
|
20793
|
+
vitest: "any",
|
|
20794
|
+
mocha: "any",
|
|
20795
|
+
ava: "any",
|
|
20796
|
+
tap: "any",
|
|
20797
|
+
karma: "any",
|
|
20798
|
+
jasmine: "any",
|
|
20799
|
+
nyc: "any",
|
|
20800
|
+
c8: "any",
|
|
20801
|
+
tsc: "any",
|
|
20802
|
+
"vue-tsc": "any",
|
|
20803
|
+
"svelte-check": "any",
|
|
20804
|
+
eslint: "any",
|
|
20805
|
+
stylelint: "any",
|
|
20806
|
+
tslint: "any",
|
|
20807
|
+
webpack: "any",
|
|
20808
|
+
"webpack-cli": "any",
|
|
20809
|
+
rollup: "any",
|
|
20810
|
+
parcel: "any",
|
|
20811
|
+
tsup: "any",
|
|
20812
|
+
vite: sub("build", "optimize"),
|
|
20813
|
+
next: sub("build", "lint"),
|
|
20814
|
+
nuxt: sub("build", "generate"),
|
|
20815
|
+
nuxi: sub("build", "generate"),
|
|
20816
|
+
astro: sub("build", "check"),
|
|
20817
|
+
ng: sub("build", "b", "test", "t", "e2e", "lint"),
|
|
20818
|
+
"react-scripts": sub("build", "test"),
|
|
20819
|
+
playwright: sub("test", "install"),
|
|
20820
|
+
cypress: sub("run"),
|
|
20821
|
+
wdio: "any",
|
|
20822
|
+
testcafe: "any",
|
|
20823
|
+
turbo: "any",
|
|
20824
|
+
lerna: sub("run", "exec", "bootstrap"),
|
|
20825
|
+
"sonar-scanner": "any",
|
|
20826
|
+
sonarless: sub("scan"),
|
|
20827
|
+
"sonarless-mcp": sub("scan"),
|
|
20828
|
+
go: sub("test", "build", "vet", "install", "generate"),
|
|
20829
|
+
"golangci-lint": sub("run"),
|
|
20830
|
+
gotestsum: "any",
|
|
20831
|
+
staticcheck: "any",
|
|
20832
|
+
cargo: sub("build", "b", "test", "t", "check", "c", "clippy", "bench", "nextest", "doc", "install", "tarpaulin", "llvm-cov"),
|
|
20833
|
+
mvn: "any",
|
|
20834
|
+
mvnw: "any",
|
|
20835
|
+
gradle: "any",
|
|
20836
|
+
gradlew: "any",
|
|
20837
|
+
ant: "any",
|
|
20838
|
+
sbt: "any",
|
|
20839
|
+
bazel: sub("build", "test", "run", "coverage"),
|
|
20840
|
+
bazelisk: sub("build", "test", "run", "coverage"),
|
|
20841
|
+
buck2: sub("build", "test", "run"),
|
|
20842
|
+
dotnet: sub("build", "test", "publish", "restore", "pack", "msbuild"),
|
|
20843
|
+
msbuild: "any",
|
|
20844
|
+
pytest: "any",
|
|
20845
|
+
"py.test": "any",
|
|
20846
|
+
tox: "any",
|
|
20847
|
+
nox: "any",
|
|
20848
|
+
mypy: "any",
|
|
20849
|
+
pyright: "any",
|
|
20850
|
+
pylint: "any",
|
|
20851
|
+
coverage: sub("run"),
|
|
20852
|
+
maturin: sub("build", "develop"),
|
|
20853
|
+
pip: sub("install", "wheel"),
|
|
20854
|
+
pip3: sub("install", "wheel"),
|
|
20855
|
+
rspec: "any",
|
|
20856
|
+
rails: sub("test", "assets:precompile"),
|
|
20857
|
+
phpunit: "any",
|
|
20858
|
+
pest: "any",
|
|
20859
|
+
phpstan: "any",
|
|
20860
|
+
psalm: "any",
|
|
20861
|
+
composer: sub("install", "update", "require", "test"),
|
|
20862
|
+
mix: sub("test", "compile", "deps.get", "dialyzer", "credo"),
|
|
20863
|
+
swift: sub("build", "test"),
|
|
20864
|
+
xcodebuild: "any",
|
|
20865
|
+
flutter: sub("build", "test"),
|
|
20866
|
+
dart: sub("test", "compile"),
|
|
20867
|
+
pod: sub("install"),
|
|
20868
|
+
cmake: sub("--build"),
|
|
20869
|
+
ninja: "any",
|
|
20870
|
+
meson: sub("compile", "test"),
|
|
20871
|
+
ctest: "any",
|
|
20872
|
+
scons: "any",
|
|
20873
|
+
"docker-compose": sub("build"),
|
|
20874
|
+
buildah: sub("bud", "build"),
|
|
20875
|
+
earthly: "any"
|
|
20876
|
+
};
|
|
20877
|
+
var PM_VALUES = new Set([
|
|
20878
|
+
"--prefix",
|
|
20879
|
+
"-w",
|
|
20880
|
+
"--workspace",
|
|
20881
|
+
"-C",
|
|
20882
|
+
"--dir",
|
|
20883
|
+
"--filter",
|
|
20884
|
+
"-F",
|
|
20885
|
+
"--cwd",
|
|
20886
|
+
"-p",
|
|
20887
|
+
"--package",
|
|
20888
|
+
"--cache",
|
|
20889
|
+
"--registry"
|
|
20890
|
+
]);
|
|
20891
|
+
function positional2(words, from, values = PM_VALUES) {
|
|
20892
|
+
let index = from;
|
|
20893
|
+
while (index < words.length && words[index].startsWith("-")) {
|
|
20894
|
+
if (words[index] === "--")
|
|
20895
|
+
return index + 1;
|
|
20896
|
+
index += values.has(words[index]) ? 2 : 1;
|
|
20897
|
+
}
|
|
20898
|
+
return index;
|
|
20899
|
+
}
|
|
20900
|
+
var baseName2 = (word) => (word?.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat|ps1)$/u, "");
|
|
20901
|
+
var WORKIT = /^(?:workit|@brainervirus\/workit-cli(?:@.*)?)$/u;
|
|
20902
|
+
var PYTHONS = /^(?:python[\d.]*|py)$/u;
|
|
20903
|
+
var NPM_INSTALL = new Set([
|
|
20904
|
+
"ci",
|
|
20905
|
+
"clean-install",
|
|
20906
|
+
"cit",
|
|
20907
|
+
"install-test",
|
|
20908
|
+
"it",
|
|
20909
|
+
"install",
|
|
20910
|
+
"i",
|
|
20911
|
+
"in",
|
|
20912
|
+
"add",
|
|
20913
|
+
"rebuild",
|
|
20914
|
+
"update",
|
|
20915
|
+
"up"
|
|
20916
|
+
]);
|
|
20917
|
+
var NPM_RUN = new Set(["run", "run-script", "rum", "urn"]);
|
|
20918
|
+
var NPM_TEST = new Set(["test", "t", "tst"]);
|
|
20919
|
+
var EXEC = new Set(["exec", "x", "dlx"]);
|
|
20920
|
+
function builtIn(words, depth = 0) {
|
|
20921
|
+
if (depth > 6 || !words.length)
|
|
20922
|
+
return null;
|
|
20923
|
+
const name = baseName2(words[0]);
|
|
20924
|
+
const label = () => words.slice(0, 3).join(" ");
|
|
20925
|
+
const inner = (from) => builtIn(words.slice(from), depth + 1);
|
|
20926
|
+
if (WORKIT.test(name))
|
|
20927
|
+
return null;
|
|
20928
|
+
if (INFO_FLAGS.has(words[1] ?? "") && words.length === 2)
|
|
20929
|
+
return null;
|
|
20930
|
+
if (words.some((word) => word === "--watch" || word.startsWith("--watch=") || word === "--watchAll"))
|
|
20931
|
+
return null;
|
|
20932
|
+
if (name === "npx" || name === "bunx" || name === "pnpx")
|
|
20933
|
+
return inner(positional2(words, 1));
|
|
20934
|
+
if (name === "corepack")
|
|
20935
|
+
return inner(1);
|
|
20936
|
+
if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun" || name === "deno") {
|
|
20937
|
+
const at = positional2(words, 1);
|
|
20938
|
+
const command = words[at];
|
|
20939
|
+
if (command === undefined)
|
|
20940
|
+
return name === "yarn" ? "yarn (install)" : null;
|
|
20941
|
+
if (name === "deno") {
|
|
20942
|
+
if (["test", "bench", "check", "compile", "lint"].includes(command))
|
|
20943
|
+
return label();
|
|
20944
|
+
if (command === "task" && HEAVY_SCRIPT.test(words[at + 1] ?? ""))
|
|
20945
|
+
return label();
|
|
20946
|
+
return null;
|
|
20947
|
+
}
|
|
20948
|
+
if (NPM_INSTALL.has(command) || NPM_TEST.has(command))
|
|
20949
|
+
return label();
|
|
20950
|
+
if (name === "bun" && command === "build")
|
|
20951
|
+
return label();
|
|
20952
|
+
if (EXEC.has(command))
|
|
20953
|
+
return inner(positional2(words, at + 1));
|
|
20954
|
+
if (name === "yarn" && command === "workspace")
|
|
20955
|
+
return inner(at + 2);
|
|
20956
|
+
if (name === "yarn" && command === "workspaces")
|
|
20957
|
+
return words.slice(at + 1).some((word) => HEAVY_SCRIPT.test(word)) ? label() : null;
|
|
20958
|
+
if (NPM_RUN.has(command)) {
|
|
20959
|
+
const script = words[positional2(words, at + 1)];
|
|
20960
|
+
if (script === undefined || WATCH_SCRIPT.test(script))
|
|
20961
|
+
return null;
|
|
20962
|
+
if (HEAVY_SCRIPT.test(script))
|
|
20963
|
+
return label();
|
|
20964
|
+
return name === "bun" ? inner(positional2(words, at + 1)) : null;
|
|
20965
|
+
}
|
|
20966
|
+
if (name === "npm")
|
|
20967
|
+
return null;
|
|
20968
|
+
if (WATCH_SCRIPT.test(command))
|
|
20969
|
+
return null;
|
|
20970
|
+
if (HEAVY_SCRIPT.test(command) && !/\.[cm]?[jt]sx?$/u.test(command))
|
|
20971
|
+
return label();
|
|
20972
|
+
return inner(at);
|
|
20973
|
+
}
|
|
20974
|
+
if (PYTHONS.test(name)) {
|
|
20975
|
+
const at = words.indexOf("-m");
|
|
20976
|
+
return at > 0 && words[at + 1] ? inner(at + 1) : null;
|
|
20977
|
+
}
|
|
20978
|
+
if (["uv", "poetry", "pdm", "pipenv", "hatch", "rye"].includes(name)) {
|
|
20979
|
+
const at = positional2(words, 1);
|
|
20980
|
+
const command = words[at];
|
|
20981
|
+
if (command === "run")
|
|
20982
|
+
return inner(positional2(words, at + 1));
|
|
20983
|
+
if (command === "pip")
|
|
20984
|
+
return inner(at);
|
|
20985
|
+
return ["sync", "install", "build", "test", "add"].includes(command ?? "") ? label() : null;
|
|
20986
|
+
}
|
|
20987
|
+
if (name === "bundle") {
|
|
20988
|
+
const command = words[positional2(words, 1)];
|
|
20989
|
+
if (command === "exec")
|
|
20990
|
+
return inner(positional2(words, 2));
|
|
20991
|
+
return command === "install" || command === undefined ? label() : null;
|
|
20992
|
+
}
|
|
20993
|
+
if (name === "make" || name === "gmake") {
|
|
20994
|
+
const targets = words.slice(1).filter((word) => !word.startsWith("-") && !word.includes("="));
|
|
20995
|
+
if (targets.some((target) => WATCH_SCRIPT.test(target)))
|
|
20996
|
+
return null;
|
|
20997
|
+
return !targets.length || targets.some((target) => HEAVY_SCRIPT.test(target)) ? label() : null;
|
|
20998
|
+
}
|
|
20999
|
+
if (name === "rake") {
|
|
21000
|
+
const targets = words.slice(1).filter((word) => !word.startsWith("-") && !word.includes("="));
|
|
21001
|
+
return targets.some((target) => HEAVY_SCRIPT.test(target) || /^spec(?:$|:)/u.test(target)) ? label() : null;
|
|
21002
|
+
}
|
|
21003
|
+
if (name === "nx") {
|
|
21004
|
+
const at = positional2(words, 1);
|
|
21005
|
+
const command = words[at] ?? "";
|
|
21006
|
+
if (["run-many", "affected"].includes(command))
|
|
21007
|
+
return words.some((word) => /^(?:-t|--targets?)(?:=|$)/u.test(word)) && !words.some((word) => WATCH_SCRIPT.test(word.replace(/^--?targets?=/u, ""))) ? label() : null;
|
|
21008
|
+
if (command === "run") {
|
|
21009
|
+
const target = (words[at + 1] ?? "").split(":")[1] ?? "";
|
|
21010
|
+
return HEAVY_SCRIPT.test(target) && !WATCH_SCRIPT.test(target) ? label() : null;
|
|
21011
|
+
}
|
|
21012
|
+
return HEAVY_SCRIPT.test(command) && !WATCH_SCRIPT.test(command) ? label() : null;
|
|
21013
|
+
}
|
|
21014
|
+
if (name === "docker" || name === "podman" || name === "nerdctl") {
|
|
21015
|
+
const at = positional2(words, 1, new Set(["-H", "--host", "--context", "-c", "--config"]));
|
|
21016
|
+
const command = words[at];
|
|
21017
|
+
if (command === "build")
|
|
21018
|
+
return label();
|
|
21019
|
+
if (command === "buildx" || command === "image" || command === "builder")
|
|
21020
|
+
return words[positional2(words, at + 1)] === "build" ? label() : null;
|
|
21021
|
+
if (command === "compose") {
|
|
21022
|
+
const at2 = positional2(words, at + 1, new Set(["-f", "--file", "-p"]));
|
|
21023
|
+
const action = words[at2];
|
|
21024
|
+
if (action === "build")
|
|
21025
|
+
return label();
|
|
21026
|
+
return action === "up" && words.slice(at2 + 1).includes("--build") ? label() : null;
|
|
21027
|
+
}
|
|
21028
|
+
return null;
|
|
21029
|
+
}
|
|
21030
|
+
const rule = TOOLS[name];
|
|
21031
|
+
if (!rule)
|
|
21032
|
+
return null;
|
|
21033
|
+
if (words.slice(1).some((word) => WATCH_FLAG.test(word) || word === "watch" || word === "dev"))
|
|
21034
|
+
return null;
|
|
21035
|
+
if (rule === "any")
|
|
21036
|
+
return label();
|
|
21037
|
+
const command = words.slice(1).find((word) => !word.startsWith("-") && !word.startsWith("+") || rule.has(word));
|
|
21038
|
+
return command !== undefined && rule.has(command) ? label() : null;
|
|
21039
|
+
}
|
|
21040
|
+
var globWord = (word) => new RegExp(`^${word.replace(/[.+?^${}()|[\]\\]/gu, "\\$&").replaceAll("*", ".*")}$`, "u");
|
|
21041
|
+
function parsePattern(pattern) {
|
|
21042
|
+
const words = pattern.trim().split(/\s+/u).filter(Boolean);
|
|
21043
|
+
return words.length ? words.map(globWord) : null;
|
|
21044
|
+
}
|
|
21045
|
+
var matches = (matchers, words) => matchers.length <= words.length && matchers.every((matcher, index) => index === 0 ? matcher.test(words[0]) || matcher.test(baseName2(words[0])) : matcher.test(words[index]));
|
|
21046
|
+
var readJson = (file) => {
|
|
21047
|
+
try {
|
|
21048
|
+
return JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
21049
|
+
} catch {
|
|
21050
|
+
return;
|
|
21051
|
+
}
|
|
21052
|
+
};
|
|
21053
|
+
var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21054
|
+
var stringList = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
21055
|
+
function machinePatterns(env) {
|
|
21056
|
+
const value = readJson(heavyCommandsFile(env));
|
|
21057
|
+
return isRecord5(value) ? stringList(value.patterns) : [];
|
|
21058
|
+
}
|
|
21059
|
+
function repoPatterns(cwd) {
|
|
21060
|
+
return repoHeavyCommands(gitTop(cwd) ?? path23.resolve(cwd));
|
|
21061
|
+
}
|
|
21062
|
+
function heavyLines(lines, options) {
|
|
21063
|
+
const found = [];
|
|
21064
|
+
let patterns = null;
|
|
21065
|
+
for (const line of lines) {
|
|
21066
|
+
const reason = builtIn(line);
|
|
21067
|
+
if (reason) {
|
|
21068
|
+
found.push({ line, source: "catalog", reason });
|
|
21069
|
+
continue;
|
|
21070
|
+
}
|
|
21071
|
+
patterns ??= (options.patterns ?? [
|
|
21072
|
+
...machinePatterns(options.env ?? process.env).map((pattern) => ({
|
|
21073
|
+
pattern,
|
|
21074
|
+
from: "machine"
|
|
21075
|
+
})),
|
|
21076
|
+
...repoPatterns(options.cwd).map((pattern) => ({ pattern, from: "repo" }))
|
|
21077
|
+
]).flatMap((entry) => {
|
|
21078
|
+
const matchers = parsePattern(entry.pattern);
|
|
21079
|
+
return matchers ? [{ ...entry, matchers }] : [];
|
|
21080
|
+
});
|
|
21081
|
+
const hit = patterns.find((entry) => matches(entry.matchers, line));
|
|
21082
|
+
if (hit)
|
|
21083
|
+
found.push({
|
|
21084
|
+
line,
|
|
21085
|
+
source: { pattern: hit.pattern, from: hit.from },
|
|
21086
|
+
reason: `${hit.from} pattern "${hit.pattern}"`
|
|
21087
|
+
});
|
|
21088
|
+
}
|
|
21089
|
+
return found;
|
|
21090
|
+
}
|
|
21091
|
+
var linesOf = (command, dialect) => {
|
|
21092
|
+
try {
|
|
21093
|
+
return commandLines(command, dialect);
|
|
21094
|
+
} catch {
|
|
21095
|
+
return null;
|
|
21096
|
+
}
|
|
21097
|
+
};
|
|
21098
|
+
var quoteWord = (word) => /^[\w@%+=:,./-]+$/u.test(word) ? word : `'${word.replaceAll("'", `'\\''`)}'`;
|
|
21099
|
+
var leaseCommand = (words) => `workit check --heavy -- ${words.map(quoteWord).join(" ")}`;
|
|
21100
|
+
function heavyPlan(command, options) {
|
|
21101
|
+
const dialect = options.dialect ?? "posix";
|
|
21102
|
+
if (dialect !== "posix" || !gitTop(options.cwd))
|
|
21103
|
+
return { kind: "none" };
|
|
21104
|
+
const lines = linesOf(command, dialect);
|
|
21105
|
+
if (!lines)
|
|
21106
|
+
return { kind: "none" };
|
|
21107
|
+
const heavy = heavyLines(lines, options);
|
|
21108
|
+
if (!heavy.length)
|
|
21109
|
+
return { kind: "none" };
|
|
21110
|
+
const simple = simpleCommand(command, dialect);
|
|
21111
|
+
const single = simple && !WORKIT.test(baseName2(simple[0])) && heavyLines([simple], options)[0];
|
|
21112
|
+
const judged = single ? [{ words: lines.flat(), text: command }] : heavy.map((match) => ({
|
|
21113
|
+
words: match.line,
|
|
21114
|
+
text: match.line.join(" ")
|
|
21115
|
+
}));
|
|
21116
|
+
if (options.rules !== null)
|
|
21117
|
+
for (const { words, text } of judged) {
|
|
21118
|
+
const verdict = wrapAllowed(options.rules, words, text, { bypass: options.bypass });
|
|
21119
|
+
if (!verdict.ok)
|
|
21120
|
+
return {
|
|
21121
|
+
kind: "passthrough",
|
|
21122
|
+
note: `workit: \`${command.trim()}\` is heavy but runs outside the heavy-check lease because a host permission rule names it (or \`workit\` is allowlisted); the host decides.`
|
|
21123
|
+
};
|
|
21124
|
+
}
|
|
21125
|
+
if (single) {
|
|
21126
|
+
const rewrite = leaseRewrite(command);
|
|
21127
|
+
return rewrite ? { kind: "rewrite", command: rewrite, reason: single.reason } : { kind: "none" };
|
|
21128
|
+
}
|
|
21129
|
+
const parts = heavy.map((match) => leaseCommand(match.line));
|
|
21130
|
+
return {
|
|
21131
|
+
kind: "split",
|
|
21132
|
+
reason: `heavy_command: \`${command.trim()}\` chains heavy work (${heavy.map((match) => match.reason).join("; ")}) with other commands, and a chain cannot run under the machine-wide heavy-check lease. Run each heavy part alone as \`workit check --heavy -- <segment>\` (here: ${parts.map((part) => `\`${part}\``).join("; ")}); run the other parts as their own commands.`,
|
|
21133
|
+
unblock: parts[0] ?? null
|
|
21134
|
+
};
|
|
21135
|
+
}
|
|
21136
|
+
function workitOnPath2(env = process.env) {
|
|
21137
|
+
const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
|
|
21138
|
+
for (const dir of (env.PATH ?? "").split(path23.delimiter))
|
|
21139
|
+
for (const name of names) {
|
|
21140
|
+
if (!dir)
|
|
21141
|
+
continue;
|
|
21142
|
+
try {
|
|
21143
|
+
fs18.accessSync(path23.join(dir, name), fs18.constants.X_OK);
|
|
21144
|
+
return true;
|
|
21145
|
+
} catch {}
|
|
21146
|
+
}
|
|
21147
|
+
return false;
|
|
21148
|
+
}
|
|
21149
|
+
var ASSIGNMENTS = /^\s*((?:[A-Za-z_]\w*=(?:'[^']*'|"(?:[^"\\]|\\.)*"|[^\s'"])*\s+)*)/u;
|
|
21150
|
+
function leaseRewrite(command) {
|
|
21151
|
+
const text = command.trim();
|
|
21152
|
+
const prefix = ASSIGNMENTS.exec(text)?.[1] ?? "";
|
|
21153
|
+
const rest = text.slice(prefix.length).trim();
|
|
21154
|
+
if (!rest)
|
|
21155
|
+
return null;
|
|
21156
|
+
return `${prefix}workit check --heavy -- ${rest}`;
|
|
21157
|
+
}
|
|
21158
|
+
|
|
21159
|
+
// packages/workit-core/src/core/heavy-words.ts
|
|
21160
|
+
import fs19 from "node:fs";
|
|
21161
|
+
import os8 from "node:os";
|
|
21162
|
+
import path24 from "node:path";
|
|
21163
|
+
var HEAVY_WORDS = new Set(("npm npx pnpm pnpx yarn bun bunx deno corepack make gmake docker podman nerdctl " + "python python3 py uv poetry pdm pipenv hatch rye bundle rake nx " + "jest vitest mocha ava tap karma jasmine nyc c8 tsc vue-tsc svelte-check eslint stylelint " + "tslint webpack webpack-cli rollup parcel tsup vite next nuxt nuxi astro ng react-scripts " + "playwright cypress wdio testcafe turbo lerna sonar-scanner sonarless sonarless-mcp go " + "golangci-lint gotestsum staticcheck cargo mvn mvnw gradle gradlew ant sbt bazel bazelisk " + "buck2 dotnet msbuild pytest py.test tox nox mypy pyright pylint coverage maturin pip pip3 " + "rspec rails phpunit pest phpstan psalm composer mix swift xcodebuild flutter dart pod cmake " + "ninja meson ctest scons docker-compose buildah earthly").split(" "));
|
|
21164
|
+
var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
|
|
21165
|
+
var stateDir2 = (env) => {
|
|
21166
|
+
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
21167
|
+
return env.WORKFLOW_TOOLKIT_STATE;
|
|
21168
|
+
if (env.XDG_STATE_HOME)
|
|
21169
|
+
return path24.join(env.XDG_STATE_HOME, "workit");
|
|
21170
|
+
const home = env.HOME || os8.homedir();
|
|
21171
|
+
if (process.platform === "darwin")
|
|
21172
|
+
return path24.join(home, "Library", "Application Support", "workit");
|
|
21173
|
+
if (process.platform === "win32")
|
|
21174
|
+
return path24.join(env.LOCALAPPDATA ?? path24.join(home, "AppData", "Local"), "workit");
|
|
21175
|
+
return path24.join(home, ".local", "state", "workit");
|
|
21176
|
+
};
|
|
21177
|
+
var readJson2 = (file) => {
|
|
21178
|
+
try {
|
|
21179
|
+
return JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
21180
|
+
} catch {
|
|
21181
|
+
return;
|
|
21182
|
+
}
|
|
21183
|
+
};
|
|
21184
|
+
var patternWords = (cwd, env) => {
|
|
21185
|
+
const patterns = [];
|
|
21186
|
+
const machine = readJson2(path24.join(stateDir2(env), "heavy-commands.json"));
|
|
21187
|
+
if (Array.isArray(machine?.patterns))
|
|
21188
|
+
patterns.push(...machine.patterns);
|
|
21189
|
+
for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
|
|
21190
|
+
if (fs19.existsSync(path24.join(dir, ".git"))) {
|
|
21191
|
+
patterns.push(...repoHeavyCommands(dir));
|
|
21192
|
+
break;
|
|
21193
|
+
}
|
|
21194
|
+
if (path24.dirname(dir) === dir)
|
|
21195
|
+
break;
|
|
21196
|
+
}
|
|
21197
|
+
const words = new Set;
|
|
21198
|
+
for (const pattern of patterns) {
|
|
21199
|
+
if (typeof pattern !== "string")
|
|
21200
|
+
continue;
|
|
21201
|
+
const first = wordsOf(pattern)[0];
|
|
21202
|
+
if (!first || first.includes("*"))
|
|
21203
|
+
return null;
|
|
21204
|
+
words.add(first);
|
|
21205
|
+
}
|
|
21206
|
+
return words;
|
|
21207
|
+
};
|
|
21208
|
+
function maybeHeavy(command, cwd, env = process.env) {
|
|
21209
|
+
try {
|
|
21210
|
+
const words = wordsOf(command);
|
|
21211
|
+
if (words.some((word) => HEAVY_WORDS.has(word) || /^python[\d.]*$/u.test(word)))
|
|
21212
|
+
return true;
|
|
21213
|
+
const extra = patternWords(cwd, env);
|
|
21214
|
+
return extra === null || words.some((word) => extra.has(word));
|
|
21215
|
+
} catch {
|
|
21216
|
+
return true;
|
|
21217
|
+
}
|
|
21218
|
+
}
|
|
21219
|
+
|
|
21220
|
+
// packages/workit-core/src/hooks/heavy-gate.ts
|
|
21221
|
+
var rulesFor = (host, cwd, env) => {
|
|
21222
|
+
switch (host) {
|
|
21223
|
+
case "claude_code":
|
|
21224
|
+
return claudeRules(cwd, env);
|
|
21225
|
+
case "opencode":
|
|
21226
|
+
return opencodeRules(cwd, env);
|
|
21227
|
+
case "codex_cli":
|
|
21228
|
+
case "codex_desktop":
|
|
21229
|
+
return codexRules(cwd, env);
|
|
21230
|
+
case "cursor":
|
|
21231
|
+
return cursorRules(cwd, env);
|
|
21232
|
+
case "pi":
|
|
21233
|
+
return null;
|
|
21234
|
+
}
|
|
21235
|
+
};
|
|
21236
|
+
function heavyGate(cwd, command, options) {
|
|
21237
|
+
const env = options.env ?? process.env;
|
|
21238
|
+
try {
|
|
21239
|
+
if (env.WORKIT_HEAVY_GATE === "0" || !maybeHeavy(command, cwd, env) || !workitOnPath2(env))
|
|
21240
|
+
return { kind: "none" };
|
|
21241
|
+
return heavyPlan(command, {
|
|
21242
|
+
cwd,
|
|
21243
|
+
env,
|
|
21244
|
+
dialect: options.dialect ?? "posix",
|
|
21245
|
+
rules: rulesFor(options.host, cwd, env),
|
|
21246
|
+
bypass: options.permissionMode === "bypassPermissions"
|
|
21247
|
+
});
|
|
21248
|
+
} catch {
|
|
21249
|
+
return { kind: "none" };
|
|
21250
|
+
}
|
|
21251
|
+
}
|
|
21252
|
+
var rewriteNote = (plan) => `workit: this heavy command (${plan.reason}) runs under the machine-wide heavy-check lease as \`${plan.command}\`: it waits while another heavy check holds the lease (\`workit lock status\` shows who). Run heavy commands that way yourself; \`workit help lock\` explains the lease.`;
|
|
21253
|
+
var heavyDenial = (original, plan) => ({
|
|
21254
|
+
kind: "deny",
|
|
21255
|
+
reason: `heavy_command: \`${original.trim()}\` is heavy (${plan.reason}) and must run under the machine-wide heavy-check lease, so parallel agents cannot exhaust this machine's memory and CPU. Run exactly: ${plan.command}`,
|
|
21256
|
+
unblock: plan.command
|
|
21257
|
+
});
|
|
21258
|
+
var splitDenial = (plan) => ({
|
|
21259
|
+
kind: "deny",
|
|
21260
|
+
reason: plan.reason,
|
|
21261
|
+
unblock: plan.unblock
|
|
21262
|
+
});
|
|
21263
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
21264
|
+
import { realpathSync as realpathSync5 } from "node:fs";
|
|
21265
|
+
import os10 from "node:os";
|
|
21266
|
+
import path26 from "node:path";
|
|
20327
21267
|
|
|
20328
21268
|
// packages/workit-core/src/core/live-session.ts
|
|
20329
21269
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
20330
21270
|
import { createHash as createHash15 } from "node:crypto";
|
|
20331
|
-
import
|
|
20332
|
-
import
|
|
20333
|
-
import
|
|
21271
|
+
import fs20 from "node:fs";
|
|
21272
|
+
import os9 from "node:os";
|
|
21273
|
+
import path25 from "node:path";
|
|
20334
21274
|
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
20335
21275
|
var HEARTBEAT_EVERY_MS = 30000;
|
|
20336
21276
|
var liveProbe = {
|
|
@@ -20351,9 +21291,9 @@ var processAlive = (pid) => {
|
|
|
20351
21291
|
var processInfo = (pid) => {
|
|
20352
21292
|
try {
|
|
20353
21293
|
if (liveProbe.platform === "linux") {
|
|
20354
|
-
const stat =
|
|
21294
|
+
const stat = fs20.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
20355
21295
|
const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
|
|
20356
|
-
const command =
|
|
21296
|
+
const command = fs20.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
|
|
20357
21297
|
return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
|
|
20358
21298
|
}
|
|
20359
21299
|
if (liveProbe.platform === "win32")
|
|
@@ -20404,35 +21344,35 @@ var liveOwner = (input, transport, env = process.env) => {
|
|
|
20404
21344
|
};
|
|
20405
21345
|
};
|
|
20406
21346
|
var checkoutOf = (target) => {
|
|
20407
|
-
let dir =
|
|
20408
|
-
while (!
|
|
20409
|
-
dir =
|
|
20410
|
-
if (
|
|
20411
|
-
dir =
|
|
20412
|
-
const parts = dir.split(
|
|
21347
|
+
let dir = path25.resolve(target);
|
|
21348
|
+
while (!fs20.existsSync(dir) && path25.dirname(dir) !== dir)
|
|
21349
|
+
dir = path25.dirname(dir);
|
|
21350
|
+
if (fs20.existsSync(dir) && !fs20.statSync(dir).isDirectory())
|
|
21351
|
+
dir = path25.dirname(dir);
|
|
21352
|
+
const parts = dir.split(path25.sep);
|
|
20413
21353
|
const dotGit = parts.lastIndexOf(".git");
|
|
20414
21354
|
if (dotGit > 0)
|
|
20415
|
-
dir = parts.slice(0, dotGit).join(
|
|
21355
|
+
dir = parts.slice(0, dotGit).join(path25.sep) || path25.sep;
|
|
20416
21356
|
const location = resolveStore(dir);
|
|
20417
21357
|
if (location instanceof Error || !location.shared || !location.top)
|
|
20418
21358
|
return null;
|
|
20419
21359
|
let top = location.top;
|
|
20420
21360
|
try {
|
|
20421
|
-
top =
|
|
21361
|
+
top = fs20.realpathSync.native(top);
|
|
20422
21362
|
} catch {}
|
|
20423
21363
|
return top;
|
|
20424
21364
|
};
|
|
20425
|
-
var markerFile2 = (top) =>
|
|
21365
|
+
var markerFile2 = (top) => path25.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
|
|
20426
21366
|
var checkoutKey = (top, platform = process.platform) => {
|
|
20427
|
-
let key =
|
|
21367
|
+
let key = path25.resolve(top);
|
|
20428
21368
|
try {
|
|
20429
|
-
key =
|
|
21369
|
+
key = fs20.realpathSync.native(key);
|
|
20430
21370
|
} catch {}
|
|
20431
21371
|
return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
|
|
20432
21372
|
};
|
|
20433
21373
|
var readMarker = (file) => {
|
|
20434
21374
|
try {
|
|
20435
|
-
const value = JSON.parse(
|
|
21375
|
+
const value = JSON.parse(fs20.readFileSync(file, "utf8"));
|
|
20436
21376
|
if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
|
|
20437
21377
|
return null;
|
|
20438
21378
|
return {
|
|
@@ -20455,7 +21395,7 @@ var isLive = (marker, deps) => {
|
|
|
20455
21395
|
const age = ageOf2(marker, deps.now ?? new Date);
|
|
20456
21396
|
if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
|
|
20457
21397
|
return false;
|
|
20458
|
-
if (marker.machine !== (deps.machine ??
|
|
21398
|
+
if (marker.machine !== (deps.machine ?? os9.hostname()) || marker.pid === null)
|
|
20459
21399
|
return true;
|
|
20460
21400
|
if (!(deps.alive ?? processAlive)(marker.pid))
|
|
20461
21401
|
return false;
|
|
@@ -20484,7 +21424,7 @@ var liveConflict = (target, owner, deps = {}) => {
|
|
|
20484
21424
|
const marker = readMarker(file);
|
|
20485
21425
|
if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
|
|
20486
21426
|
return null;
|
|
20487
|
-
if (sameOwner(marker, owner, deps.machine ??
|
|
21427
|
+
if (sameOwner(marker, owner, deps.machine ?? os9.hostname()))
|
|
20488
21428
|
return null;
|
|
20489
21429
|
return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
|
|
20490
21430
|
} catch {
|
|
@@ -20498,7 +21438,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
|
|
|
20498
21438
|
return;
|
|
20499
21439
|
const file = markerFile2(checkout);
|
|
20500
21440
|
const now = deps.now ?? new Date;
|
|
20501
|
-
const machine = deps.machine ??
|
|
21441
|
+
const machine = deps.machine ?? os9.hostname();
|
|
20502
21442
|
const current = readMarker(file);
|
|
20503
21443
|
if (current) {
|
|
20504
21444
|
const age = ageOf2(current, now);
|
|
@@ -20519,10 +21459,10 @@ var claimLiveSession = (target, owner, deps = {}) => {
|
|
|
20519
21459
|
checkout,
|
|
20520
21460
|
heartbeatAt: now.toISOString()
|
|
20521
21461
|
};
|
|
20522
|
-
|
|
21462
|
+
fs20.mkdirSync(path25.dirname(file), { recursive: true });
|
|
20523
21463
|
const temp = `${file}.${process.pid}.tmp`;
|
|
20524
|
-
|
|
20525
|
-
|
|
21464
|
+
fs20.writeFileSync(temp, JSON.stringify(marker));
|
|
21465
|
+
fs20.renameSync(temp, file);
|
|
20526
21466
|
} catch {}
|
|
20527
21467
|
};
|
|
20528
21468
|
|
|
@@ -20531,27 +21471,42 @@ var NONE3 = { kind: "none" };
|
|
|
20531
21471
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
20532
21472
|
var canonical2 = (file) => {
|
|
20533
21473
|
let rest = "";
|
|
20534
|
-
for (let dir =
|
|
21474
|
+
for (let dir = path26.resolve(file);; dir = path26.dirname(dir)) {
|
|
20535
21475
|
try {
|
|
20536
|
-
return
|
|
21476
|
+
return path26.join(realpathSync5(dir), rest);
|
|
20537
21477
|
} catch {
|
|
20538
|
-
if (
|
|
20539
|
-
return
|
|
20540
|
-
rest = rest ?
|
|
21478
|
+
if (path26.dirname(dir) === dir)
|
|
21479
|
+
return path26.resolve(file);
|
|
21480
|
+
rest = rest ? path26.join(path26.basename(dir), rest) : path26.basename(dir);
|
|
20541
21481
|
}
|
|
20542
21482
|
}
|
|
20543
21483
|
};
|
|
20544
21484
|
var exempt = (cwd, root, file, plans) => {
|
|
20545
21485
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
20546
|
-
const relative =
|
|
20547
|
-
if (relative.startsWith("..") ||
|
|
21486
|
+
const relative = path26.relative(fold(canonical2(root)), fold(canonical2(path26.resolve(cwd, file))));
|
|
21487
|
+
if (relative.startsWith("..") || path26.isAbsolute(relative))
|
|
20548
21488
|
return true;
|
|
20549
|
-
const posix = relative.split(
|
|
21489
|
+
const posix = relative.split(path26.sep).join("/");
|
|
20550
21490
|
const segments = posix.split("/");
|
|
20551
21491
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
20552
21492
|
};
|
|
20553
21493
|
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
20554
21494
|
var operands = (words) => words.filter((item) => !item.startsWith("-"));
|
|
21495
|
+
var SCRIPT_SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
21496
|
+
var wrappedScript = (input) => {
|
|
21497
|
+
let words = input;
|
|
21498
|
+
while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
|
|
21499
|
+
words = words.slice(1);
|
|
21500
|
+
const tail = workitCheckTail(words);
|
|
21501
|
+
if (!tail)
|
|
21502
|
+
return null;
|
|
21503
|
+
if ("script" in tail)
|
|
21504
|
+
return tail.script;
|
|
21505
|
+
const [shell, flag, script] = tail.argv;
|
|
21506
|
+
if (SCRIPT_SHELLS.has((shell.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && /^-[a-z]*c$/.test(flag ?? ""))
|
|
21507
|
+
return script ?? null;
|
|
21508
|
+
return tail.argv.map(quoteWord2).join(" ");
|
|
21509
|
+
};
|
|
20555
21510
|
var editsOf = (input) => {
|
|
20556
21511
|
let words = input;
|
|
20557
21512
|
while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
|
|
@@ -20628,10 +21583,18 @@ var editsOf = (input) => {
|
|
|
20628
21583
|
return [];
|
|
20629
21584
|
}
|
|
20630
21585
|
};
|
|
20631
|
-
var shellWrites = (command) => {
|
|
21586
|
+
var shellWrites = (command, depth = 0) => {
|
|
20632
21587
|
const targets = [];
|
|
20633
21588
|
let unknown = false;
|
|
20634
21589
|
for (const segment of segmentsOf(command)) {
|
|
21590
|
+
const inner = depth < 4 ? wrappedScript(segment.words) : null;
|
|
21591
|
+
if (inner !== null) {
|
|
21592
|
+
const nested = shellWrites(inner, depth + 1);
|
|
21593
|
+
if (nested.writes && nested.targets === null)
|
|
21594
|
+
unknown = true;
|
|
21595
|
+
else if (nested.targets)
|
|
21596
|
+
targets.push(...nested.targets);
|
|
21597
|
+
}
|
|
20635
21598
|
targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
|
|
20636
21599
|
const edits = editsOf(segment.words);
|
|
20637
21600
|
if (edits === null)
|
|
@@ -20644,7 +21607,7 @@ var shellWrites = (command) => {
|
|
|
20644
21607
|
return { writes: true, targets: unknown ? null : targets };
|
|
20645
21608
|
};
|
|
20646
21609
|
var cache2 = new Map;
|
|
20647
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
21610
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path26.resolve(root, file)) ?? "-").join("\x00");
|
|
20648
21611
|
var gateFor = (cwd) => {
|
|
20649
21612
|
const store = new TaskStore(cwd);
|
|
20650
21613
|
const listed = store.listTaskIndex();
|
|
@@ -20723,7 +21686,7 @@ var gitMutationDirs = (cwd, command, dialect) => {
|
|
|
20723
21686
|
return [];
|
|
20724
21687
|
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
20725
21688
|
return [];
|
|
20726
|
-
return [
|
|
21689
|
+
return [path26.resolve(cwd, invocation.dir ?? ".")];
|
|
20727
21690
|
});
|
|
20728
21691
|
};
|
|
20729
21692
|
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
@@ -20733,15 +21696,15 @@ var cdInto = (dir, words) => {
|
|
|
20733
21696
|
const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
20734
21697
|
if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
|
|
20735
21698
|
return;
|
|
20736
|
-
return target === undefined ?
|
|
21699
|
+
return target === undefined ? os10.homedir() : path26.resolve(dir, target);
|
|
20737
21700
|
};
|
|
20738
|
-
var
|
|
20739
|
-
var segmentTargets = (cwd, command, dialect) => {
|
|
21701
|
+
var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
|
|
21702
|
+
var segmentTargets = (cwd, command, dialect, depth = 0) => {
|
|
20740
21703
|
const targets = [];
|
|
20741
21704
|
let dir = cwd;
|
|
20742
21705
|
let stack = [];
|
|
20743
21706
|
const groups = [];
|
|
20744
|
-
const at = (file) =>
|
|
21707
|
+
const at = (file) => path26.isAbsolute(file) ? file : dir === undefined ? null : path26.resolve(dir, file);
|
|
20745
21708
|
for (const segment of segmentsOf(command, dialect)) {
|
|
20746
21709
|
if (segment.group === "open") {
|
|
20747
21710
|
groups.push({ dir, stack: [...stack] });
|
|
@@ -20769,6 +21732,9 @@ var segmentTargets = (cwd, command, dialect) => {
|
|
|
20769
21732
|
dir = stack.length ? stack.pop() : undefined;
|
|
20770
21733
|
continue;
|
|
20771
21734
|
}
|
|
21735
|
+
const inner = depth < 4 ? wrappedScript(words) : null;
|
|
21736
|
+
if (inner !== null && dir !== undefined)
|
|
21737
|
+
targets.push(...segmentTargets(dir, inner, "posix", depth + 1));
|
|
20772
21738
|
for (const file of segment.redirects)
|
|
20773
21739
|
if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
|
|
20774
21740
|
const resolved = at(file);
|
|
@@ -20778,15 +21744,15 @@ var segmentTargets = (cwd, command, dialect) => {
|
|
|
20778
21744
|
const edits = editsOf(segment.words);
|
|
20779
21745
|
if (edits === null) {
|
|
20780
21746
|
if (dir !== undefined)
|
|
20781
|
-
targets.push(
|
|
21747
|
+
targets.push(path26.join(dir, DIR_TARGET));
|
|
20782
21748
|
} else
|
|
20783
21749
|
for (const file of edits) {
|
|
20784
21750
|
const resolved = at(file);
|
|
20785
21751
|
if (resolved)
|
|
20786
21752
|
targets.push(resolved);
|
|
20787
21753
|
}
|
|
20788
|
-
if (dir !== undefined && workitInvocations(segment.words.map(
|
|
20789
|
-
targets.push(
|
|
21754
|
+
if (dir !== undefined && workitInvocations(segment.words.map(quoteWord2).join(" "), "posix").some((args) => args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), "")))
|
|
21755
|
+
targets.push(path26.join(dir, DIR_TARGET));
|
|
20790
21756
|
}
|
|
20791
21757
|
return targets;
|
|
20792
21758
|
};
|
|
@@ -20821,7 +21787,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
|
|
|
20821
21787
|
var guarded = (targets) => {
|
|
20822
21788
|
const seen = new Set;
|
|
20823
21789
|
return targets.filter((target) => {
|
|
20824
|
-
const dir =
|
|
21790
|
+
const dir = path26.dirname(target);
|
|
20825
21791
|
if (seen.has(dir))
|
|
20826
21792
|
return false;
|
|
20827
21793
|
seen.add(dir);
|
|
@@ -20842,7 +21808,7 @@ var claimAll = (owner, targets, deps) => {
|
|
|
20842
21808
|
for (const target of guarded(targets))
|
|
20843
21809
|
claimLiveSession(target, owner, deps);
|
|
20844
21810
|
};
|
|
20845
|
-
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) =>
|
|
21811
|
+
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path26.resolve(cwd, file)) : [path26.join(cwd, DIR_TARGET)];
|
|
20846
21812
|
function writeGate(cwd, paths, owner, deps) {
|
|
20847
21813
|
if (!owner)
|
|
20848
21814
|
return beforeWriteGate(cwd, paths);
|
|
@@ -20862,7 +21828,7 @@ function shellGate(cwd, command, owner, dialect = "posix", deps) {
|
|
|
20862
21828
|
try {
|
|
20863
21829
|
targets = [
|
|
20864
21830
|
...segmentTargets(cwd, command, dialect),
|
|
20865
|
-
...gitMutationDirs(cwd, command, dialect).map((dir) =>
|
|
21831
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path26.join(dir, DIR_TARGET))
|
|
20866
21832
|
];
|
|
20867
21833
|
} catch {
|
|
20868
21834
|
targets = [];
|
|
@@ -21296,12 +22262,12 @@ ${line}` };
|
|
|
21296
22262
|
};
|
|
21297
22263
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
21298
22264
|
import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
21299
|
-
import
|
|
21300
|
-
var
|
|
22265
|
+
import path27 from "node:path";
|
|
22266
|
+
var isRecord6 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
21301
22267
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
21302
22268
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
21303
22269
|
var existingDirectory = (value) => {
|
|
21304
|
-
if (!nonEmpty2(value) || !
|
|
22270
|
+
if (!nonEmpty2(value) || !path27.isAbsolute(value) || !existsSync7(value))
|
|
21305
22271
|
return null;
|
|
21306
22272
|
try {
|
|
21307
22273
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -21354,7 +22320,7 @@ var isWriteTool = (name) => typeof name === "string" && WRITE_TOOLS.has(name.toL
|
|
|
21354
22320
|
var PATH_KEYS = ["file_path", "filePath", "notebook_path", "path", "target_file", "file"];
|
|
21355
22321
|
var PATCH_HEADER = /^[ \t]*\*\*\* (?:(?:Add|Update|Delete) File|Move to): (.+)$/gm;
|
|
21356
22322
|
var writePaths = (input) => {
|
|
21357
|
-
if (!
|
|
22323
|
+
if (!isRecord6(input))
|
|
21358
22324
|
return [];
|
|
21359
22325
|
const paths = PATH_KEYS.map((key) => input[key]).filter(nonEmpty2);
|
|
21360
22326
|
for (const value of Object.values(input))
|
|
@@ -21449,10 +22415,32 @@ function handleHook(input, deps) {
|
|
|
21449
22415
|
const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE5;
|
|
21450
22416
|
if (gate.kind === "deny")
|
|
21451
22417
|
return gate;
|
|
22418
|
+
const plan = heavyGate(input.cwd, event.command, {
|
|
22419
|
+
host: input.host,
|
|
22420
|
+
dialect: event.dialect,
|
|
22421
|
+
permissionMode: input.permissionMode
|
|
22422
|
+
});
|
|
22423
|
+
if (plan.kind === "split" && canDeny)
|
|
22424
|
+
return splitDenial(plan);
|
|
22425
|
+
const rewrites = usable2(descriptor.shellPolicy.rewrite);
|
|
22426
|
+
if (plan.kind === "rewrite" && !rewrites && canDeny)
|
|
22427
|
+
return heavyDenial(event.command, plan);
|
|
22428
|
+
const lease = plan.kind === "rewrite" && rewrites ? plan : null;
|
|
22429
|
+
const aside = plan.kind === "passthrough" ? plan.note : null;
|
|
21452
22430
|
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
22431
|
+
const rewritten = (text) => lease ? {
|
|
22432
|
+
kind: "rewrite",
|
|
22433
|
+
command: lease.command,
|
|
22434
|
+
input: event.toolInput ? { ...event.toolInput, command: lease.command } : null,
|
|
22435
|
+
text
|
|
22436
|
+
} : NONE5;
|
|
21453
22437
|
if (descriptor.transport !== "hook-process")
|
|
21454
|
-
return
|
|
21455
|
-
|
|
22438
|
+
return rewritten(null);
|
|
22439
|
+
const nudged = withContextLine(withContextLine(canDeny ? rawGitPre(input, event.command) : NONE5, aside), shipNudge(input, event.command));
|
|
22440
|
+
if (!lease)
|
|
22441
|
+
return nudged;
|
|
22442
|
+
return rewritten([rewriteNote(lease), nudged.kind === "context" ? nudged.text : null].filter(Boolean).join(`
|
|
22443
|
+
`));
|
|
21456
22444
|
}
|
|
21457
22445
|
case "shell.post":
|
|
21458
22446
|
if (usable2(descriptor.events["shell.post"].support))
|
|
@@ -21530,7 +22518,12 @@ var CLAUDE_CODE_DESCRIPTOR = {
|
|
|
21530
22518
|
"compact.pre": { support: "none", native: null },
|
|
21531
22519
|
stop: { support: "native", native: "Stop" }
|
|
21532
22520
|
},
|
|
21533
|
-
shellPolicy: {
|
|
22521
|
+
shellPolicy: {
|
|
22522
|
+
deny: "native",
|
|
22523
|
+
channel: "permissionDecision",
|
|
22524
|
+
failClosed: false,
|
|
22525
|
+
rewrite: "native"
|
|
22526
|
+
},
|
|
21534
22527
|
context: {
|
|
21535
22528
|
sessionStart: "native",
|
|
21536
22529
|
perTurn: "native",
|
|
@@ -21612,7 +22605,7 @@ var EVENTS = {
|
|
|
21612
22605
|
var SOURCES = new Set(["startup", "resume", "clear", "compact", "fork"]);
|
|
21613
22606
|
var SHELL_TOOLS = new Set(["Bash", "PowerShell"]);
|
|
21614
22607
|
var dialectOf2 = (value) => value.tool_name === "PowerShell" ? "powershell" : "posix";
|
|
21615
|
-
var toolCommand = (value) =>
|
|
22608
|
+
var toolCommand = (value) => isRecord6(value.tool_input) ? commandText(value.tool_input.command) : null;
|
|
21616
22609
|
var eventOf = (name, value) => {
|
|
21617
22610
|
const toolUseId = optionalText(value.tool_use_id);
|
|
21618
22611
|
switch (name) {
|
|
@@ -21640,19 +22633,28 @@ var eventOf = (name, value) => {
|
|
|
21640
22633
|
kind: "tool.pre",
|
|
21641
22634
|
tool: value.tool_name,
|
|
21642
22635
|
toolUseId,
|
|
21643
|
-
skill:
|
|
22636
|
+
skill: isRecord6(value.tool_input) ? optionalText(value.tool_input.skill) : null
|
|
21644
22637
|
}
|
|
21645
22638
|
};
|
|
21646
22639
|
if (!SHELL_TOOLS.has(value.tool_name))
|
|
21647
22640
|
return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
|
|
21648
22641
|
const command = toolCommand(value);
|
|
21649
|
-
return command ? {
|
|
22642
|
+
return command ? {
|
|
22643
|
+
ok: true,
|
|
22644
|
+
event: {
|
|
22645
|
+
kind: "shell.pre",
|
|
22646
|
+
command,
|
|
22647
|
+
toolUseId,
|
|
22648
|
+
dialect: dialectOf2(value),
|
|
22649
|
+
toolInput: value.tool_input
|
|
22650
|
+
}
|
|
22651
|
+
} : { ok: false, error: `tool_input.command is required for ${value.tool_name}` };
|
|
21650
22652
|
}
|
|
21651
22653
|
case "PostToolUse": {
|
|
21652
22654
|
const command = toolCommand(value);
|
|
21653
22655
|
if (!SHELL_TOOLS.has(String(value.tool_name)) || !command)
|
|
21654
22656
|
return { ok: false, error: "only shell PostToolUse is mapped" };
|
|
21655
|
-
const response =
|
|
22657
|
+
const response = isRecord6(value.tool_response) ? value.tool_response : {};
|
|
21656
22658
|
return {
|
|
21657
22659
|
ok: true,
|
|
21658
22660
|
event: {
|
|
@@ -21710,6 +22712,17 @@ var CONTEXT_EVENTS = new Set([
|
|
|
21710
22712
|
"PostToolUse"
|
|
21711
22713
|
]);
|
|
21712
22714
|
var render = (decision, native) => {
|
|
22715
|
+
if (decision.kind === "rewrite" && native === "PreToolUse" && decision.input)
|
|
22716
|
+
return {
|
|
22717
|
+
json: {
|
|
22718
|
+
hookSpecificOutput: {
|
|
22719
|
+
hookEventName: "PreToolUse",
|
|
22720
|
+
updatedInput: decision.input,
|
|
22721
|
+
...decision.text ? { additionalContext: decision.text } : {}
|
|
22722
|
+
}
|
|
22723
|
+
},
|
|
22724
|
+
exitCode: 0
|
|
22725
|
+
};
|
|
21713
22726
|
if (decision.kind === "deny" && native === "PreToolUse")
|
|
21714
22727
|
return {
|
|
21715
22728
|
json: {
|
|
@@ -21735,14 +22748,14 @@ var render = (decision, native) => {
|
|
|
21735
22748
|
var claudeCodeAdapter = {
|
|
21736
22749
|
descriptor: CLAUDE_CODE_DESCRIPTOR,
|
|
21737
22750
|
parse(raw) {
|
|
21738
|
-
const native =
|
|
22751
|
+
const native = isRecord6(raw) && Object.hasOwn(EVENTS, String(raw.hook_event_name)) ? raw.hook_event_name : null;
|
|
21739
22752
|
const fail = (error) => ({
|
|
21740
22753
|
ok: false,
|
|
21741
22754
|
error,
|
|
21742
22755
|
native,
|
|
21743
22756
|
event: native ? EVENTS[native] : null
|
|
21744
22757
|
});
|
|
21745
|
-
if (!
|
|
22758
|
+
if (!isRecord6(raw) || !native)
|
|
21746
22759
|
return fail("hook_event_name is required");
|
|
21747
22760
|
if (!nonEmpty2(raw.session_id))
|
|
21748
22761
|
return fail("session_id is required");
|
|
@@ -21796,7 +22809,12 @@ var CODEX_DESCRIPTOR = {
|
|
|
21796
22809
|
"compact.pre": { support: "none", native: null },
|
|
21797
22810
|
stop: { support: "native", native: "Stop" }
|
|
21798
22811
|
},
|
|
21799
|
-
shellPolicy: {
|
|
22812
|
+
shellPolicy: {
|
|
22813
|
+
deny: "native",
|
|
22814
|
+
channel: "permissionDecision",
|
|
22815
|
+
failClosed: false,
|
|
22816
|
+
rewrite: "none"
|
|
22817
|
+
},
|
|
21800
22818
|
context: {
|
|
21801
22819
|
sessionStart: "native",
|
|
21802
22820
|
perTurn: "native",
|
|
@@ -21888,7 +22906,7 @@ var SHELL_TOOLS2 = new Set(["bash", "unified-exec"]);
|
|
|
21888
22906
|
var isShellTool = (name) => SHELL_TOOLS2.has(String(name).toLowerCase());
|
|
21889
22907
|
var SOURCES2 = new Set(["startup", "resume", "clear", "compact", "fork"]);
|
|
21890
22908
|
var parseCodexHookInput = (value) => {
|
|
21891
|
-
if (!
|
|
22909
|
+
if (!isRecord6(value) || !Object.hasOwn(EVENTS2, String(value.hook_event_name)))
|
|
21892
22910
|
return { ok: false, error: "hook_event_name is required" };
|
|
21893
22911
|
const event = value.hook_event_name;
|
|
21894
22912
|
const cwd = existingDirectory(value.cwd);
|
|
@@ -21897,7 +22915,7 @@ var parseCodexHookInput = (value) => {
|
|
|
21897
22915
|
const toolEvent = event === "PreToolUse" || event === "PostToolUse";
|
|
21898
22916
|
if (toolEvent && !nonEmpty2(value.tool_name))
|
|
21899
22917
|
return { ok: false, error: "tool_name is required" };
|
|
21900
|
-
const command =
|
|
22918
|
+
const command = isRecord6(value.tool_input) ? commandText(value.tool_input.command) : null;
|
|
21901
22919
|
if (toolEvent && isShellTool(value.tool_name) && !command)
|
|
21902
22920
|
return { ok: false, error: "tool_input.command is required for shell tools" };
|
|
21903
22921
|
if (event === "SubagentStart" && !nonEmpty2(value.agent_id))
|
|
@@ -22011,7 +23029,7 @@ var codexAdapter = {
|
|
|
22011
23029
|
const host = detectCodexSurface(env);
|
|
22012
23030
|
const parsed = parseCodexHookInput(raw);
|
|
22013
23031
|
if (!parsed.ok) {
|
|
22014
|
-
const native =
|
|
23032
|
+
const native = isRecord6(raw) && Object.hasOwn(EVENTS2, String(raw.hook_event_name)) ? raw.hook_event_name : null;
|
|
22015
23033
|
return { ok: false, error: parsed.error, native, event: native ? EVENTS2[native] : null };
|
|
22016
23034
|
}
|
|
22017
23035
|
const input = parsed.data;
|
|
@@ -22038,8 +23056,8 @@ var codexAdapter = {
|
|
|
22038
23056
|
};
|
|
22039
23057
|
|
|
22040
23058
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
22041
|
-
import
|
|
22042
|
-
import
|
|
23059
|
+
import fs21 from "node:fs";
|
|
23060
|
+
import path28 from "node:path";
|
|
22043
23061
|
var undocumented = { support: "undocumented", native: null };
|
|
22044
23062
|
var none = { support: "none", native: null };
|
|
22045
23063
|
var CURSOR_DESCRIPTOR = {
|
|
@@ -22061,7 +23079,7 @@ var CURSOR_DESCRIPTOR = {
|
|
|
22061
23079
|
"compact.pre": { support: "partial", native: "preCompact" },
|
|
22062
23080
|
stop: { support: "native", native: "stop" }
|
|
22063
23081
|
},
|
|
22064
|
-
shellPolicy: { deny: "native", channel: "exit2+json", failClosed: false },
|
|
23082
|
+
shellPolicy: { deny: "native", channel: "exit2+json", failClosed: false, rewrite: "none" },
|
|
22065
23083
|
context: {
|
|
22066
23084
|
sessionStart: "native",
|
|
22067
23085
|
perTurn: "none",
|
|
@@ -22154,7 +23172,7 @@ var EVENTS3 = {
|
|
|
22154
23172
|
stop: "stop"
|
|
22155
23173
|
};
|
|
22156
23174
|
var parseCursorHookInput = (value) => {
|
|
22157
|
-
if (!
|
|
23175
|
+
if (!isRecord6(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
|
|
22158
23176
|
return { ok: false, error: "hook_event_name is required" };
|
|
22159
23177
|
const roots = value.workspace_roots;
|
|
22160
23178
|
if (!Array.isArray(roots) || roots.length === 0 || !roots.every(nonEmpty2))
|
|
@@ -22236,17 +23254,17 @@ var protocolEvent2 = (input) => {
|
|
|
22236
23254
|
};
|
|
22237
23255
|
var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
|
|
22238
23256
|
var lastAssistantText = (file) => {
|
|
22239
|
-
if (!file || !
|
|
23257
|
+
if (!file || !path28.isAbsolute(file))
|
|
22240
23258
|
return null;
|
|
22241
23259
|
try {
|
|
22242
|
-
const size =
|
|
23260
|
+
const size = fs21.statSync(file).size;
|
|
22243
23261
|
const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
|
|
22244
23262
|
const buffer = Buffer.alloc(size - start);
|
|
22245
|
-
const fd =
|
|
23263
|
+
const fd = fs21.openSync(file, "r");
|
|
22246
23264
|
try {
|
|
22247
|
-
|
|
23265
|
+
fs21.readSync(fd, buffer, 0, buffer.length, start);
|
|
22248
23266
|
} finally {
|
|
22249
|
-
|
|
23267
|
+
fs21.closeSync(fd);
|
|
22250
23268
|
}
|
|
22251
23269
|
const lines = buffer.toString("utf8").split(`
|
|
22252
23270
|
`);
|
|
@@ -22257,14 +23275,14 @@ var lastAssistantText = (file) => {
|
|
|
22257
23275
|
} catch {
|
|
22258
23276
|
continue;
|
|
22259
23277
|
}
|
|
22260
|
-
if (!
|
|
23278
|
+
if (!isRecord6(entry) || entry.role !== "assistant")
|
|
22261
23279
|
continue;
|
|
22262
|
-
const content =
|
|
23280
|
+
const content = isRecord6(entry.message) ? entry.message.content : entry.content;
|
|
22263
23281
|
if (typeof content === "string")
|
|
22264
23282
|
return content;
|
|
22265
23283
|
if (!Array.isArray(content))
|
|
22266
23284
|
return null;
|
|
22267
|
-
const text = content.filter((part) =>
|
|
23285
|
+
const text = content.filter((part) => isRecord6(part) && part.type === "text" && typeof part.text === "string").map((part) => part.text).join(`
|
|
22268
23286
|
`);
|
|
22269
23287
|
return text || null;
|
|
22270
23288
|
}
|
|
@@ -22273,7 +23291,7 @@ var lastAssistantText = (file) => {
|
|
|
22273
23291
|
return null;
|
|
22274
23292
|
}
|
|
22275
23293
|
};
|
|
22276
|
-
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
23294
|
+
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path28.sep) ? root : `${root}${path28.sep}`);
|
|
22277
23295
|
var hookCwd = (input) => {
|
|
22278
23296
|
const cwd = existingDirectory(input.cwd);
|
|
22279
23297
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
|
@@ -22314,7 +23332,7 @@ var cursorAdapter = {
|
|
|
22314
23332
|
parse(raw) {
|
|
22315
23333
|
const parsed = parseCursorHookInput(raw);
|
|
22316
23334
|
if (!parsed.ok) {
|
|
22317
|
-
const native =
|
|
23335
|
+
const native = isRecord6(raw) && Object.hasOwn(EVENTS3, String(raw.hook_event_name)) ? raw.hook_event_name : null;
|
|
22318
23336
|
return { ok: false, error: parsed.error, native, event: native ? EVENTS3[native] : null };
|
|
22319
23337
|
}
|
|
22320
23338
|
const input = parsed.data;
|