@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
|
@@ -9711,6 +9711,7 @@ var init_workspaces = __esm(() => {
|
|
|
9711
9711
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
9712
9712
|
defaultEndpoint: unknown().optional(),
|
|
9713
9713
|
verification: _enum(["self", "independent"]).optional(),
|
|
9714
|
+
delegation: unknown().optional(),
|
|
9714
9715
|
trailers: boolean2().optional(),
|
|
9715
9716
|
liveSessionGuard: boolean2().optional(),
|
|
9716
9717
|
fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
@@ -15032,6 +15033,16 @@ var init_workers = __esm(() => {
|
|
|
15032
15033
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
15033
15034
|
import fs12 from "node:fs";
|
|
15034
15035
|
import path16 from "node:path";
|
|
15036
|
+
function repoHeavyCommands(root) {
|
|
15037
|
+
try {
|
|
15038
|
+
const value = JSON.parse(fs12.readFileSync(checksConfigFile(root), "utf8"));
|
|
15039
|
+
const resources = isRecord3(value) ? value.resources : undefined;
|
|
15040
|
+
const list = isRecord3(resources) ? resources.heavyCommands : undefined;
|
|
15041
|
+
return Array.isArray(list) ? list.filter((item) => typeof item === "string") : [];
|
|
15042
|
+
} catch {
|
|
15043
|
+
return [];
|
|
15044
|
+
}
|
|
15045
|
+
}
|
|
15035
15046
|
function splitCommand(command) {
|
|
15036
15047
|
const argv = [];
|
|
15037
15048
|
let current = "";
|
|
@@ -15112,7 +15123,7 @@ function detectedChecks(root) {
|
|
|
15112
15123
|
}
|
|
15113
15124
|
function loadCheckConfig(cwd) {
|
|
15114
15125
|
const root = checkRoot(cwd);
|
|
15115
|
-
const file =
|
|
15126
|
+
const file = checksConfigFile(root);
|
|
15116
15127
|
const none = (error = null) => ({
|
|
15117
15128
|
source: error ? CHECKS_FILE : "none",
|
|
15118
15129
|
root,
|
|
@@ -15196,7 +15207,7 @@ function workitAvailable(env = process.env) {
|
|
|
15196
15207
|
function checkCommand(args, version, onPath = workitAvailable()) {
|
|
15197
15208
|
return onPath ? `workit check ${args}` : `npx -y @brainervirus/workit-cli@${version} check ${args}`;
|
|
15198
15209
|
}
|
|
15199
|
-
var SCRIPT_CHECKS, CHECKS_FILE = "workit.checks.json", isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SHELL_META, git4 = (cwd, args) => {
|
|
15210
|
+
var SCRIPT_CHECKS, CHECKS_FILE = "workit.checks.json", checksConfigFile = (root) => path16.join(root, CHECKS_FILE), isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SHELL_META, git4 = (cwd, args) => {
|
|
15200
15211
|
const run = spawnSync9("git", args, {
|
|
15201
15212
|
cwd,
|
|
15202
15213
|
encoding: "utf8",
|
|
@@ -15648,6 +15659,42 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
15648
15659
|
requirementIds: evaluations.map((item) => item.requirementId)
|
|
15649
15660
|
});
|
|
15650
15661
|
}
|
|
15662
|
+
function boundTaskSummary(summary) {
|
|
15663
|
+
const rules = new Map((summary.policy?.requirements ?? []).map((item) => [item.id, item]));
|
|
15664
|
+
const unmet = summary.requirements.filter((item) => item.status === "unsatisfied" || item.status === "unavailable");
|
|
15665
|
+
const { progress, closure } = summary;
|
|
15666
|
+
const next = progress.nextAction;
|
|
15667
|
+
const open = unmet.slice(0, BRIEF_OPEN).map((item) => ({
|
|
15668
|
+
dimension: rules.get(item.requirementId)?.dimension ?? "verification",
|
|
15669
|
+
before: rules.get(item.requirementId)?.before ?? "close",
|
|
15670
|
+
status: item.status
|
|
15671
|
+
}));
|
|
15672
|
+
const brief = {
|
|
15673
|
+
id: summary.id,
|
|
15674
|
+
revision: summary.revision,
|
|
15675
|
+
workspaceRevision: summary.workspaceRevision,
|
|
15676
|
+
updatedAt: summary.updatedAt,
|
|
15677
|
+
objective: clip(summary.objective),
|
|
15678
|
+
status: summary.status,
|
|
15679
|
+
closure: closure && {
|
|
15680
|
+
outcome: closure.outcome,
|
|
15681
|
+
at: closure.at,
|
|
15682
|
+
summary: clip(closure.summary)
|
|
15683
|
+
},
|
|
15684
|
+
progress: {
|
|
15685
|
+
summary: clip(progress.summary),
|
|
15686
|
+
nextAction: next && clip(next),
|
|
15687
|
+
blockers: progress.blockers.slice(0, BRIEF_BLOCKERS).map((item) => ({
|
|
15688
|
+
reason: clip(item.reason),
|
|
15689
|
+
dependentAction: clip(item.dependentAction)
|
|
15690
|
+
}))
|
|
15691
|
+
},
|
|
15692
|
+
requirements: { total: summary.requirements.length, unmet: unmet.length, open },
|
|
15693
|
+
truncated: false
|
|
15694
|
+
};
|
|
15695
|
+
brief.truncated = JSON.stringify(brief).includes("…") || progress.blockers.length > BRIEF_BLOCKERS || unmet.length > BRIEF_OPEN;
|
|
15696
|
+
return brief;
|
|
15697
|
+
}
|
|
15651
15698
|
var digestBytes = (value) => createHash10("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path17.sep}`), relative = (root, candidate) => path17.relative(root, candidate).split(path17.sep).join("/") || ".", canonicalPath = (value) => {
|
|
15652
15699
|
if (typeof value !== "string" || !value)
|
|
15653
15700
|
return null;
|
|
@@ -15867,7 +15914,15 @@ var digestBytes = (value) => createHash10("sha256").update(value).digest("hex"),
|
|
|
15867
15914
|
if (dimension === "decisions")
|
|
15868
15915
|
return "decision";
|
|
15869
15916
|
return "worker report";
|
|
15870
|
-
}, observedGate = (requirement) => (requirement.dimension === "testing" || requirement.dimension === "verification") && requirement.before === "close"
|
|
15917
|
+
}, observedGate = (requirement) => (requirement.dimension === "testing" || requirement.dimension === "verification") && requirement.before === "close", BRIEF_TEXT = 250, BRIEF_BLOCKERS = 3, BRIEF_OPEN = 8, clip = (value) => {
|
|
15918
|
+
const fits = (text) => Buffer.byteLength(JSON.stringify(text)) <= BRIEF_TEXT;
|
|
15919
|
+
if (fits(value))
|
|
15920
|
+
return value;
|
|
15921
|
+
let length = Math.min(value.length, BRIEF_TEXT);
|
|
15922
|
+
while (length > 0 && !fits(`${value.slice(0, length)}…`))
|
|
15923
|
+
length -= 1;
|
|
15924
|
+
return `${value.slice(0, length)}…`;
|
|
15925
|
+
};
|
|
15871
15926
|
var init_task_evaluation = __esm(() => {
|
|
15872
15927
|
init_task_contract();
|
|
15873
15928
|
init_check_config();
|
|
@@ -16294,6 +16349,15 @@ class WorkitCore {
|
|
|
16294
16349
|
if (!parsed.ok)
|
|
16295
16350
|
return parsed;
|
|
16296
16351
|
const input = parsed.data;
|
|
16352
|
+
if (this.context.readOnlyChild) {
|
|
16353
|
+
if (input.action !== "inspect")
|
|
16354
|
+
return failure2("permission_denied", "child sessions can only inspect the task summary");
|
|
16355
|
+
const task = this.store.readTask(input.taskId);
|
|
16356
|
+
if (!task.ok)
|
|
16357
|
+
return task;
|
|
16358
|
+
const summary = this.summary(task.data);
|
|
16359
|
+
return summary.ok ? { ...summary, data: boundTaskSummary(summary.data) } : summary;
|
|
16360
|
+
}
|
|
16297
16361
|
if ((this.context.workerId ?? null) !== null) {
|
|
16298
16362
|
if (input.action !== "inspect" && input.action !== "list")
|
|
16299
16363
|
return failure2("permission_denied", "helpers cannot control task lifecycle or scope");
|
|
@@ -18696,7 +18760,16 @@ function rawInvocations(command, dialect = "posix", depth = 0) {
|
|
|
18696
18760
|
RECENT.delete(RECENT.keys().next().value);
|
|
18697
18761
|
return found;
|
|
18698
18762
|
}
|
|
18699
|
-
function
|
|
18763
|
+
function commandLines(command, dialect = "posix") {
|
|
18764
|
+
const lines = [];
|
|
18765
|
+
try {
|
|
18766
|
+
walk2(command, dialect, 0, lines);
|
|
18767
|
+
} catch (error) {
|
|
18768
|
+
throw error instanceof ShellAnalysisError ? error : new ShellAnalysisError(error instanceof Error ? error.message : String(error));
|
|
18769
|
+
}
|
|
18770
|
+
return lines;
|
|
18771
|
+
}
|
|
18772
|
+
function walk2(command, dialect, depth, seen) {
|
|
18700
18773
|
const found = [];
|
|
18701
18774
|
let dir = null;
|
|
18702
18775
|
const groups = [];
|
|
@@ -18707,7 +18780,8 @@ function walk2(command, dialect, depth) {
|
|
|
18707
18780
|
const nested = (script, inner) => {
|
|
18708
18781
|
if (!script || depth >= MAX_DEPTH)
|
|
18709
18782
|
return;
|
|
18710
|
-
|
|
18783
|
+
const inside = seen ? walk2(script, inner, depth + 1, seen) : rawInvocations(script, inner, depth + 1);
|
|
18784
|
+
for (const invocation of inside)
|
|
18711
18785
|
found.push({ ...invocation, dir: rebase(dir, invocation.dir) });
|
|
18712
18786
|
};
|
|
18713
18787
|
const visit = (input, stdin, hops) => {
|
|
@@ -18719,11 +18793,17 @@ function walk2(command, dialect, depth) {
|
|
|
18719
18793
|
const name = commandName(words[0]);
|
|
18720
18794
|
if (!name)
|
|
18721
18795
|
return;
|
|
18796
|
+
seen?.push(words);
|
|
18797
|
+
if (name === "-c" || name === "--command")
|
|
18798
|
+
return nested(words[1], "posix");
|
|
18722
18799
|
const runs = (lines) => {
|
|
18723
18800
|
if (hops < MAX_DEPTH)
|
|
18724
18801
|
for (const next of lines)
|
|
18725
18802
|
visit(next, undefined, hops + 1);
|
|
18726
18803
|
};
|
|
18804
|
+
const wrapped = seen ? null : workitCheckTail(words);
|
|
18805
|
+
if (wrapped)
|
|
18806
|
+
return "script" in wrapped ? nested(wrapped.script, "posix") : runs([wrapped.argv]);
|
|
18727
18807
|
if (CD.has(name)) {
|
|
18728
18808
|
const target = cdTarget(words);
|
|
18729
18809
|
dir = target === null ? os5.homedir() : joinDir(dir, target);
|
|
@@ -18800,6 +18880,49 @@ function walk2(command, dialect, depth) {
|
|
|
18800
18880
|
}
|
|
18801
18881
|
return found;
|
|
18802
18882
|
}
|
|
18883
|
+
function workitCheckTail(words) {
|
|
18884
|
+
let at = 0;
|
|
18885
|
+
if (PACKAGE_RUNNERS.has(commandName(words[0]))) {
|
|
18886
|
+
at = words.findIndex((word, index) => index > 0 && !word.startsWith("-"));
|
|
18887
|
+
if (at < 0 || !WORKIT_PACKAGE.test(words[at]))
|
|
18888
|
+
return null;
|
|
18889
|
+
} else if (!/^workit(?:\.exe|\.cmd)?$/.test(commandName(words[0])))
|
|
18890
|
+
return null;
|
|
18891
|
+
const args = words.slice(at + 1);
|
|
18892
|
+
if (args[0] !== "check" && !(args[0] === "lock" && args[1] === "run"))
|
|
18893
|
+
return null;
|
|
18894
|
+
const split = args.indexOf("--");
|
|
18895
|
+
const tail = split < 0 ? [] : args.slice(split + 1);
|
|
18896
|
+
if (!tail.length)
|
|
18897
|
+
return null;
|
|
18898
|
+
return args.slice(0, split).includes("--shell") ? { script: tail.join(" ") } : { argv: tail };
|
|
18899
|
+
}
|
|
18900
|
+
function simpleCommand(command, dialect = "posix", depth = 0) {
|
|
18901
|
+
let segments;
|
|
18902
|
+
try {
|
|
18903
|
+
segments = segmentsOf(command, dialect);
|
|
18904
|
+
} catch {
|
|
18905
|
+
return null;
|
|
18906
|
+
}
|
|
18907
|
+
if (segments.length !== 1)
|
|
18908
|
+
return null;
|
|
18909
|
+
const [segment] = segments;
|
|
18910
|
+
if (segment.group || segment.pipe || segment.unanalyzable || segment.redirects.length || segment.subs?.length || segment.stdin?.length)
|
|
18911
|
+
return null;
|
|
18912
|
+
const { words } = stripPrefixes(segment.words);
|
|
18913
|
+
const name = commandName(words[0]);
|
|
18914
|
+
if (!name || depth >= MAX_DEPTH)
|
|
18915
|
+
return null;
|
|
18916
|
+
if (SHELLS.has(name)) {
|
|
18917
|
+
const script = shellScript(words, undefined);
|
|
18918
|
+
return script ? simpleCommand(script, "posix", depth + 1) : null;
|
|
18919
|
+
}
|
|
18920
|
+
if (name === "-c" || name === "--command")
|
|
18921
|
+
return words[1] ? simpleCommand(words[1], "posix", depth + 1) : null;
|
|
18922
|
+
if (EVALS.has(name) || POWERSHELLS.has(name) || ["xargs", "find", "parallel", "script", "cmd", "source", "."].includes(name))
|
|
18923
|
+
return null;
|
|
18924
|
+
return words;
|
|
18925
|
+
}
|
|
18803
18926
|
function workitInvocations(command, dialect = "posix", depth = 0) {
|
|
18804
18927
|
const found = [];
|
|
18805
18928
|
for (const { words } of segmentsOf(command, dialect)) {
|
|
@@ -19216,7 +19339,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
|
|
|
19216
19339
|
...Object.keys(env).length ? { env } : {},
|
|
19217
19340
|
...stdin === undefined ? {} : { stdin }
|
|
19218
19341
|
};
|
|
19219
|
-
}, RECENT, LAUNCHERS, WORKIT_BIN, PUSH_VALUE_OPTIONS, CURRENT_BRANCH, pushDestination = (destination) => {
|
|
19342
|
+
}, RECENT, LAUNCHERS, WORKIT_BIN, WORKIT_PACKAGE, PACKAGE_RUNNERS, PUSH_VALUE_OPTIONS, CURRENT_BRANCH, pushDestination = (destination) => {
|
|
19220
19343
|
if (destination === "HEAD" || destination === "@" || CURRENT_BRANCH.test(destination))
|
|
19221
19344
|
return { name: null };
|
|
19222
19345
|
if (destination.startsWith("refs/heads/"))
|
|
@@ -19908,6 +20031,8 @@ var init_raw_git = __esm(() => {
|
|
|
19908
20031
|
"command"
|
|
19909
20032
|
]);
|
|
19910
20033
|
WORKIT_BIN = /(?:^|[\\/])workit(?:\.exe)?$|^@brainervirus\/workit-cli$/;
|
|
20034
|
+
WORKIT_PACKAGE = /^@brainervirus\/workit-cli(?:@\S*)?$/;
|
|
20035
|
+
PACKAGE_RUNNERS = new Set(["npx", "bunx", "pnpx"]);
|
|
19911
20036
|
PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
|
|
19912
20037
|
CURRENT_BRANCH = /^\$\(\s*git\s+(?:branch\s+--show-current|rev-parse\s+--abbrev-ref\s+HEAD|symbolic-ref\s+--short\s+HEAD)\s*\)$/;
|
|
19913
20038
|
VALUE_OPTIONS = {
|
|
@@ -20147,12 +20272,808 @@ var init_policy = __esm(() => {
|
|
|
20147
20272
|
init_shell_words();
|
|
20148
20273
|
});
|
|
20149
20274
|
|
|
20150
|
-
// packages/workit-core/src/core/
|
|
20151
|
-
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
20152
|
-
import { createHash as createHash15 } from "node:crypto";
|
|
20275
|
+
// packages/workit-core/src/core/heavy-rules.ts
|
|
20153
20276
|
import fs17 from "node:fs";
|
|
20154
20277
|
import os6 from "node:os";
|
|
20155
20278
|
import path22 from "node:path";
|
|
20279
|
+
function parseJsonc(text) {
|
|
20280
|
+
let out = "";
|
|
20281
|
+
for (let index = 0;index < text.length; index += 1) {
|
|
20282
|
+
const char = text[index];
|
|
20283
|
+
if (char === '"') {
|
|
20284
|
+
const end = /"(?:[^"\\]|\\.)*"/y;
|
|
20285
|
+
end.lastIndex = index;
|
|
20286
|
+
const match = end.exec(text);
|
|
20287
|
+
if (!match)
|
|
20288
|
+
throw new Error("unterminated string");
|
|
20289
|
+
out += match[0];
|
|
20290
|
+
index += match[0].length - 1;
|
|
20291
|
+
} else if (char === "/" && text[index + 1] === "/") {
|
|
20292
|
+
while (index < text.length && text[index] !== `
|
|
20293
|
+
`)
|
|
20294
|
+
index += 1;
|
|
20295
|
+
out += `
|
|
20296
|
+
`;
|
|
20297
|
+
} else if (char === "/" && text[index + 1] === "*") {
|
|
20298
|
+
const close = text.indexOf("*/", index + 2);
|
|
20299
|
+
if (close < 0)
|
|
20300
|
+
throw new Error("unterminated comment");
|
|
20301
|
+
index = close + 1;
|
|
20302
|
+
} else
|
|
20303
|
+
out += char;
|
|
20304
|
+
}
|
|
20305
|
+
return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
|
|
20306
|
+
}
|
|
20307
|
+
function gitTop(cwd) {
|
|
20308
|
+
for (let dir = path22.resolve(cwd);; dir = path22.dirname(dir)) {
|
|
20309
|
+
if (fs17.existsSync(path22.join(dir, ".git")))
|
|
20310
|
+
return dir;
|
|
20311
|
+
if (path22.dirname(dir) === dir)
|
|
20312
|
+
return null;
|
|
20313
|
+
}
|
|
20314
|
+
}
|
|
20315
|
+
function claudeSettingsFiles(cwd, env, platform = process.platform) {
|
|
20316
|
+
const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
|
|
20317
|
+
const user = env.CLAUDE_CONFIG_DIR || path22.join(homeOf(env), ".claude");
|
|
20318
|
+
return [
|
|
20319
|
+
...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
|
|
20320
|
+
path22.join(user, "settings.json"),
|
|
20321
|
+
path22.join(project, ".claude", "settings.json"),
|
|
20322
|
+
path22.join(project, ".claude", "settings.local.json")
|
|
20323
|
+
];
|
|
20324
|
+
}
|
|
20325
|
+
function claudeRules(cwd, env = process.env) {
|
|
20326
|
+
const rules = EMPTY();
|
|
20327
|
+
try {
|
|
20328
|
+
for (const file of claudeSettingsFiles(cwd, env)) {
|
|
20329
|
+
const text = readText2(file);
|
|
20330
|
+
if (text === null)
|
|
20331
|
+
continue;
|
|
20332
|
+
const value = JSON.parse(text);
|
|
20333
|
+
if (!isRecord4(value))
|
|
20334
|
+
return "unreadable";
|
|
20335
|
+
const permissions = value.permissions;
|
|
20336
|
+
if (permissions === undefined)
|
|
20337
|
+
continue;
|
|
20338
|
+
if (!isRecord4(permissions))
|
|
20339
|
+
return "unreadable";
|
|
20340
|
+
for (const [key, bucket] of [
|
|
20341
|
+
["deny", rules.deny],
|
|
20342
|
+
["ask", rules.deny],
|
|
20343
|
+
["allow", rules.allow]
|
|
20344
|
+
]) {
|
|
20345
|
+
const list = permissions[key];
|
|
20346
|
+
if (list === undefined)
|
|
20347
|
+
continue;
|
|
20348
|
+
if (!Array.isArray(list))
|
|
20349
|
+
return "unreadable";
|
|
20350
|
+
for (const entry of list) {
|
|
20351
|
+
if (typeof entry !== "string")
|
|
20352
|
+
return "unreadable";
|
|
20353
|
+
const pattern = bashPattern(entry);
|
|
20354
|
+
if (pattern !== null)
|
|
20355
|
+
bucket.push(pattern);
|
|
20356
|
+
}
|
|
20357
|
+
}
|
|
20358
|
+
}
|
|
20359
|
+
} catch {
|
|
20360
|
+
return "unreadable";
|
|
20361
|
+
}
|
|
20362
|
+
return rules;
|
|
20363
|
+
}
|
|
20364
|
+
function opencodeRules(cwd, env = process.env) {
|
|
20365
|
+
const rules = EMPTY();
|
|
20366
|
+
try {
|
|
20367
|
+
const config = env.XDG_CONFIG_HOME || path22.join(homeOf(env), ".config");
|
|
20368
|
+
const files = [
|
|
20369
|
+
...OPENCODE_NAMES.map((name) => path22.join(config, "opencode", name)),
|
|
20370
|
+
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path22.join(dir, name), path22.join(dir, ".opencode", name)])),
|
|
20371
|
+
...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
|
|
20372
|
+
];
|
|
20373
|
+
for (const file of files) {
|
|
20374
|
+
const text = readText2(file);
|
|
20375
|
+
if (text !== null && !opencodeConfig(parseJsonc(text), rules))
|
|
20376
|
+
return "unreadable";
|
|
20377
|
+
}
|
|
20378
|
+
if (env.OPENCODE_CONFIG_CONTENT && !opencodeConfig(parseJsonc(env.OPENCODE_CONFIG_CONTENT), rules))
|
|
20379
|
+
return "unreadable";
|
|
20380
|
+
} catch {
|
|
20381
|
+
return "unreadable";
|
|
20382
|
+
}
|
|
20383
|
+
return rules;
|
|
20384
|
+
}
|
|
20385
|
+
function codexRules(cwd, env = process.env) {
|
|
20386
|
+
const rules = EMPTY();
|
|
20387
|
+
try {
|
|
20388
|
+
const home = env.CODEX_HOME || path22.join(homeOf(env), ".codex");
|
|
20389
|
+
const top = gitTop(cwd) ?? cwd;
|
|
20390
|
+
for (const dir of [path22.join(home, "rules"), path22.join(top, ".codex", "rules")]) {
|
|
20391
|
+
let files;
|
|
20392
|
+
try {
|
|
20393
|
+
files = fs17.readdirSync(dir).filter((name) => name.endsWith(".rules"));
|
|
20394
|
+
} catch (error) {
|
|
20395
|
+
if (error.code === "ENOENT")
|
|
20396
|
+
continue;
|
|
20397
|
+
return "unreadable";
|
|
20398
|
+
}
|
|
20399
|
+
for (const name of files) {
|
|
20400
|
+
const text = readText2(path22.join(dir, name)) ?? "";
|
|
20401
|
+
for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
|
|
20402
|
+
const rule = codexRule(part);
|
|
20403
|
+
if (!rule)
|
|
20404
|
+
return "unreadable";
|
|
20405
|
+
(rule.decision === "allow" ? rules.allow : rules.deny).push(rule.pattern);
|
|
20406
|
+
}
|
|
20407
|
+
}
|
|
20408
|
+
}
|
|
20409
|
+
} catch {
|
|
20410
|
+
return "unreadable";
|
|
20411
|
+
}
|
|
20412
|
+
return rules;
|
|
20413
|
+
}
|
|
20414
|
+
function cursorRules(cwd, env = process.env) {
|
|
20415
|
+
const rules = EMPTY();
|
|
20416
|
+
try {
|
|
20417
|
+
const top = gitTop(cwd) ?? cwd;
|
|
20418
|
+
for (const file of [
|
|
20419
|
+
path22.join(homeOf(env), ".cursor", "cli-config.json"),
|
|
20420
|
+
path22.join(top, ".cursor", "cli.json")
|
|
20421
|
+
]) {
|
|
20422
|
+
const text = readText2(file);
|
|
20423
|
+
if (text === null)
|
|
20424
|
+
continue;
|
|
20425
|
+
const value = JSON.parse(text);
|
|
20426
|
+
if (!isRecord4(value))
|
|
20427
|
+
return "unreadable";
|
|
20428
|
+
if (value.permissions === undefined)
|
|
20429
|
+
continue;
|
|
20430
|
+
if (!isRecord4(value.permissions))
|
|
20431
|
+
return "unreadable";
|
|
20432
|
+
for (const [key, bucket] of [
|
|
20433
|
+
["deny", rules.deny],
|
|
20434
|
+
["allow", rules.allow]
|
|
20435
|
+
]) {
|
|
20436
|
+
const list = value.permissions[key];
|
|
20437
|
+
if (list === undefined)
|
|
20438
|
+
continue;
|
|
20439
|
+
if (!Array.isArray(list))
|
|
20440
|
+
return "unreadable";
|
|
20441
|
+
for (const entry of list) {
|
|
20442
|
+
if (typeof entry !== "string")
|
|
20443
|
+
return "unreadable";
|
|
20444
|
+
const match = /^Shell(?:\((.*)\))?$/su.exec(entry.trim());
|
|
20445
|
+
if (match)
|
|
20446
|
+
bucket.push(match[1] ?? "");
|
|
20447
|
+
}
|
|
20448
|
+
}
|
|
20449
|
+
}
|
|
20450
|
+
} catch {
|
|
20451
|
+
return "unreadable";
|
|
20452
|
+
}
|
|
20453
|
+
return rules;
|
|
20454
|
+
}
|
|
20455
|
+
function names(pattern, words) {
|
|
20456
|
+
const token = firstToken(pattern);
|
|
20457
|
+
if (!token || token === "*" || token.includes("*"))
|
|
20458
|
+
return true;
|
|
20459
|
+
const wanted = baseName(token);
|
|
20460
|
+
return words.some((word) => baseName(word) === wanted);
|
|
20461
|
+
}
|
|
20462
|
+
function wrapAllowed(read, words, command, options = {}) {
|
|
20463
|
+
if (read === "unreadable")
|
|
20464
|
+
return { ok: false, why: "named" };
|
|
20465
|
+
if (read.deny.some((pattern) => names(pattern, words)))
|
|
20466
|
+
return { ok: false, why: "named" };
|
|
20467
|
+
if (!options.bypass && read.allow.some(coversWrapper) && !read.allow.some((pattern) => coversCommand(pattern, command)))
|
|
20468
|
+
return { ok: false, why: "allowlisted" };
|
|
20469
|
+
return { ok: true };
|
|
20470
|
+
}
|
|
20471
|
+
var EMPTY = () => ({ deny: [], allow: [] }), readText2 = (file) => {
|
|
20472
|
+
try {
|
|
20473
|
+
return fs17.readFileSync(file, "utf8");
|
|
20474
|
+
} catch (error) {
|
|
20475
|
+
if (error.code === "ENOENT")
|
|
20476
|
+
return null;
|
|
20477
|
+
throw error;
|
|
20478
|
+
}
|
|
20479
|
+
}, isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), projectDirs = (cwd) => {
|
|
20480
|
+
const top = gitTop(cwd);
|
|
20481
|
+
const dirs = [];
|
|
20482
|
+
for (let dir = path22.resolve(cwd);; dir = path22.dirname(dir)) {
|
|
20483
|
+
dirs.push(dir);
|
|
20484
|
+
if (!top || dir === top || path22.dirname(dir) === dir)
|
|
20485
|
+
return dirs;
|
|
20486
|
+
}
|
|
20487
|
+
}, homeOf = (env) => env.HOME || os6.homedir(), CLAUDE_MANAGED, bashPattern = (entry) => {
|
|
20488
|
+
const match = /^Bash(?:\((.*)\))?$/su.exec(entry.trim());
|
|
20489
|
+
return match ? match[1] ?? "" : null;
|
|
20490
|
+
}, OPENCODE_NAMES, opencodePermission = (permission, rules) => {
|
|
20491
|
+
if (permission === undefined)
|
|
20492
|
+
return true;
|
|
20493
|
+
if (typeof permission === "string") {
|
|
20494
|
+
(permission === "allow" ? rules.allow : rules.deny).push("");
|
|
20495
|
+
return ["allow", "ask", "deny"].includes(permission);
|
|
20496
|
+
}
|
|
20497
|
+
if (!isRecord4(permission))
|
|
20498
|
+
return false;
|
|
20499
|
+
for (const key of ["bash", "shell"]) {
|
|
20500
|
+
const value = permission[key];
|
|
20501
|
+
if (value === undefined)
|
|
20502
|
+
continue;
|
|
20503
|
+
if (typeof value === "string") {
|
|
20504
|
+
if (!["allow", "ask", "deny"].includes(value))
|
|
20505
|
+
return false;
|
|
20506
|
+
(value === "allow" ? rules.allow : rules.deny).push("");
|
|
20507
|
+
continue;
|
|
20508
|
+
}
|
|
20509
|
+
if (!isRecord4(value))
|
|
20510
|
+
return false;
|
|
20511
|
+
for (const [pattern, action] of Object.entries(value)) {
|
|
20512
|
+
if (action !== "allow" && action !== "ask" && action !== "deny")
|
|
20513
|
+
return false;
|
|
20514
|
+
(action === "allow" ? rules.allow : rules.deny).push(pattern);
|
|
20515
|
+
}
|
|
20516
|
+
}
|
|
20517
|
+
return true;
|
|
20518
|
+
}, opencodeConfig = (value, rules) => {
|
|
20519
|
+
if (!isRecord4(value))
|
|
20520
|
+
return false;
|
|
20521
|
+
if (!opencodePermission(value.permission, rules))
|
|
20522
|
+
return false;
|
|
20523
|
+
if (value.agent === undefined)
|
|
20524
|
+
return true;
|
|
20525
|
+
if (!isRecord4(value.agent))
|
|
20526
|
+
return false;
|
|
20527
|
+
return Object.values(value.agent).every((agent) => isRecord4(agent) && opencodePermission(agent.permission, rules));
|
|
20528
|
+
}, codexRule = (body) => {
|
|
20529
|
+
const open = /\bpattern\s*=\s*\[/u.exec(body);
|
|
20530
|
+
if (!open)
|
|
20531
|
+
return null;
|
|
20532
|
+
const words = [];
|
|
20533
|
+
let index = open.index + open[0].length;
|
|
20534
|
+
for (;; ) {
|
|
20535
|
+
while (/[\s,]/u.test(body[index] ?? ""))
|
|
20536
|
+
index += 1;
|
|
20537
|
+
if (body[index] === "]")
|
|
20538
|
+
break;
|
|
20539
|
+
if (body[index] !== '"')
|
|
20540
|
+
return null;
|
|
20541
|
+
const literal = /"((?:[^"\\]|\\.)*)"/y;
|
|
20542
|
+
literal.lastIndex = index;
|
|
20543
|
+
const match = literal.exec(body);
|
|
20544
|
+
if (!match)
|
|
20545
|
+
return null;
|
|
20546
|
+
words.push(JSON.parse(`"${match[1]}"`));
|
|
20547
|
+
index = literal.lastIndex;
|
|
20548
|
+
}
|
|
20549
|
+
const decision = /\bdecision\s*=\s*"(\w+)"/u.exec(body)?.[1];
|
|
20550
|
+
if (!words.length || !decision || !["allow", "prompt", "forbidden"].includes(decision))
|
|
20551
|
+
return null;
|
|
20552
|
+
return { pattern: words.join(" "), decision };
|
|
20553
|
+
}, baseName = (word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, ""), firstToken = (pattern) => pattern.trim().split(/[:\s]/u)[0] ?? "", literalPrefix = (pattern) => pattern.trim().replace(/(?::\*| \*|\*)$/u, ""), coversWrapper = (pattern) => {
|
|
20554
|
+
const token = firstToken(pattern);
|
|
20555
|
+
if (!token || token.includes("*"))
|
|
20556
|
+
return true;
|
|
20557
|
+
const words = literalPrefix(pattern).split(/\s+/u);
|
|
20558
|
+
if (baseName(words[0]) !== "workit")
|
|
20559
|
+
return false;
|
|
20560
|
+
return ["check", "--heavy"].slice(0, words.length - 1).every((word, at) => {
|
|
20561
|
+
const given = words[at + 1];
|
|
20562
|
+
return given === word || word.startsWith(given);
|
|
20563
|
+
});
|
|
20564
|
+
}, coversCommand = (pattern, command) => {
|
|
20565
|
+
const prefix = literalPrefix(pattern);
|
|
20566
|
+
if (!prefix)
|
|
20567
|
+
return true;
|
|
20568
|
+
return !prefix.includes("*") && command.trim().startsWith(prefix);
|
|
20569
|
+
};
|
|
20570
|
+
var init_heavy_rules = __esm(() => {
|
|
20571
|
+
CLAUDE_MANAGED = {
|
|
20572
|
+
linux: "/etc/claude-code/managed-settings.json",
|
|
20573
|
+
darwin: "/Library/Application Support/ClaudeCode/managed-settings.json",
|
|
20574
|
+
win32: "C:\\Program Files\\ClaudeCode\\managed-settings.json"
|
|
20575
|
+
};
|
|
20576
|
+
OPENCODE_NAMES = ["opencode.json", "opencode.jsonc"];
|
|
20577
|
+
});
|
|
20578
|
+
|
|
20579
|
+
// packages/workit-core/src/core/heavy.ts
|
|
20580
|
+
import fs18 from "node:fs";
|
|
20581
|
+
import os7 from "node:os";
|
|
20582
|
+
import path23 from "node:path";
|
|
20583
|
+
function stateDir(env, platform = process.platform) {
|
|
20584
|
+
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
20585
|
+
return env.WORKFLOW_TOOLKIT_STATE;
|
|
20586
|
+
if (env.XDG_STATE_HOME)
|
|
20587
|
+
return path23.join(env.XDG_STATE_HOME, "workit");
|
|
20588
|
+
const home = env.HOME || os7.homedir();
|
|
20589
|
+
if (platform === "darwin")
|
|
20590
|
+
return path23.join(home, "Library", "Application Support", "workit");
|
|
20591
|
+
if (platform === "win32")
|
|
20592
|
+
return path23.join(env.LOCALAPPDATA ?? path23.join(home, "AppData", "Local"), "workit");
|
|
20593
|
+
return path23.join(home, ".local", "state", "workit");
|
|
20594
|
+
}
|
|
20595
|
+
function positional2(words, from, values = PM_VALUES) {
|
|
20596
|
+
let index = from;
|
|
20597
|
+
while (index < words.length && words[index].startsWith("-")) {
|
|
20598
|
+
if (words[index] === "--")
|
|
20599
|
+
return index + 1;
|
|
20600
|
+
index += values.has(words[index]) ? 2 : 1;
|
|
20601
|
+
}
|
|
20602
|
+
return index;
|
|
20603
|
+
}
|
|
20604
|
+
function builtIn(words, depth = 0) {
|
|
20605
|
+
if (depth > 6 || !words.length)
|
|
20606
|
+
return null;
|
|
20607
|
+
const name = baseName2(words[0]);
|
|
20608
|
+
const label = () => words.slice(0, 3).join(" ");
|
|
20609
|
+
const inner = (from) => builtIn(words.slice(from), depth + 1);
|
|
20610
|
+
if (WORKIT.test(name))
|
|
20611
|
+
return null;
|
|
20612
|
+
if (INFO_FLAGS.has(words[1] ?? "") && words.length === 2)
|
|
20613
|
+
return null;
|
|
20614
|
+
if (words.some((word) => word === "--watch" || word.startsWith("--watch=") || word === "--watchAll"))
|
|
20615
|
+
return null;
|
|
20616
|
+
if (name === "npx" || name === "bunx" || name === "pnpx")
|
|
20617
|
+
return inner(positional2(words, 1));
|
|
20618
|
+
if (name === "corepack")
|
|
20619
|
+
return inner(1);
|
|
20620
|
+
if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun" || name === "deno") {
|
|
20621
|
+
const at = positional2(words, 1);
|
|
20622
|
+
const command = words[at];
|
|
20623
|
+
if (command === undefined)
|
|
20624
|
+
return name === "yarn" ? "yarn (install)" : null;
|
|
20625
|
+
if (name === "deno") {
|
|
20626
|
+
if (["test", "bench", "check", "compile", "lint"].includes(command))
|
|
20627
|
+
return label();
|
|
20628
|
+
if (command === "task" && HEAVY_SCRIPT.test(words[at + 1] ?? ""))
|
|
20629
|
+
return label();
|
|
20630
|
+
return null;
|
|
20631
|
+
}
|
|
20632
|
+
if (NPM_INSTALL.has(command) || NPM_TEST.has(command))
|
|
20633
|
+
return label();
|
|
20634
|
+
if (name === "bun" && command === "build")
|
|
20635
|
+
return label();
|
|
20636
|
+
if (EXEC.has(command))
|
|
20637
|
+
return inner(positional2(words, at + 1));
|
|
20638
|
+
if (name === "yarn" && command === "workspace")
|
|
20639
|
+
return inner(at + 2);
|
|
20640
|
+
if (name === "yarn" && command === "workspaces")
|
|
20641
|
+
return words.slice(at + 1).some((word) => HEAVY_SCRIPT.test(word)) ? label() : null;
|
|
20642
|
+
if (NPM_RUN.has(command)) {
|
|
20643
|
+
const script = words[positional2(words, at + 1)];
|
|
20644
|
+
if (script === undefined || WATCH_SCRIPT.test(script))
|
|
20645
|
+
return null;
|
|
20646
|
+
if (HEAVY_SCRIPT.test(script))
|
|
20647
|
+
return label();
|
|
20648
|
+
return name === "bun" ? inner(positional2(words, at + 1)) : null;
|
|
20649
|
+
}
|
|
20650
|
+
if (name === "npm")
|
|
20651
|
+
return null;
|
|
20652
|
+
if (WATCH_SCRIPT.test(command))
|
|
20653
|
+
return null;
|
|
20654
|
+
if (HEAVY_SCRIPT.test(command) && !/\.[cm]?[jt]sx?$/u.test(command))
|
|
20655
|
+
return label();
|
|
20656
|
+
return inner(at);
|
|
20657
|
+
}
|
|
20658
|
+
if (PYTHONS.test(name)) {
|
|
20659
|
+
const at = words.indexOf("-m");
|
|
20660
|
+
return at > 0 && words[at + 1] ? inner(at + 1) : null;
|
|
20661
|
+
}
|
|
20662
|
+
if (["uv", "poetry", "pdm", "pipenv", "hatch", "rye"].includes(name)) {
|
|
20663
|
+
const at = positional2(words, 1);
|
|
20664
|
+
const command = words[at];
|
|
20665
|
+
if (command === "run")
|
|
20666
|
+
return inner(positional2(words, at + 1));
|
|
20667
|
+
if (command === "pip")
|
|
20668
|
+
return inner(at);
|
|
20669
|
+
return ["sync", "install", "build", "test", "add"].includes(command ?? "") ? label() : null;
|
|
20670
|
+
}
|
|
20671
|
+
if (name === "bundle") {
|
|
20672
|
+
const command = words[positional2(words, 1)];
|
|
20673
|
+
if (command === "exec")
|
|
20674
|
+
return inner(positional2(words, 2));
|
|
20675
|
+
return command === "install" || command === undefined ? label() : null;
|
|
20676
|
+
}
|
|
20677
|
+
if (name === "make" || name === "gmake") {
|
|
20678
|
+
const targets = words.slice(1).filter((word) => !word.startsWith("-") && !word.includes("="));
|
|
20679
|
+
if (targets.some((target) => WATCH_SCRIPT.test(target)))
|
|
20680
|
+
return null;
|
|
20681
|
+
return !targets.length || targets.some((target) => HEAVY_SCRIPT.test(target)) ? label() : null;
|
|
20682
|
+
}
|
|
20683
|
+
if (name === "rake") {
|
|
20684
|
+
const targets = words.slice(1).filter((word) => !word.startsWith("-") && !word.includes("="));
|
|
20685
|
+
return targets.some((target) => HEAVY_SCRIPT.test(target) || /^spec(?:$|:)/u.test(target)) ? label() : null;
|
|
20686
|
+
}
|
|
20687
|
+
if (name === "nx") {
|
|
20688
|
+
const at = positional2(words, 1);
|
|
20689
|
+
const command = words[at] ?? "";
|
|
20690
|
+
if (["run-many", "affected"].includes(command))
|
|
20691
|
+
return words.some((word) => /^(?:-t|--targets?)(?:=|$)/u.test(word)) && !words.some((word) => WATCH_SCRIPT.test(word.replace(/^--?targets?=/u, ""))) ? label() : null;
|
|
20692
|
+
if (command === "run") {
|
|
20693
|
+
const target = (words[at + 1] ?? "").split(":")[1] ?? "";
|
|
20694
|
+
return HEAVY_SCRIPT.test(target) && !WATCH_SCRIPT.test(target) ? label() : null;
|
|
20695
|
+
}
|
|
20696
|
+
return HEAVY_SCRIPT.test(command) && !WATCH_SCRIPT.test(command) ? label() : null;
|
|
20697
|
+
}
|
|
20698
|
+
if (name === "docker" || name === "podman" || name === "nerdctl") {
|
|
20699
|
+
const at = positional2(words, 1, new Set(["-H", "--host", "--context", "-c", "--config"]));
|
|
20700
|
+
const command = words[at];
|
|
20701
|
+
if (command === "build")
|
|
20702
|
+
return label();
|
|
20703
|
+
if (command === "buildx" || command === "image" || command === "builder")
|
|
20704
|
+
return words[positional2(words, at + 1)] === "build" ? label() : null;
|
|
20705
|
+
if (command === "compose") {
|
|
20706
|
+
const at2 = positional2(words, at + 1, new Set(["-f", "--file", "-p"]));
|
|
20707
|
+
const action = words[at2];
|
|
20708
|
+
if (action === "build")
|
|
20709
|
+
return label();
|
|
20710
|
+
return action === "up" && words.slice(at2 + 1).includes("--build") ? label() : null;
|
|
20711
|
+
}
|
|
20712
|
+
return null;
|
|
20713
|
+
}
|
|
20714
|
+
const rule = TOOLS[name];
|
|
20715
|
+
if (!rule)
|
|
20716
|
+
return null;
|
|
20717
|
+
if (words.slice(1).some((word) => WATCH_FLAG.test(word) || word === "watch" || word === "dev"))
|
|
20718
|
+
return null;
|
|
20719
|
+
if (rule === "any")
|
|
20720
|
+
return label();
|
|
20721
|
+
const command = words.slice(1).find((word) => !word.startsWith("-") && !word.startsWith("+") || rule.has(word));
|
|
20722
|
+
return command !== undefined && rule.has(command) ? label() : null;
|
|
20723
|
+
}
|
|
20724
|
+
function parsePattern(pattern) {
|
|
20725
|
+
const words = pattern.trim().split(/\s+/u).filter(Boolean);
|
|
20726
|
+
return words.length ? words.map(globWord) : null;
|
|
20727
|
+
}
|
|
20728
|
+
function machinePatterns(env) {
|
|
20729
|
+
const value = readJson(heavyCommandsFile(env));
|
|
20730
|
+
return isRecord5(value) ? stringList(value.patterns) : [];
|
|
20731
|
+
}
|
|
20732
|
+
function repoPatterns(cwd) {
|
|
20733
|
+
return repoHeavyCommands(gitTop(cwd) ?? path23.resolve(cwd));
|
|
20734
|
+
}
|
|
20735
|
+
function heavyLines(lines, options) {
|
|
20736
|
+
const found = [];
|
|
20737
|
+
let patterns = null;
|
|
20738
|
+
for (const line of lines) {
|
|
20739
|
+
const reason = builtIn(line);
|
|
20740
|
+
if (reason) {
|
|
20741
|
+
found.push({ line, source: "catalog", reason });
|
|
20742
|
+
continue;
|
|
20743
|
+
}
|
|
20744
|
+
patterns ??= (options.patterns ?? [
|
|
20745
|
+
...machinePatterns(options.env ?? process.env).map((pattern) => ({
|
|
20746
|
+
pattern,
|
|
20747
|
+
from: "machine"
|
|
20748
|
+
})),
|
|
20749
|
+
...repoPatterns(options.cwd).map((pattern) => ({ pattern, from: "repo" }))
|
|
20750
|
+
]).flatMap((entry) => {
|
|
20751
|
+
const matchers = parsePattern(entry.pattern);
|
|
20752
|
+
return matchers ? [{ ...entry, matchers }] : [];
|
|
20753
|
+
});
|
|
20754
|
+
const hit = patterns.find((entry) => matches(entry.matchers, line));
|
|
20755
|
+
if (hit)
|
|
20756
|
+
found.push({
|
|
20757
|
+
line,
|
|
20758
|
+
source: { pattern: hit.pattern, from: hit.from },
|
|
20759
|
+
reason: `${hit.from} pattern "${hit.pattern}"`
|
|
20760
|
+
});
|
|
20761
|
+
}
|
|
20762
|
+
return found;
|
|
20763
|
+
}
|
|
20764
|
+
function heavyPlan(command, options) {
|
|
20765
|
+
const dialect = options.dialect ?? "posix";
|
|
20766
|
+
if (dialect !== "posix" || !gitTop(options.cwd))
|
|
20767
|
+
return { kind: "none" };
|
|
20768
|
+
const lines = linesOf(command, dialect);
|
|
20769
|
+
if (!lines)
|
|
20770
|
+
return { kind: "none" };
|
|
20771
|
+
const heavy = heavyLines(lines, options);
|
|
20772
|
+
if (!heavy.length)
|
|
20773
|
+
return { kind: "none" };
|
|
20774
|
+
const simple = simpleCommand(command, dialect);
|
|
20775
|
+
const single = simple && !WORKIT.test(baseName2(simple[0])) && heavyLines([simple], options)[0];
|
|
20776
|
+
const judged = single ? [{ words: lines.flat(), text: command }] : heavy.map((match) => ({
|
|
20777
|
+
words: match.line,
|
|
20778
|
+
text: match.line.join(" ")
|
|
20779
|
+
}));
|
|
20780
|
+
if (options.rules !== null)
|
|
20781
|
+
for (const { words, text } of judged) {
|
|
20782
|
+
const verdict = wrapAllowed(options.rules, words, text, { bypass: options.bypass });
|
|
20783
|
+
if (!verdict.ok)
|
|
20784
|
+
return {
|
|
20785
|
+
kind: "passthrough",
|
|
20786
|
+
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.`
|
|
20787
|
+
};
|
|
20788
|
+
}
|
|
20789
|
+
if (single) {
|
|
20790
|
+
const rewrite = leaseRewrite(command);
|
|
20791
|
+
return rewrite ? { kind: "rewrite", command: rewrite, reason: single.reason } : { kind: "none" };
|
|
20792
|
+
}
|
|
20793
|
+
const parts = heavy.map((match) => leaseCommand(match.line));
|
|
20794
|
+
return {
|
|
20795
|
+
kind: "split",
|
|
20796
|
+
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.`,
|
|
20797
|
+
unblock: parts[0] ?? null
|
|
20798
|
+
};
|
|
20799
|
+
}
|
|
20800
|
+
function workitOnPath2(env = process.env) {
|
|
20801
|
+
const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
|
|
20802
|
+
for (const dir of (env.PATH ?? "").split(path23.delimiter))
|
|
20803
|
+
for (const name of names) {
|
|
20804
|
+
if (!dir)
|
|
20805
|
+
continue;
|
|
20806
|
+
try {
|
|
20807
|
+
fs18.accessSync(path23.join(dir, name), fs18.constants.X_OK);
|
|
20808
|
+
return true;
|
|
20809
|
+
} catch {}
|
|
20810
|
+
}
|
|
20811
|
+
return false;
|
|
20812
|
+
}
|
|
20813
|
+
function leaseRewrite(command) {
|
|
20814
|
+
const text = command.trim();
|
|
20815
|
+
const prefix = ASSIGNMENTS.exec(text)?.[1] ?? "";
|
|
20816
|
+
const rest = text.slice(prefix.length).trim();
|
|
20817
|
+
if (!rest)
|
|
20818
|
+
return null;
|
|
20819
|
+
return `${prefix}workit check --heavy -- ${rest}`;
|
|
20820
|
+
}
|
|
20821
|
+
var HEAVY_COMMANDS_FILE = "heavy-commands.json", heavyCommandsFile = (env) => path23.join(stateDir(env), HEAVY_COMMANDS_FILE), HEAVY_SCRIPT, INFO_FLAGS, WATCH_FLAG, WATCH_SCRIPT, sub = (...names) => new Set(names), TOOLS, PM_VALUES, baseName2 = (word) => (word?.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat|ps1)$/u, ""), WORKIT, PYTHONS, NPM_INSTALL, NPM_RUN, NPM_TEST, EXEC, globWord = (word) => new RegExp(`^${word.replace(/[.+?^${}()|[\]\\]/gu, "\\$&").replaceAll("*", ".*")}$`, "u"), 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])), readJson = (file) => {
|
|
20822
|
+
try {
|
|
20823
|
+
return JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
20824
|
+
} catch {
|
|
20825
|
+
return;
|
|
20826
|
+
}
|
|
20827
|
+
}, isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), stringList = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [], linesOf = (command, dialect) => {
|
|
20828
|
+
try {
|
|
20829
|
+
return commandLines(command, dialect);
|
|
20830
|
+
} catch {
|
|
20831
|
+
return null;
|
|
20832
|
+
}
|
|
20833
|
+
}, quoteWord = (word) => /^[\w@%+=:,./-]+$/u.test(word) ? word : `'${word.replaceAll("'", `'\\''`)}'`, leaseCommand = (words) => `workit check --heavy -- ${words.map(quoteWord).join(" ")}`, ASSIGNMENTS;
|
|
20834
|
+
var init_heavy = __esm(() => {
|
|
20835
|
+
init_raw_git();
|
|
20836
|
+
init_check_config();
|
|
20837
|
+
init_heavy_rules();
|
|
20838
|
+
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;
|
|
20839
|
+
INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
|
|
20840
|
+
WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
|
|
20841
|
+
WATCH_SCRIPT = /(?:^|[:_.-])(?:watch|dev|serve|start|storybook)(?:$|[:_.-])/iu;
|
|
20842
|
+
TOOLS = {
|
|
20843
|
+
jest: "any",
|
|
20844
|
+
vitest: "any",
|
|
20845
|
+
mocha: "any",
|
|
20846
|
+
ava: "any",
|
|
20847
|
+
tap: "any",
|
|
20848
|
+
karma: "any",
|
|
20849
|
+
jasmine: "any",
|
|
20850
|
+
nyc: "any",
|
|
20851
|
+
c8: "any",
|
|
20852
|
+
tsc: "any",
|
|
20853
|
+
"vue-tsc": "any",
|
|
20854
|
+
"svelte-check": "any",
|
|
20855
|
+
eslint: "any",
|
|
20856
|
+
stylelint: "any",
|
|
20857
|
+
tslint: "any",
|
|
20858
|
+
webpack: "any",
|
|
20859
|
+
"webpack-cli": "any",
|
|
20860
|
+
rollup: "any",
|
|
20861
|
+
parcel: "any",
|
|
20862
|
+
tsup: "any",
|
|
20863
|
+
vite: sub("build", "optimize"),
|
|
20864
|
+
next: sub("build", "lint"),
|
|
20865
|
+
nuxt: sub("build", "generate"),
|
|
20866
|
+
nuxi: sub("build", "generate"),
|
|
20867
|
+
astro: sub("build", "check"),
|
|
20868
|
+
ng: sub("build", "b", "test", "t", "e2e", "lint"),
|
|
20869
|
+
"react-scripts": sub("build", "test"),
|
|
20870
|
+
playwright: sub("test", "install"),
|
|
20871
|
+
cypress: sub("run"),
|
|
20872
|
+
wdio: "any",
|
|
20873
|
+
testcafe: "any",
|
|
20874
|
+
turbo: "any",
|
|
20875
|
+
lerna: sub("run", "exec", "bootstrap"),
|
|
20876
|
+
"sonar-scanner": "any",
|
|
20877
|
+
sonarless: sub("scan"),
|
|
20878
|
+
"sonarless-mcp": sub("scan"),
|
|
20879
|
+
go: sub("test", "build", "vet", "install", "generate"),
|
|
20880
|
+
"golangci-lint": sub("run"),
|
|
20881
|
+
gotestsum: "any",
|
|
20882
|
+
staticcheck: "any",
|
|
20883
|
+
cargo: sub("build", "b", "test", "t", "check", "c", "clippy", "bench", "nextest", "doc", "install", "tarpaulin", "llvm-cov"),
|
|
20884
|
+
mvn: "any",
|
|
20885
|
+
mvnw: "any",
|
|
20886
|
+
gradle: "any",
|
|
20887
|
+
gradlew: "any",
|
|
20888
|
+
ant: "any",
|
|
20889
|
+
sbt: "any",
|
|
20890
|
+
bazel: sub("build", "test", "run", "coverage"),
|
|
20891
|
+
bazelisk: sub("build", "test", "run", "coverage"),
|
|
20892
|
+
buck2: sub("build", "test", "run"),
|
|
20893
|
+
dotnet: sub("build", "test", "publish", "restore", "pack", "msbuild"),
|
|
20894
|
+
msbuild: "any",
|
|
20895
|
+
pytest: "any",
|
|
20896
|
+
"py.test": "any",
|
|
20897
|
+
tox: "any",
|
|
20898
|
+
nox: "any",
|
|
20899
|
+
mypy: "any",
|
|
20900
|
+
pyright: "any",
|
|
20901
|
+
pylint: "any",
|
|
20902
|
+
coverage: sub("run"),
|
|
20903
|
+
maturin: sub("build", "develop"),
|
|
20904
|
+
pip: sub("install", "wheel"),
|
|
20905
|
+
pip3: sub("install", "wheel"),
|
|
20906
|
+
rspec: "any",
|
|
20907
|
+
rails: sub("test", "assets:precompile"),
|
|
20908
|
+
phpunit: "any",
|
|
20909
|
+
pest: "any",
|
|
20910
|
+
phpstan: "any",
|
|
20911
|
+
psalm: "any",
|
|
20912
|
+
composer: sub("install", "update", "require", "test"),
|
|
20913
|
+
mix: sub("test", "compile", "deps.get", "dialyzer", "credo"),
|
|
20914
|
+
swift: sub("build", "test"),
|
|
20915
|
+
xcodebuild: "any",
|
|
20916
|
+
flutter: sub("build", "test"),
|
|
20917
|
+
dart: sub("test", "compile"),
|
|
20918
|
+
pod: sub("install"),
|
|
20919
|
+
cmake: sub("--build"),
|
|
20920
|
+
ninja: "any",
|
|
20921
|
+
meson: sub("compile", "test"),
|
|
20922
|
+
ctest: "any",
|
|
20923
|
+
scons: "any",
|
|
20924
|
+
"docker-compose": sub("build"),
|
|
20925
|
+
buildah: sub("bud", "build"),
|
|
20926
|
+
earthly: "any"
|
|
20927
|
+
};
|
|
20928
|
+
PM_VALUES = new Set([
|
|
20929
|
+
"--prefix",
|
|
20930
|
+
"-w",
|
|
20931
|
+
"--workspace",
|
|
20932
|
+
"-C",
|
|
20933
|
+
"--dir",
|
|
20934
|
+
"--filter",
|
|
20935
|
+
"-F",
|
|
20936
|
+
"--cwd",
|
|
20937
|
+
"-p",
|
|
20938
|
+
"--package",
|
|
20939
|
+
"--cache",
|
|
20940
|
+
"--registry"
|
|
20941
|
+
]);
|
|
20942
|
+
WORKIT = /^(?:workit|@brainervirus\/workit-cli(?:@.*)?)$/u;
|
|
20943
|
+
PYTHONS = /^(?:python[\d.]*|py)$/u;
|
|
20944
|
+
NPM_INSTALL = new Set([
|
|
20945
|
+
"ci",
|
|
20946
|
+
"clean-install",
|
|
20947
|
+
"cit",
|
|
20948
|
+
"install-test",
|
|
20949
|
+
"it",
|
|
20950
|
+
"install",
|
|
20951
|
+
"i",
|
|
20952
|
+
"in",
|
|
20953
|
+
"add",
|
|
20954
|
+
"rebuild",
|
|
20955
|
+
"update",
|
|
20956
|
+
"up"
|
|
20957
|
+
]);
|
|
20958
|
+
NPM_RUN = new Set(["run", "run-script", "rum", "urn"]);
|
|
20959
|
+
NPM_TEST = new Set(["test", "t", "tst"]);
|
|
20960
|
+
EXEC = new Set(["exec", "x", "dlx"]);
|
|
20961
|
+
ASSIGNMENTS = /^\s*((?:[A-Za-z_]\w*=(?:'[^']*'|"(?:[^"\\]|\\.)*"|[^\s'"])*\s+)*)/u;
|
|
20962
|
+
});
|
|
20963
|
+
|
|
20964
|
+
// packages/workit-core/src/core/heavy-words.ts
|
|
20965
|
+
import fs19 from "node:fs";
|
|
20966
|
+
import os8 from "node:os";
|
|
20967
|
+
import path24 from "node:path";
|
|
20968
|
+
function maybeHeavy(command, cwd, env = process.env) {
|
|
20969
|
+
try {
|
|
20970
|
+
const words = wordsOf(command);
|
|
20971
|
+
if (words.some((word) => HEAVY_WORDS.has(word) || /^python[\d.]*$/u.test(word)))
|
|
20972
|
+
return true;
|
|
20973
|
+
const extra = patternWords(cwd, env);
|
|
20974
|
+
return extra === null || words.some((word) => extra.has(word));
|
|
20975
|
+
} catch {
|
|
20976
|
+
return true;
|
|
20977
|
+
}
|
|
20978
|
+
}
|
|
20979
|
+
var HEAVY_WORDS, wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean), stateDir2 = (env) => {
|
|
20980
|
+
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
20981
|
+
return env.WORKFLOW_TOOLKIT_STATE;
|
|
20982
|
+
if (env.XDG_STATE_HOME)
|
|
20983
|
+
return path24.join(env.XDG_STATE_HOME, "workit");
|
|
20984
|
+
const home = env.HOME || os8.homedir();
|
|
20985
|
+
if (process.platform === "darwin")
|
|
20986
|
+
return path24.join(home, "Library", "Application Support", "workit");
|
|
20987
|
+
if (process.platform === "win32")
|
|
20988
|
+
return path24.join(env.LOCALAPPDATA ?? path24.join(home, "AppData", "Local"), "workit");
|
|
20989
|
+
return path24.join(home, ".local", "state", "workit");
|
|
20990
|
+
}, readJson2 = (file) => {
|
|
20991
|
+
try {
|
|
20992
|
+
return JSON.parse(fs19.readFileSync(file, "utf8"));
|
|
20993
|
+
} catch {
|
|
20994
|
+
return;
|
|
20995
|
+
}
|
|
20996
|
+
}, patternWords = (cwd, env) => {
|
|
20997
|
+
const patterns = [];
|
|
20998
|
+
const machine = readJson2(path24.join(stateDir2(env), "heavy-commands.json"));
|
|
20999
|
+
if (Array.isArray(machine?.patterns))
|
|
21000
|
+
patterns.push(...machine.patterns);
|
|
21001
|
+
for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
|
|
21002
|
+
if (fs19.existsSync(path24.join(dir, ".git"))) {
|
|
21003
|
+
patterns.push(...repoHeavyCommands(dir));
|
|
21004
|
+
break;
|
|
21005
|
+
}
|
|
21006
|
+
if (path24.dirname(dir) === dir)
|
|
21007
|
+
break;
|
|
21008
|
+
}
|
|
21009
|
+
const words = new Set;
|
|
21010
|
+
for (const pattern of patterns) {
|
|
21011
|
+
if (typeof pattern !== "string")
|
|
21012
|
+
continue;
|
|
21013
|
+
const first = wordsOf(pattern)[0];
|
|
21014
|
+
if (!first || first.includes("*"))
|
|
21015
|
+
return null;
|
|
21016
|
+
words.add(first);
|
|
21017
|
+
}
|
|
21018
|
+
return words;
|
|
21019
|
+
};
|
|
21020
|
+
var init_heavy_words = __esm(() => {
|
|
21021
|
+
init_check_config();
|
|
21022
|
+
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(" "));
|
|
21023
|
+
});
|
|
21024
|
+
|
|
21025
|
+
// packages/workit-core/src/hooks/heavy-gate.ts
|
|
21026
|
+
function heavyGate(cwd, command, options) {
|
|
21027
|
+
const env = options.env ?? process.env;
|
|
21028
|
+
try {
|
|
21029
|
+
if (env.WORKIT_HEAVY_GATE === "0" || !maybeHeavy(command, cwd, env) || !workitOnPath2(env))
|
|
21030
|
+
return { kind: "none" };
|
|
21031
|
+
return heavyPlan(command, {
|
|
21032
|
+
cwd,
|
|
21033
|
+
env,
|
|
21034
|
+
dialect: options.dialect ?? "posix",
|
|
21035
|
+
rules: rulesFor(options.host, cwd, env),
|
|
21036
|
+
bypass: options.permissionMode === "bypassPermissions"
|
|
21037
|
+
});
|
|
21038
|
+
} catch {
|
|
21039
|
+
return { kind: "none" };
|
|
21040
|
+
}
|
|
21041
|
+
}
|
|
21042
|
+
var rulesFor = (host, cwd, env) => {
|
|
21043
|
+
switch (host) {
|
|
21044
|
+
case "claude_code":
|
|
21045
|
+
return claudeRules(cwd, env);
|
|
21046
|
+
case "opencode":
|
|
21047
|
+
return opencodeRules(cwd, env);
|
|
21048
|
+
case "codex_cli":
|
|
21049
|
+
case "codex_desktop":
|
|
21050
|
+
return codexRules(cwd, env);
|
|
21051
|
+
case "cursor":
|
|
21052
|
+
return cursorRules(cwd, env);
|
|
21053
|
+
case "pi":
|
|
21054
|
+
return null;
|
|
21055
|
+
}
|
|
21056
|
+
}, 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.`, heavyDenial = (original, plan) => ({
|
|
21057
|
+
kind: "deny",
|
|
21058
|
+
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}`,
|
|
21059
|
+
unblock: plan.command
|
|
21060
|
+
}), splitDenial = (plan) => ({
|
|
21061
|
+
kind: "deny",
|
|
21062
|
+
reason: plan.reason,
|
|
21063
|
+
unblock: plan.unblock
|
|
21064
|
+
});
|
|
21065
|
+
var init_heavy_gate = __esm(() => {
|
|
21066
|
+
init_heavy_rules();
|
|
21067
|
+
init_heavy();
|
|
21068
|
+
init_heavy_words();
|
|
21069
|
+
});
|
|
21070
|
+
|
|
21071
|
+
// packages/workit-core/src/core/live-session.ts
|
|
21072
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
21073
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
21074
|
+
import fs20 from "node:fs";
|
|
21075
|
+
import os9 from "node:os";
|
|
21076
|
+
import path25 from "node:path";
|
|
20156
21077
|
var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session, familyOf = (host) => host.replace(/_(?:cli|desktop)$/u, ""), processAlive = (pid) => {
|
|
20157
21078
|
try {
|
|
20158
21079
|
process.kill(pid, 0);
|
|
@@ -20163,9 +21084,9 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
|
|
|
20163
21084
|
}, processInfo = (pid) => {
|
|
20164
21085
|
try {
|
|
20165
21086
|
if (liveProbe.platform === "linux") {
|
|
20166
|
-
const stat =
|
|
21087
|
+
const stat = fs20.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
20167
21088
|
const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
|
|
20168
|
-
const command =
|
|
21089
|
+
const command = fs20.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
|
|
20169
21090
|
return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
|
|
20170
21091
|
}
|
|
20171
21092
|
if (liveProbe.platform === "win32")
|
|
@@ -20210,32 +21131,32 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
|
|
|
20210
21131
|
pid: () => hostPid(transport)
|
|
20211
21132
|
};
|
|
20212
21133
|
}, checkoutOf = (target) => {
|
|
20213
|
-
let dir =
|
|
20214
|
-
while (!
|
|
20215
|
-
dir =
|
|
20216
|
-
if (
|
|
20217
|
-
dir =
|
|
20218
|
-
const parts = dir.split(
|
|
21134
|
+
let dir = path25.resolve(target);
|
|
21135
|
+
while (!fs20.existsSync(dir) && path25.dirname(dir) !== dir)
|
|
21136
|
+
dir = path25.dirname(dir);
|
|
21137
|
+
if (fs20.existsSync(dir) && !fs20.statSync(dir).isDirectory())
|
|
21138
|
+
dir = path25.dirname(dir);
|
|
21139
|
+
const parts = dir.split(path25.sep);
|
|
20219
21140
|
const dotGit = parts.lastIndexOf(".git");
|
|
20220
21141
|
if (dotGit > 0)
|
|
20221
|
-
dir = parts.slice(0, dotGit).join(
|
|
21142
|
+
dir = parts.slice(0, dotGit).join(path25.sep) || path25.sep;
|
|
20222
21143
|
const location = resolveStore(dir);
|
|
20223
21144
|
if (location instanceof Error || !location.shared || !location.top)
|
|
20224
21145
|
return null;
|
|
20225
21146
|
let top = location.top;
|
|
20226
21147
|
try {
|
|
20227
|
-
top =
|
|
21148
|
+
top = fs20.realpathSync.native(top);
|
|
20228
21149
|
} catch {}
|
|
20229
21150
|
return top;
|
|
20230
|
-
}, markerFile2 = (top) =>
|
|
20231
|
-
let key =
|
|
21151
|
+
}, markerFile2 = (top) => path25.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`), checkoutKey = (top, platform = process.platform) => {
|
|
21152
|
+
let key = path25.resolve(top);
|
|
20232
21153
|
try {
|
|
20233
|
-
key =
|
|
21154
|
+
key = fs20.realpathSync.native(key);
|
|
20234
21155
|
} catch {}
|
|
20235
21156
|
return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
|
|
20236
21157
|
}, readMarker = (file) => {
|
|
20237
21158
|
try {
|
|
20238
|
-
const value = JSON.parse(
|
|
21159
|
+
const value = JSON.parse(fs20.readFileSync(file, "utf8"));
|
|
20239
21160
|
if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
|
|
20240
21161
|
return null;
|
|
20241
21162
|
return {
|
|
@@ -20256,7 +21177,7 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
|
|
|
20256
21177
|
const age = ageOf2(marker, deps.now ?? new Date);
|
|
20257
21178
|
if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
|
|
20258
21179
|
return false;
|
|
20259
|
-
if (marker.machine !== (deps.machine ??
|
|
21180
|
+
if (marker.machine !== (deps.machine ?? os9.hostname()) || marker.pid === null)
|
|
20260
21181
|
return true;
|
|
20261
21182
|
if (!(deps.alive ?? processAlive)(marker.pid))
|
|
20262
21183
|
return false;
|
|
@@ -20282,7 +21203,7 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
|
|
|
20282
21203
|
const marker = readMarker(file);
|
|
20283
21204
|
if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
|
|
20284
21205
|
return null;
|
|
20285
|
-
if (sameOwner(marker, owner, deps.machine ??
|
|
21206
|
+
if (sameOwner(marker, owner, deps.machine ?? os9.hostname()))
|
|
20286
21207
|
return null;
|
|
20287
21208
|
return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
|
|
20288
21209
|
} catch {
|
|
@@ -20295,7 +21216,7 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
|
|
|
20295
21216
|
return;
|
|
20296
21217
|
const file = markerFile2(checkout);
|
|
20297
21218
|
const now = deps.now ?? new Date;
|
|
20298
|
-
const machine = deps.machine ??
|
|
21219
|
+
const machine = deps.machine ?? os9.hostname();
|
|
20299
21220
|
const current = readMarker(file);
|
|
20300
21221
|
if (current) {
|
|
20301
21222
|
const age = ageOf2(current, now);
|
|
@@ -20316,10 +21237,10 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
|
|
|
20316
21237
|
checkout,
|
|
20317
21238
|
heartbeatAt: now.toISOString()
|
|
20318
21239
|
};
|
|
20319
|
-
|
|
21240
|
+
fs20.mkdirSync(path25.dirname(file), { recursive: true });
|
|
20320
21241
|
const temp = `${file}.${process.pid}.tmp`;
|
|
20321
|
-
|
|
20322
|
-
|
|
21242
|
+
fs20.writeFileSync(temp, JSON.stringify(marker));
|
|
21243
|
+
fs20.renameSync(temp, file);
|
|
20323
21244
|
} catch {}
|
|
20324
21245
|
};
|
|
20325
21246
|
var init_live_session = __esm(() => {
|
|
@@ -20338,8 +21259,8 @@ var init_live_session = __esm(() => {
|
|
|
20338
21259
|
|
|
20339
21260
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
20340
21261
|
import { realpathSync as realpathSync5 } from "node:fs";
|
|
20341
|
-
import
|
|
20342
|
-
import
|
|
21262
|
+
import os10 from "node:os";
|
|
21263
|
+
import path26 from "node:path";
|
|
20343
21264
|
function writeGate(cwd, paths, owner, deps) {
|
|
20344
21265
|
if (!owner)
|
|
20345
21266
|
return beforeWriteGate(cwd, paths);
|
|
@@ -20359,7 +21280,7 @@ function shellGate(cwd, command, owner, dialect = "posix", deps) {
|
|
|
20359
21280
|
try {
|
|
20360
21281
|
targets = [
|
|
20361
21282
|
...segmentTargets(cwd, command, dialect),
|
|
20362
|
-
...gitMutationDirs(cwd, command, dialect).map((dir) =>
|
|
21283
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path26.join(dir, DIR_TARGET))
|
|
20363
21284
|
];
|
|
20364
21285
|
} catch {
|
|
20365
21286
|
targets = [];
|
|
@@ -20397,24 +21318,37 @@ function beforeWriteGate(cwd, paths) {
|
|
|
20397
21318
|
}
|
|
20398
21319
|
var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
20399
21320
|
let rest = "";
|
|
20400
|
-
for (let dir =
|
|
21321
|
+
for (let dir = path26.resolve(file);; dir = path26.dirname(dir)) {
|
|
20401
21322
|
try {
|
|
20402
|
-
return
|
|
21323
|
+
return path26.join(realpathSync5(dir), rest);
|
|
20403
21324
|
} catch {
|
|
20404
|
-
if (
|
|
20405
|
-
return
|
|
20406
|
-
rest = rest ?
|
|
21325
|
+
if (path26.dirname(dir) === dir)
|
|
21326
|
+
return path26.resolve(file);
|
|
21327
|
+
rest = rest ? path26.join(path26.basename(dir), rest) : path26.basename(dir);
|
|
20407
21328
|
}
|
|
20408
21329
|
}
|
|
20409
21330
|
}, exempt = (cwd, root, file, plans) => {
|
|
20410
21331
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
20411
|
-
const relative =
|
|
20412
|
-
if (relative.startsWith("..") ||
|
|
21332
|
+
const relative = path26.relative(fold(canonical2(root)), fold(canonical2(path26.resolve(cwd, file))));
|
|
21333
|
+
if (relative.startsWith("..") || path26.isAbsolute(relative))
|
|
20413
21334
|
return true;
|
|
20414
|
-
const posix = relative.split(
|
|
21335
|
+
const posix = relative.split(path26.sep).join("/");
|
|
20415
21336
|
const segments = posix.split("/");
|
|
20416
21337
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
20417
|
-
}, PREFIXES2, operands = (words) => words.filter((item) => !item.startsWith("-")),
|
|
21338
|
+
}, PREFIXES2, operands = (words) => words.filter((item) => !item.startsWith("-")), SCRIPT_SHELLS, wrappedScript = (input) => {
|
|
21339
|
+
let words = input;
|
|
21340
|
+
while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
|
|
21341
|
+
words = words.slice(1);
|
|
21342
|
+
const tail = workitCheckTail(words);
|
|
21343
|
+
if (!tail)
|
|
21344
|
+
return null;
|
|
21345
|
+
if ("script" in tail)
|
|
21346
|
+
return tail.script;
|
|
21347
|
+
const [shell, flag, script] = tail.argv;
|
|
21348
|
+
if (SCRIPT_SHELLS.has((shell.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && /^-[a-z]*c$/.test(flag ?? ""))
|
|
21349
|
+
return script ?? null;
|
|
21350
|
+
return tail.argv.map(quoteWord2).join(" ");
|
|
21351
|
+
}, editsOf = (input) => {
|
|
20418
21352
|
let words = input;
|
|
20419
21353
|
while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
|
|
20420
21354
|
words = words.slice(1);
|
|
@@ -20489,10 +21423,18 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20489
21423
|
default:
|
|
20490
21424
|
return [];
|
|
20491
21425
|
}
|
|
20492
|
-
}, shellWrites = (command) => {
|
|
21426
|
+
}, shellWrites = (command, depth = 0) => {
|
|
20493
21427
|
const targets = [];
|
|
20494
21428
|
let unknown = false;
|
|
20495
21429
|
for (const segment of segmentsOf(command)) {
|
|
21430
|
+
const inner = depth < 4 ? wrappedScript(segment.words) : null;
|
|
21431
|
+
if (inner !== null) {
|
|
21432
|
+
const nested = shellWrites(inner, depth + 1);
|
|
21433
|
+
if (nested.writes && nested.targets === null)
|
|
21434
|
+
unknown = true;
|
|
21435
|
+
else if (nested.targets)
|
|
21436
|
+
targets.push(...nested.targets);
|
|
21437
|
+
}
|
|
20496
21438
|
targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
|
|
20497
21439
|
const edits = editsOf(segment.words);
|
|
20498
21440
|
if (edits === null)
|
|
@@ -20503,7 +21445,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20503
21445
|
if (!unknown && targets.length === 0)
|
|
20504
21446
|
return { writes: false, targets: null };
|
|
20505
21447
|
return { writes: true, targets: unknown ? null : targets };
|
|
20506
|
-
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
21448
|
+
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path26.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
|
|
20507
21449
|
const store = new TaskStore(cwd);
|
|
20508
21450
|
const listed = store.listTaskIndex();
|
|
20509
21451
|
if (!listed.ok)
|
|
@@ -20556,19 +21498,19 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20556
21498
|
return [];
|
|
20557
21499
|
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
20558
21500
|
return [];
|
|
20559
|
-
return [
|
|
21501
|
+
return [path26.resolve(cwd, invocation.dir ?? ".")];
|
|
20560
21502
|
});
|
|
20561
21503
|
}, CD2, PUSHD2, POPD2, cdInto = (dir, words) => {
|
|
20562
21504
|
const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
20563
21505
|
if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
|
|
20564
21506
|
return;
|
|
20565
|
-
return target === undefined ?
|
|
20566
|
-
},
|
|
21507
|
+
return target === undefined ? os10.homedir() : path26.resolve(dir, target);
|
|
21508
|
+
}, quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`, segmentTargets = (cwd, command, dialect, depth = 0) => {
|
|
20567
21509
|
const targets = [];
|
|
20568
21510
|
let dir = cwd;
|
|
20569
21511
|
let stack = [];
|
|
20570
21512
|
const groups = [];
|
|
20571
|
-
const at = (file) =>
|
|
21513
|
+
const at = (file) => path26.isAbsolute(file) ? file : dir === undefined ? null : path26.resolve(dir, file);
|
|
20572
21514
|
for (const segment of segmentsOf(command, dialect)) {
|
|
20573
21515
|
if (segment.group === "open") {
|
|
20574
21516
|
groups.push({ dir, stack: [...stack] });
|
|
@@ -20596,6 +21538,9 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20596
21538
|
dir = stack.length ? stack.pop() : undefined;
|
|
20597
21539
|
continue;
|
|
20598
21540
|
}
|
|
21541
|
+
const inner = depth < 4 ? wrappedScript(words) : null;
|
|
21542
|
+
if (inner !== null && dir !== undefined)
|
|
21543
|
+
targets.push(...segmentTargets(dir, inner, "posix", depth + 1));
|
|
20599
21544
|
for (const file of segment.redirects)
|
|
20600
21545
|
if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
|
|
20601
21546
|
const resolved = at(file);
|
|
@@ -20605,15 +21550,15 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20605
21550
|
const edits = editsOf(segment.words);
|
|
20606
21551
|
if (edits === null) {
|
|
20607
21552
|
if (dir !== undefined)
|
|
20608
|
-
targets.push(
|
|
21553
|
+
targets.push(path26.join(dir, DIR_TARGET));
|
|
20609
21554
|
} else
|
|
20610
21555
|
for (const file of edits) {
|
|
20611
21556
|
const resolved = at(file);
|
|
20612
21557
|
if (resolved)
|
|
20613
21558
|
targets.push(resolved);
|
|
20614
21559
|
}
|
|
20615
|
-
if (dir !== undefined && workitInvocations(segment.words.map(
|
|
20616
|
-
targets.push(
|
|
21560
|
+
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), "")))
|
|
21561
|
+
targets.push(path26.join(dir, DIR_TARGET));
|
|
20617
21562
|
}
|
|
20618
21563
|
return targets;
|
|
20619
21564
|
}, overridden = (command, dialect) => {
|
|
@@ -20636,7 +21581,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20636
21581
|
}, guarded = (targets) => {
|
|
20637
21582
|
const seen = new Set;
|
|
20638
21583
|
return targets.filter((target) => {
|
|
20639
|
-
const dir =
|
|
21584
|
+
const dir = path26.dirname(target);
|
|
20640
21585
|
if (seen.has(dir))
|
|
20641
21586
|
return false;
|
|
20642
21587
|
seen.add(dir);
|
|
@@ -20654,7 +21599,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
|
20654
21599
|
}, claimAll = (owner, targets, deps) => {
|
|
20655
21600
|
for (const target of guarded(targets))
|
|
20656
21601
|
claimLiveSession(target, owner, deps);
|
|
20657
|
-
}, liveTargets = (cwd, paths) => paths?.length ? paths.map((file) =>
|
|
21602
|
+
}, liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path26.resolve(cwd, file)) : [path26.join(cwd, DIR_TARGET)], noteOverride = (cwd, owner, command) => {
|
|
20658
21603
|
try {
|
|
20659
21604
|
if (!inWorkitWorkspace(cwd))
|
|
20660
21605
|
return;
|
|
@@ -20678,6 +21623,7 @@ var init_write_gate = __esm(() => {
|
|
|
20678
21623
|
NONE3 = { kind: "none" };
|
|
20679
21624
|
CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
20680
21625
|
PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
21626
|
+
SCRIPT_SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
20681
21627
|
cache2 = new Map;
|
|
20682
21628
|
MUTATING_GIT = new Set([
|
|
20683
21629
|
"add",
|
|
@@ -21090,9 +22036,9 @@ var init_skill_nudge = __esm(() => {
|
|
|
21090
22036
|
|
|
21091
22037
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
21092
22038
|
import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
21093
|
-
import
|
|
21094
|
-
var
|
|
21095
|
-
if (!nonEmpty2(value) || !
|
|
22039
|
+
import path27 from "node:path";
|
|
22040
|
+
var isRecord6 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "", optionalText = (value) => typeof value === "string" ? value : null, existingDirectory = (value) => {
|
|
22041
|
+
if (!nonEmpty2(value) || !path27.isAbsolute(value) || !existsSync7(value))
|
|
21096
22042
|
return null;
|
|
21097
22043
|
try {
|
|
21098
22044
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -21118,7 +22064,7 @@ var isRecord4 = (value) => value !== null && typeof value === "object" && !Array
|
|
|
21118
22064
|
const joined = argv.map(quoteArg).join(" ");
|
|
21119
22065
|
return nonEmpty2(joined) ? joined : null;
|
|
21120
22066
|
}, WRITE_TOOLS, isWriteTool = (name) => typeof name === "string" && WRITE_TOOLS.has(name.toLowerCase().replace(/[_-]/g, "")), PATH_KEYS, PATCH_HEADER, writePaths = (input) => {
|
|
21121
|
-
if (!
|
|
22067
|
+
if (!isRecord6(input))
|
|
21122
22068
|
return [];
|
|
21123
22069
|
const paths = PATH_KEYS.map((key) => input[key]).filter(nonEmpty2);
|
|
21124
22070
|
for (const value of Object.values(input))
|
|
@@ -21200,10 +22146,32 @@ function handleHook(input, deps) {
|
|
|
21200
22146
|
const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE5;
|
|
21201
22147
|
if (gate.kind === "deny")
|
|
21202
22148
|
return gate;
|
|
22149
|
+
const plan = heavyGate(input.cwd, event.command, {
|
|
22150
|
+
host: input.host,
|
|
22151
|
+
dialect: event.dialect,
|
|
22152
|
+
permissionMode: input.permissionMode
|
|
22153
|
+
});
|
|
22154
|
+
if (plan.kind === "split" && canDeny)
|
|
22155
|
+
return splitDenial(plan);
|
|
22156
|
+
const rewrites = usable2(descriptor.shellPolicy.rewrite);
|
|
22157
|
+
if (plan.kind === "rewrite" && !rewrites && canDeny)
|
|
22158
|
+
return heavyDenial(event.command, plan);
|
|
22159
|
+
const lease = plan.kind === "rewrite" && rewrites ? plan : null;
|
|
22160
|
+
const aside = plan.kind === "passthrough" ? plan.note : null;
|
|
21203
22161
|
noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
|
|
22162
|
+
const rewritten = (text) => lease ? {
|
|
22163
|
+
kind: "rewrite",
|
|
22164
|
+
command: lease.command,
|
|
22165
|
+
input: event.toolInput ? { ...event.toolInput, command: lease.command } : null,
|
|
22166
|
+
text
|
|
22167
|
+
} : NONE5;
|
|
21204
22168
|
if (descriptor.transport !== "hook-process")
|
|
21205
|
-
return
|
|
21206
|
-
|
|
22169
|
+
return rewritten(null);
|
|
22170
|
+
const nudged = withContextLine(withContextLine(canDeny ? rawGitPre(input, event.command) : NONE5, aside), shipNudge(input, event.command));
|
|
22171
|
+
if (!lease)
|
|
22172
|
+
return nudged;
|
|
22173
|
+
return rewritten([rewriteNote(lease), nudged.kind === "context" ? nudged.text : null].filter(Boolean).join(`
|
|
22174
|
+
`));
|
|
21207
22175
|
}
|
|
21208
22176
|
case "shell.post":
|
|
21209
22177
|
if (usable2(descriptor.events["shell.post"].support))
|
|
@@ -21289,6 +22257,7 @@ var init_handle = __esm(() => {
|
|
|
21289
22257
|
init_branch();
|
|
21290
22258
|
init_workspaces();
|
|
21291
22259
|
init_host_session();
|
|
22260
|
+
init_heavy_gate();
|
|
21292
22261
|
init_policy();
|
|
21293
22262
|
init_raw_git();
|
|
21294
22263
|
init_skill_nudge();
|
|
@@ -21306,7 +22275,7 @@ var init_handle = __esm(() => {
|
|
|
21306
22275
|
});
|
|
21307
22276
|
|
|
21308
22277
|
// packages/workit-core/src/hooks/hosts/claude-code.ts
|
|
21309
|
-
var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) => value.tool_name === "PowerShell" ? "powershell" : "posix", toolCommand = (value) =>
|
|
22278
|
+
var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) => value.tool_name === "PowerShell" ? "powershell" : "posix", toolCommand = (value) => isRecord6(value.tool_input) ? commandText(value.tool_input.command) : null, eventOf = (name, value) => {
|
|
21310
22279
|
const toolUseId = optionalText(value.tool_use_id);
|
|
21311
22280
|
switch (name) {
|
|
21312
22281
|
case "SessionStart":
|
|
@@ -21333,19 +22302,28 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) =
|
|
|
21333
22302
|
kind: "tool.pre",
|
|
21334
22303
|
tool: value.tool_name,
|
|
21335
22304
|
toolUseId,
|
|
21336
|
-
skill:
|
|
22305
|
+
skill: isRecord6(value.tool_input) ? optionalText(value.tool_input.skill) : null
|
|
21337
22306
|
}
|
|
21338
22307
|
};
|
|
21339
22308
|
if (!SHELL_TOOLS.has(value.tool_name))
|
|
21340
22309
|
return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
|
|
21341
22310
|
const command = toolCommand(value);
|
|
21342
|
-
return command ? {
|
|
22311
|
+
return command ? {
|
|
22312
|
+
ok: true,
|
|
22313
|
+
event: {
|
|
22314
|
+
kind: "shell.pre",
|
|
22315
|
+
command,
|
|
22316
|
+
toolUseId,
|
|
22317
|
+
dialect: dialectOf2(value),
|
|
22318
|
+
toolInput: value.tool_input
|
|
22319
|
+
}
|
|
22320
|
+
} : { ok: false, error: `tool_input.command is required for ${value.tool_name}` };
|
|
21343
22321
|
}
|
|
21344
22322
|
case "PostToolUse": {
|
|
21345
22323
|
const command = toolCommand(value);
|
|
21346
22324
|
if (!SHELL_TOOLS.has(String(value.tool_name)) || !command)
|
|
21347
22325
|
return { ok: false, error: "only shell PostToolUse is mapped" };
|
|
21348
|
-
const response =
|
|
22326
|
+
const response = isRecord6(value.tool_response) ? value.tool_response : {};
|
|
21349
22327
|
return {
|
|
21350
22328
|
ok: true,
|
|
21351
22329
|
event: {
|
|
@@ -21395,6 +22373,17 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) =
|
|
|
21395
22373
|
};
|
|
21396
22374
|
}
|
|
21397
22375
|
}, CONTEXT_EVENTS, render = (decision, native) => {
|
|
22376
|
+
if (decision.kind === "rewrite" && native === "PreToolUse" && decision.input)
|
|
22377
|
+
return {
|
|
22378
|
+
json: {
|
|
22379
|
+
hookSpecificOutput: {
|
|
22380
|
+
hookEventName: "PreToolUse",
|
|
22381
|
+
updatedInput: decision.input,
|
|
22382
|
+
...decision.text ? { additionalContext: decision.text } : {}
|
|
22383
|
+
}
|
|
22384
|
+
},
|
|
22385
|
+
exitCode: 0
|
|
22386
|
+
};
|
|
21398
22387
|
if (decision.kind === "deny" && native === "PreToolUse")
|
|
21399
22388
|
return {
|
|
21400
22389
|
json: {
|
|
@@ -21438,7 +22427,12 @@ var init_claude_code = __esm(() => {
|
|
|
21438
22427
|
"compact.pre": { support: "none", native: null },
|
|
21439
22428
|
stop: { support: "native", native: "Stop" }
|
|
21440
22429
|
},
|
|
21441
|
-
shellPolicy: {
|
|
22430
|
+
shellPolicy: {
|
|
22431
|
+
deny: "native",
|
|
22432
|
+
channel: "permissionDecision",
|
|
22433
|
+
failClosed: false,
|
|
22434
|
+
rewrite: "native"
|
|
22435
|
+
},
|
|
21442
22436
|
context: {
|
|
21443
22437
|
sessionStart: "native",
|
|
21444
22438
|
perTurn: "native",
|
|
@@ -21529,14 +22523,14 @@ var init_claude_code = __esm(() => {
|
|
|
21529
22523
|
claudeCodeAdapter = {
|
|
21530
22524
|
descriptor: CLAUDE_CODE_DESCRIPTOR,
|
|
21531
22525
|
parse(raw) {
|
|
21532
|
-
const native =
|
|
22526
|
+
const native = isRecord6(raw) && Object.hasOwn(EVENTS, String(raw.hook_event_name)) ? raw.hook_event_name : null;
|
|
21533
22527
|
const fail = (error) => ({
|
|
21534
22528
|
ok: false,
|
|
21535
22529
|
error,
|
|
21536
22530
|
native,
|
|
21537
22531
|
event: native ? EVENTS[native] : null
|
|
21538
22532
|
});
|
|
21539
|
-
if (!
|
|
22533
|
+
if (!isRecord6(raw) || !native)
|
|
21540
22534
|
return fail("hook_event_name is required");
|
|
21541
22535
|
if (!nonEmpty2(raw.session_id))
|
|
21542
22536
|
return fail("session_id is required");
|
|
@@ -21573,7 +22567,7 @@ function detectCodexSurface(env) {
|
|
|
21573
22567
|
return env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || Boolean(env.CODEX_ELECTRON_RESOURCES_PATH) ? "codex_desktop" : "codex_cli";
|
|
21574
22568
|
}
|
|
21575
22569
|
var CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name) => SHELL_TOOLS2.has(String(name).toLowerCase()), SOURCES2, parseCodexHookInput = (value) => {
|
|
21576
|
-
if (!
|
|
22570
|
+
if (!isRecord6(value) || !Object.hasOwn(EVENTS2, String(value.hook_event_name)))
|
|
21577
22571
|
return { ok: false, error: "hook_event_name is required" };
|
|
21578
22572
|
const event = value.hook_event_name;
|
|
21579
22573
|
const cwd = existingDirectory(value.cwd);
|
|
@@ -21582,7 +22576,7 @@ var CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name) => SHELL_TOOLS
|
|
|
21582
22576
|
const toolEvent = event === "PreToolUse" || event === "PostToolUse";
|
|
21583
22577
|
if (toolEvent && !nonEmpty2(value.tool_name))
|
|
21584
22578
|
return { ok: false, error: "tool_name is required" };
|
|
21585
|
-
const command =
|
|
22579
|
+
const command = isRecord6(value.tool_input) ? commandText(value.tool_input.command) : null;
|
|
21586
22580
|
if (toolEvent && isShellTool(value.tool_name) && !command)
|
|
21587
22581
|
return { ok: false, error: "tool_input.command is required for shell tools" };
|
|
21588
22582
|
if (event === "SubagentStart" && !nonEmpty2(value.agent_id))
|
|
@@ -21708,7 +22702,12 @@ var init_codex = __esm(() => {
|
|
|
21708
22702
|
"compact.pre": { support: "none", native: null },
|
|
21709
22703
|
stop: { support: "native", native: "Stop" }
|
|
21710
22704
|
},
|
|
21711
|
-
shellPolicy: {
|
|
22705
|
+
shellPolicy: {
|
|
22706
|
+
deny: "native",
|
|
22707
|
+
channel: "permissionDecision",
|
|
22708
|
+
failClosed: false,
|
|
22709
|
+
rewrite: "none"
|
|
22710
|
+
},
|
|
21712
22711
|
context: {
|
|
21713
22712
|
sessionStart: "native",
|
|
21714
22713
|
perTurn: "native",
|
|
@@ -21804,7 +22803,7 @@ var init_codex = __esm(() => {
|
|
|
21804
22803
|
const host = detectCodexSurface(env);
|
|
21805
22804
|
const parsed = parseCodexHookInput(raw);
|
|
21806
22805
|
if (!parsed.ok) {
|
|
21807
|
-
const native =
|
|
22806
|
+
const native = isRecord6(raw) && Object.hasOwn(EVENTS2, String(raw.hook_event_name)) ? raw.hook_event_name : null;
|
|
21808
22807
|
return { ok: false, error: parsed.error, native, event: native ? EVENTS2[native] : null };
|
|
21809
22808
|
}
|
|
21810
22809
|
const input = parsed.data;
|
|
@@ -21832,10 +22831,10 @@ var init_codex = __esm(() => {
|
|
|
21832
22831
|
});
|
|
21833
22832
|
|
|
21834
22833
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
21835
|
-
import
|
|
21836
|
-
import
|
|
22834
|
+
import fs21 from "node:fs";
|
|
22835
|
+
import path28 from "node:path";
|
|
21837
22836
|
var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
|
|
21838
|
-
if (!
|
|
22837
|
+
if (!isRecord6(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
|
|
21839
22838
|
return { ok: false, error: "hook_event_name is required" };
|
|
21840
22839
|
const roots = value.workspace_roots;
|
|
21841
22840
|
if (!Array.isArray(roots) || roots.length === 0 || !roots.every(nonEmpty2))
|
|
@@ -21914,17 +22913,17 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
21914
22913
|
};
|
|
21915
22914
|
}
|
|
21916
22915
|
}, TRANSCRIPT_TAIL_BYTES, lastAssistantText = (file) => {
|
|
21917
|
-
if (!file || !
|
|
22916
|
+
if (!file || !path28.isAbsolute(file))
|
|
21918
22917
|
return null;
|
|
21919
22918
|
try {
|
|
21920
|
-
const size =
|
|
22919
|
+
const size = fs21.statSync(file).size;
|
|
21921
22920
|
const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
|
|
21922
22921
|
const buffer = Buffer.alloc(size - start);
|
|
21923
|
-
const fd =
|
|
22922
|
+
const fd = fs21.openSync(file, "r");
|
|
21924
22923
|
try {
|
|
21925
|
-
|
|
22924
|
+
fs21.readSync(fd, buffer, 0, buffer.length, start);
|
|
21926
22925
|
} finally {
|
|
21927
|
-
|
|
22926
|
+
fs21.closeSync(fd);
|
|
21928
22927
|
}
|
|
21929
22928
|
const lines = buffer.toString("utf8").split(`
|
|
21930
22929
|
`);
|
|
@@ -21935,14 +22934,14 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
21935
22934
|
} catch {
|
|
21936
22935
|
continue;
|
|
21937
22936
|
}
|
|
21938
|
-
if (!
|
|
22937
|
+
if (!isRecord6(entry) || entry.role !== "assistant")
|
|
21939
22938
|
continue;
|
|
21940
|
-
const content =
|
|
22939
|
+
const content = isRecord6(entry.message) ? entry.message.content : entry.content;
|
|
21941
22940
|
if (typeof content === "string")
|
|
21942
22941
|
return content;
|
|
21943
22942
|
if (!Array.isArray(content))
|
|
21944
22943
|
return null;
|
|
21945
|
-
const text = content.filter((part) =>
|
|
22944
|
+
const text = content.filter((part) => isRecord6(part) && part.type === "text" && typeof part.text === "string").map((part) => part.text).join(`
|
|
21946
22945
|
`);
|
|
21947
22946
|
return text || null;
|
|
21948
22947
|
}
|
|
@@ -21950,7 +22949,7 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
21950
22949
|
} catch {
|
|
21951
22950
|
return null;
|
|
21952
22951
|
}
|
|
21953
|
-
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
22952
|
+
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path28.sep) ? root : `${root}${path28.sep}`), hookCwd = (input) => {
|
|
21954
22953
|
const cwd = existingDirectory(input.cwd);
|
|
21955
22954
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
|
21956
22955
|
return cwd;
|
|
@@ -22004,7 +23003,7 @@ var init_cursor = __esm(() => {
|
|
|
22004
23003
|
"compact.pre": { support: "partial", native: "preCompact" },
|
|
22005
23004
|
stop: { support: "native", native: "stop" }
|
|
22006
23005
|
},
|
|
22007
|
-
shellPolicy: { deny: "native", channel: "exit2+json", failClosed: false },
|
|
23006
|
+
shellPolicy: { deny: "native", channel: "exit2+json", failClosed: false, rewrite: "none" },
|
|
22008
23007
|
context: {
|
|
22009
23008
|
sessionStart: "native",
|
|
22010
23009
|
perTurn: "none",
|
|
@@ -22103,7 +23102,7 @@ var init_cursor = __esm(() => {
|
|
|
22103
23102
|
parse(raw) {
|
|
22104
23103
|
const parsed = parseCursorHookInput(raw);
|
|
22105
23104
|
if (!parsed.ok) {
|
|
22106
|
-
const native =
|
|
23105
|
+
const native = isRecord6(raw) && Object.hasOwn(EVENTS3, String(raw.hook_event_name)) ? raw.hook_event_name : null;
|
|
22107
23106
|
return { ok: false, error: parsed.error, native, event: native ? EVENTS3[native] : null };
|
|
22108
23107
|
}
|
|
22109
23108
|
const input = parsed.data;
|
|
@@ -22175,6 +23174,7 @@ var init_pi = () => {};
|
|
|
22175
23174
|
var init_hooks = __esm(async () => {
|
|
22176
23175
|
init_context();
|
|
22177
23176
|
init_policy();
|
|
23177
|
+
init_heavy_gate();
|
|
22178
23178
|
init_write_gate();
|
|
22179
23179
|
init_live_session();
|
|
22180
23180
|
init_stop();
|