@brainervirus/workit-cursor 8.16.0 → 8.17.1

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.
@@ -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 = path16.join(root, CHECKS_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",
@@ -18749,7 +18760,16 @@ function rawInvocations(command, dialect = "posix", depth = 0) {
18749
18760
  RECENT.delete(RECENT.keys().next().value);
18750
18761
  return found;
18751
18762
  }
18752
- function walk2(command, dialect, depth) {
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) {
18753
18773
  const found = [];
18754
18774
  let dir = null;
18755
18775
  const groups = [];
@@ -18760,7 +18780,8 @@ function walk2(command, dialect, depth) {
18760
18780
  const nested = (script, inner) => {
18761
18781
  if (!script || depth >= MAX_DEPTH)
18762
18782
  return;
18763
- for (const invocation of rawInvocations(script, inner, depth + 1))
18783
+ const inside = seen ? walk2(script, inner, depth + 1, seen) : rawInvocations(script, inner, depth + 1);
18784
+ for (const invocation of inside)
18764
18785
  found.push({ ...invocation, dir: rebase(dir, invocation.dir) });
18765
18786
  };
18766
18787
  const visit = (input, stdin, hops) => {
@@ -18772,11 +18793,17 @@ function walk2(command, dialect, depth) {
18772
18793
  const name = commandName(words[0]);
18773
18794
  if (!name)
18774
18795
  return;
18796
+ seen?.push(words);
18797
+ if (name === "-c" || name === "--command")
18798
+ return nested(words[1], "posix");
18775
18799
  const runs = (lines) => {
18776
18800
  if (hops < MAX_DEPTH)
18777
18801
  for (const next of lines)
18778
18802
  visit(next, undefined, hops + 1);
18779
18803
  };
18804
+ const wrapped = seen ? null : workitCheckTail(words);
18805
+ if (wrapped)
18806
+ return "script" in wrapped ? nested(wrapped.script, "posix") : runs([wrapped.argv]);
18780
18807
  if (CD.has(name)) {
18781
18808
  const target = cdTarget(words);
18782
18809
  dir = target === null ? os5.homedir() : joinDir(dir, target);
@@ -18853,6 +18880,49 @@ function walk2(command, dialect, depth) {
18853
18880
  }
18854
18881
  return found;
18855
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
+ }
18856
18926
  function workitInvocations(command, dialect = "posix", depth = 0) {
18857
18927
  const found = [];
18858
18928
  for (const { words } of segmentsOf(command, dialect)) {
@@ -19269,7 +19339,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
19269
19339
  ...Object.keys(env).length ? { env } : {},
19270
19340
  ...stdin === undefined ? {} : { stdin }
19271
19341
  };
19272
- }, 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) => {
19273
19343
  if (destination === "HEAD" || destination === "@" || CURRENT_BRANCH.test(destination))
19274
19344
  return { name: null };
19275
19345
  if (destination.startsWith("refs/heads/"))
@@ -19961,6 +20031,8 @@ var init_raw_git = __esm(() => {
19961
20031
  "command"
19962
20032
  ]);
19963
20033
  WORKIT_BIN = /(?:^|[\\/])workit(?:\.exe)?$|^@brainervirus\/workit-cli$/;
20034
+ WORKIT_PACKAGE = /^@brainervirus\/workit-cli(?:@\S*)?$/;
20035
+ PACKAGE_RUNNERS = new Set(["npx", "bunx", "pnpx"]);
19964
20036
  PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
19965
20037
  CURRENT_BRANCH = /^\$\(\s*git\s+(?:branch\s+--show-current|rev-parse\s+--abbrev-ref\s+HEAD|symbolic-ref\s+--short\s+HEAD)\s*\)$/;
19966
20038
  VALUE_OPTIONS = {
@@ -20200,12 +20272,808 @@ var init_policy = __esm(() => {
20200
20272
  init_shell_words();
20201
20273
  });
20202
20274
 
20203
- // packages/workit-core/src/core/live-session.ts
20204
- import { spawnSync as spawnSync13 } from "node:child_process";
20205
- import { createHash as createHash15 } from "node:crypto";
20275
+ // packages/workit-core/src/core/heavy-rules.ts
20206
20276
  import fs17 from "node:fs";
20207
20277
  import os6 from "node:os";
20208
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";
20209
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) => {
20210
21078
  try {
20211
21079
  process.kill(pid, 0);
@@ -20216,9 +21084,9 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
20216
21084
  }, processInfo = (pid) => {
20217
21085
  try {
20218
21086
  if (liveProbe.platform === "linux") {
20219
- const stat = fs17.readFileSync(`/proc/${pid}/stat`, "utf8");
21087
+ const stat = fs20.readFileSync(`/proc/${pid}/stat`, "utf8");
20220
21088
  const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
20221
- const command = fs17.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
21089
+ const command = fs20.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
20222
21090
  return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
20223
21091
  }
20224
21092
  if (liveProbe.platform === "win32")
@@ -20263,32 +21131,32 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
20263
21131
  pid: () => hostPid(transport)
20264
21132
  };
20265
21133
  }, checkoutOf = (target) => {
20266
- let dir = path22.resolve(target);
20267
- while (!fs17.existsSync(dir) && path22.dirname(dir) !== dir)
20268
- dir = path22.dirname(dir);
20269
- if (fs17.existsSync(dir) && !fs17.statSync(dir).isDirectory())
20270
- dir = path22.dirname(dir);
20271
- const parts = dir.split(path22.sep);
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);
20272
21140
  const dotGit = parts.lastIndexOf(".git");
20273
21141
  if (dotGit > 0)
20274
- dir = parts.slice(0, dotGit).join(path22.sep) || path22.sep;
21142
+ dir = parts.slice(0, dotGit).join(path25.sep) || path25.sep;
20275
21143
  const location = resolveStore(dir);
20276
21144
  if (location instanceof Error || !location.shared || !location.top)
20277
21145
  return null;
20278
21146
  let top = location.top;
20279
21147
  try {
20280
- top = fs17.realpathSync.native(top);
21148
+ top = fs20.realpathSync.native(top);
20281
21149
  } catch {}
20282
21150
  return top;
20283
- }, markerFile2 = (top) => path22.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`), checkoutKey = (top, platform = process.platform) => {
20284
- let key = path22.resolve(top);
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);
20285
21153
  try {
20286
- key = fs17.realpathSync.native(key);
21154
+ key = fs20.realpathSync.native(key);
20287
21155
  } catch {}
20288
21156
  return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
20289
21157
  }, readMarker = (file) => {
20290
21158
  try {
20291
- const value = JSON.parse(fs17.readFileSync(file, "utf8"));
21159
+ const value = JSON.parse(fs20.readFileSync(file, "utf8"));
20292
21160
  if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
20293
21161
  return null;
20294
21162
  return {
@@ -20309,7 +21177,7 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
20309
21177
  const age = ageOf2(marker, deps.now ?? new Date);
20310
21178
  if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
20311
21179
  return false;
20312
- if (marker.machine !== (deps.machine ?? os6.hostname()) || marker.pid === null)
21180
+ if (marker.machine !== (deps.machine ?? os9.hostname()) || marker.pid === null)
20313
21181
  return true;
20314
21182
  if (!(deps.alive ?? processAlive)(marker.pid))
20315
21183
  return false;
@@ -20335,7 +21203,7 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
20335
21203
  const marker = readMarker(file);
20336
21204
  if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
20337
21205
  return null;
20338
- if (sameOwner(marker, owner, deps.machine ?? os6.hostname()))
21206
+ if (sameOwner(marker, owner, deps.machine ?? os9.hostname()))
20339
21207
  return null;
20340
21208
  return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
20341
21209
  } catch {
@@ -20348,7 +21216,7 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
20348
21216
  return;
20349
21217
  const file = markerFile2(checkout);
20350
21218
  const now = deps.now ?? new Date;
20351
- const machine = deps.machine ?? os6.hostname();
21219
+ const machine = deps.machine ?? os9.hostname();
20352
21220
  const current = readMarker(file);
20353
21221
  if (current) {
20354
21222
  const age = ageOf2(current, now);
@@ -20369,10 +21237,10 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
20369
21237
  checkout,
20370
21238
  heartbeatAt: now.toISOString()
20371
21239
  };
20372
- fs17.mkdirSync(path22.dirname(file), { recursive: true });
21240
+ fs20.mkdirSync(path25.dirname(file), { recursive: true });
20373
21241
  const temp = `${file}.${process.pid}.tmp`;
20374
- fs17.writeFileSync(temp, JSON.stringify(marker));
20375
- fs17.renameSync(temp, file);
21242
+ fs20.writeFileSync(temp, JSON.stringify(marker));
21243
+ fs20.renameSync(temp, file);
20376
21244
  } catch {}
20377
21245
  };
20378
21246
  var init_live_session = __esm(() => {
@@ -20391,8 +21259,8 @@ var init_live_session = __esm(() => {
20391
21259
 
20392
21260
  // packages/workit-core/src/hooks/write-gate.ts
20393
21261
  import { realpathSync as realpathSync5 } from "node:fs";
20394
- import os7 from "node:os";
20395
- import path23 from "node:path";
21262
+ import os10 from "node:os";
21263
+ import path26 from "node:path";
20396
21264
  function writeGate(cwd, paths, owner, deps) {
20397
21265
  if (!owner)
20398
21266
  return beforeWriteGate(cwd, paths);
@@ -20412,7 +21280,7 @@ function shellGate(cwd, command, owner, dialect = "posix", deps) {
20412
21280
  try {
20413
21281
  targets = [
20414
21282
  ...segmentTargets(cwd, command, dialect),
20415
- ...gitMutationDirs(cwd, command, dialect).map((dir) => path23.join(dir, DIR_TARGET))
21283
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path26.join(dir, DIR_TARGET))
20416
21284
  ];
20417
21285
  } catch {
20418
21286
  targets = [];
@@ -20450,24 +21318,37 @@ function beforeWriteGate(cwd, paths) {
20450
21318
  }
20451
21319
  var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
20452
21320
  let rest = "";
20453
- for (let dir = path23.resolve(file);; dir = path23.dirname(dir)) {
21321
+ for (let dir = path26.resolve(file);; dir = path26.dirname(dir)) {
20454
21322
  try {
20455
- return path23.join(realpathSync5(dir), rest);
21323
+ return path26.join(realpathSync5(dir), rest);
20456
21324
  } catch {
20457
- if (path23.dirname(dir) === dir)
20458
- return path23.resolve(file);
20459
- rest = rest ? path23.join(path23.basename(dir), rest) : path23.basename(dir);
21325
+ if (path26.dirname(dir) === dir)
21326
+ return path26.resolve(file);
21327
+ rest = rest ? path26.join(path26.basename(dir), rest) : path26.basename(dir);
20460
21328
  }
20461
21329
  }
20462
21330
  }, exempt = (cwd, root, file, plans) => {
20463
21331
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
20464
- const relative = path23.relative(fold(canonical2(root)), fold(canonical2(path23.resolve(cwd, file))));
20465
- if (relative.startsWith("..") || path23.isAbsolute(relative))
21332
+ const relative = path26.relative(fold(canonical2(root)), fold(canonical2(path26.resolve(cwd, file))));
21333
+ if (relative.startsWith("..") || path26.isAbsolute(relative))
20466
21334
  return true;
20467
- const posix = relative.split(path23.sep).join("/");
21335
+ const posix = relative.split(path26.sep).join("/");
20468
21336
  const segments = posix.split("/");
20469
21337
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
20470
- }, PREFIXES2, operands = (words) => words.filter((item) => !item.startsWith("-")), editsOf = (input) => {
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) => {
20471
21352
  let words = input;
20472
21353
  while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
20473
21354
  words = words.slice(1);
@@ -20542,10 +21423,18 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
20542
21423
  default:
20543
21424
  return [];
20544
21425
  }
20545
- }, shellWrites = (command) => {
21426
+ }, shellWrites = (command, depth = 0) => {
20546
21427
  const targets = [];
20547
21428
  let unknown = false;
20548
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
+ }
20549
21438
  targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
20550
21439
  const edits = editsOf(segment.words);
20551
21440
  if (edits === null)
@@ -20556,7 +21445,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
20556
21445
  if (!unknown && targets.length === 0)
20557
21446
  return { writes: false, targets: null };
20558
21447
  return { writes: true, targets: unknown ? null : targets };
20559
- }, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path23.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
21448
+ }, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path26.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
20560
21449
  const store = new TaskStore(cwd);
20561
21450
  const listed = store.listTaskIndex();
20562
21451
  if (!listed.ok)
@@ -20609,19 +21498,19 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
20609
21498
  return [];
20610
21499
  if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
20611
21500
  return [];
20612
- return [path23.resolve(cwd, invocation.dir ?? ".")];
21501
+ return [path26.resolve(cwd, invocation.dir ?? ".")];
20613
21502
  });
20614
21503
  }, CD2, PUSHD2, POPD2, cdInto = (dir, words) => {
20615
21504
  const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
20616
21505
  if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
20617
21506
  return;
20618
- return target === undefined ? os7.homedir() : path23.resolve(dir, target);
20619
- }, quoteWord = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`, segmentTargets = (cwd, command, dialect) => {
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) => {
20620
21509
  const targets = [];
20621
21510
  let dir = cwd;
20622
21511
  let stack = [];
20623
21512
  const groups = [];
20624
- const at = (file) => path23.isAbsolute(file) ? file : dir === undefined ? null : path23.resolve(dir, file);
21513
+ const at = (file) => path26.isAbsolute(file) ? file : dir === undefined ? null : path26.resolve(dir, file);
20625
21514
  for (const segment of segmentsOf(command, dialect)) {
20626
21515
  if (segment.group === "open") {
20627
21516
  groups.push({ dir, stack: [...stack] });
@@ -20649,6 +21538,9 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
20649
21538
  dir = stack.length ? stack.pop() : undefined;
20650
21539
  continue;
20651
21540
  }
21541
+ const inner = depth < 4 ? wrappedScript(words) : null;
21542
+ if (inner !== null && dir !== undefined)
21543
+ targets.push(...segmentTargets(dir, inner, "posix", depth + 1));
20652
21544
  for (const file of segment.redirects)
20653
21545
  if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
20654
21546
  const resolved = at(file);
@@ -20658,15 +21550,15 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
20658
21550
  const edits = editsOf(segment.words);
20659
21551
  if (edits === null) {
20660
21552
  if (dir !== undefined)
20661
- targets.push(path23.join(dir, DIR_TARGET));
21553
+ targets.push(path26.join(dir, DIR_TARGET));
20662
21554
  } else
20663
21555
  for (const file of edits) {
20664
21556
  const resolved = at(file);
20665
21557
  if (resolved)
20666
21558
  targets.push(resolved);
20667
21559
  }
20668
- if (dir !== undefined && workitInvocations(segment.words.map(quoteWord).join(" "), "posix").some((args) => args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), "")))
20669
- targets.push(path23.join(dir, DIR_TARGET));
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));
20670
21562
  }
20671
21563
  return targets;
20672
21564
  }, overridden = (command, dialect) => {
@@ -20689,7 +21581,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
20689
21581
  }, guarded = (targets) => {
20690
21582
  const seen = new Set;
20691
21583
  return targets.filter((target) => {
20692
- const dir = path23.dirname(target);
21584
+ const dir = path26.dirname(target);
20693
21585
  if (seen.has(dir))
20694
21586
  return false;
20695
21587
  seen.add(dir);
@@ -20707,7 +21599,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
20707
21599
  }, claimAll = (owner, targets, deps) => {
20708
21600
  for (const target of guarded(targets))
20709
21601
  claimLiveSession(target, owner, deps);
20710
- }, liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path23.resolve(cwd, file)) : [path23.join(cwd, DIR_TARGET)], noteOverride = (cwd, owner, command) => {
21602
+ }, liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path26.resolve(cwd, file)) : [path26.join(cwd, DIR_TARGET)], noteOverride = (cwd, owner, command) => {
20711
21603
  try {
20712
21604
  if (!inWorkitWorkspace(cwd))
20713
21605
  return;
@@ -20731,6 +21623,7 @@ var init_write_gate = __esm(() => {
20731
21623
  NONE3 = { kind: "none" };
20732
21624
  CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
20733
21625
  PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
21626
+ SCRIPT_SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
20734
21627
  cache2 = new Map;
20735
21628
  MUTATING_GIT = new Set([
20736
21629
  "add",
@@ -21143,9 +22036,9 @@ var init_skill_nudge = __esm(() => {
21143
22036
 
21144
22037
  // packages/workit-core/src/hooks/hosts/fields.ts
21145
22038
  import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
21146
- import path24 from "node:path";
21147
- var isRecord4 = (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) => {
21148
- if (!nonEmpty2(value) || !path24.isAbsolute(value) || !existsSync7(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))
21149
22042
  return null;
21150
22043
  try {
21151
22044
  return statSync2(value).isDirectory() ? realpathSync6(value) : null;
@@ -21171,7 +22064,7 @@ var isRecord4 = (value) => value !== null && typeof value === "object" && !Array
21171
22064
  const joined = argv.map(quoteArg).join(" ");
21172
22065
  return nonEmpty2(joined) ? joined : null;
21173
22066
  }, WRITE_TOOLS, isWriteTool = (name) => typeof name === "string" && WRITE_TOOLS.has(name.toLowerCase().replace(/[_-]/g, "")), PATH_KEYS, PATCH_HEADER, writePaths = (input) => {
21174
- if (!isRecord4(input))
22067
+ if (!isRecord6(input))
21175
22068
  return [];
21176
22069
  const paths = PATH_KEYS.map((key) => input[key]).filter(nonEmpty2);
21177
22070
  for (const value of Object.values(input))
@@ -21253,10 +22146,32 @@ function handleHook(input, deps) {
21253
22146
  const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE5;
21254
22147
  if (gate.kind === "deny")
21255
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;
21256
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;
21257
22168
  if (descriptor.transport !== "hook-process")
21258
- return NONE5;
21259
- return withContextLine(canDeny ? rawGitPre(input, event.command) : NONE5, shipNudge(input, event.command));
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
+ `));
21260
22175
  }
21261
22176
  case "shell.post":
21262
22177
  if (usable2(descriptor.events["shell.post"].support))
@@ -21342,6 +22257,7 @@ var init_handle = __esm(() => {
21342
22257
  init_branch();
21343
22258
  init_workspaces();
21344
22259
  init_host_session();
22260
+ init_heavy_gate();
21345
22261
  init_policy();
21346
22262
  init_raw_git();
21347
22263
  init_skill_nudge();
@@ -21358,8 +22274,104 @@ var init_handle = __esm(() => {
21358
22274
  CLAUDE_WORKTREE = /[\\/]\.claude[\\/]worktrees[\\/]/;
21359
22275
  });
21360
22276
 
22277
+ // packages/workit-core/src/hooks/deadline.ts
22278
+ import { appendFileSync, mkdirSync as mkdirSync3 } from "node:fs";
22279
+ import path28 from "node:path";
22280
+ var HOOK_SKIP_TEXT = "workit context skipped: host under load", HOOK_DEADLINE_MS = 8000, HOST_DEADLINES, HOOK_SKIPS_FILE = "hook-skips.jsonl", override = (env) => {
22281
+ const value = Number(env.WORKIT_HOOK_DEADLINE_MS);
22282
+ return Number.isFinite(value) && value > 0 ? value : null;
22283
+ }, hookDeadlineMs = (env, host = "", native) => {
22284
+ const row = HOST_DEADLINES[host];
22285
+ return override(env) ?? (row ? native === undefined ? Math.min(...Object.values(row)) : row[native] ?? row[""] : HOOK_DEADLINE_MS);
22286
+ }, skipsFile = (stateDir) => path28.join(stateDir, HOOK_SKIPS_FILE), recordHookSkip = (skip, stateDir = resolveStateDir()) => {
22287
+ try {
22288
+ mkdirSync3(stateDir, { recursive: true });
22289
+ appendFileSync(skipsFile(stateDir), `${JSON.stringify(skip)}
22290
+ `);
22291
+ } catch {}
22292
+ }, QUIET, skipDecision = (event) => QUIET.has(event) ? { kind: "none" } : { kind: "context", text: HOOK_SKIP_TEXT }, TIMED_OUT, within = async (work, ms) => {
22293
+ let timer;
22294
+ const expired = new Promise((resolve) => {
22295
+ timer = setTimeout(() => resolve(TIMED_OUT), Math.max(0, ms));
22296
+ timer.unref?.();
22297
+ });
22298
+ try {
22299
+ return await Promise.race([work, expired]);
22300
+ } finally {
22301
+ clearTimeout(timer);
22302
+ }
22303
+ }, isTimedOut = (value) => value === TIMED_OUT, PROCESS_START, hookBudget = (env, options = {}, host = "") => {
22304
+ const now = options.now ?? Date.now;
22305
+ return {
22306
+ now,
22307
+ record: options.record ?? ((skip) => recordHookSkip(skip)),
22308
+ startedAt: options.startedAt ?? now(),
22309
+ deadlineMs: options.deadlineMs ?? hookDeadlineMs(env, host),
22310
+ host: options.deadlineMs === undefined ? host : null
22311
+ };
22312
+ }, readWithin = async (stdin, budget) => {
22313
+ const read = (async () => {
22314
+ let text = "";
22315
+ for await (const chunk of stdin)
22316
+ text += String(chunk);
22317
+ return text;
22318
+ })();
22319
+ const text = await within(read, budget.startedAt + budget.deadlineMs - budget.now());
22320
+ return isTimedOut(text) ? null : text;
22321
+ }, note = (adapter, raw, env, budget, outcome, stage) => {
22322
+ const parsed = raw === undefined ? null : adapter.parse(raw, env);
22323
+ const event = parsed ? parsed.ok ? parsed.input.event.kind : parsed.event : null;
22324
+ budget.record({
22325
+ at: new Date(budget.now()).toISOString(),
22326
+ host: adapter.descriptor.host,
22327
+ event,
22328
+ outcome,
22329
+ stage,
22330
+ elapsedMs: Math.round(budget.now() - budget.startedAt)
22331
+ });
22332
+ return { native: parsed?.native ?? null, event };
22333
+ }, skippedHook = (adapter, raw, env, budget, stage) => {
22334
+ const { native, event } = note(adapter, raw, env, budget, "skip", stage);
22335
+ return {
22336
+ ...adapter.render(skipDecision(event), native),
22337
+ error: `${HOOK_SKIP_TEXT} (past the ${budget.deadlineMs} ms hook deadline)`,
22338
+ skipped: true
22339
+ };
22340
+ }, dispatchWithin = async (adapter, raw, env, budget, dispatch = dispatchHook) => {
22341
+ const native = raw?.hook_event_name;
22342
+ if (budget.host !== null && typeof native === "string")
22343
+ budget.deadlineMs = hookDeadlineMs(env, budget.host, native);
22344
+ const remaining = () => budget.startedAt + budget.deadlineMs - budget.now();
22345
+ const result = remaining() > 0 ? await within(Promise.resolve().then(() => dispatch(adapter, raw, env)), remaining()) : TIMED_OUT;
22346
+ if (isTimedOut(result))
22347
+ return skippedHook(adapter, raw, env, budget, "dispatch");
22348
+ if (remaining() < 0)
22349
+ note(adapter, raw, env, budget, "late", "done");
22350
+ return { ...result, skipped: false };
22351
+ };
22352
+ var init_deadline = __esm(() => {
22353
+ init_logger();
22354
+ init_handle();
22355
+ HOST_DEADLINES = {
22356
+ claude_code: { "": 8000, SessionStart: 16000, Stop: 16000 },
22357
+ codex_cli: { "": 45000, Stop: 16000 },
22358
+ codex_desktop: { "": 45000, Stop: 16000 },
22359
+ cursor: { "": 45000 }
22360
+ };
22361
+ QUIET = new Set([
22362
+ "stop",
22363
+ "subagent.stop",
22364
+ "shell.post",
22365
+ "compact.pre",
22366
+ "prompt.submit",
22367
+ null
22368
+ ]);
22369
+ TIMED_OUT = Symbol("timed out");
22370
+ PROCESS_START = performance.timeOrigin;
22371
+ });
22372
+
21361
22373
  // packages/workit-core/src/hooks/hosts/claude-code.ts
21362
- var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) => value.tool_name === "PowerShell" ? "powershell" : "posix", toolCommand = (value) => isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null, eventOf = (name, value) => {
22374
+ 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) => {
21363
22375
  const toolUseId = optionalText(value.tool_use_id);
21364
22376
  switch (name) {
21365
22377
  case "SessionStart":
@@ -21386,19 +22398,28 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) =
21386
22398
  kind: "tool.pre",
21387
22399
  tool: value.tool_name,
21388
22400
  toolUseId,
21389
- skill: isRecord4(value.tool_input) ? optionalText(value.tool_input.skill) : null
22401
+ skill: isRecord6(value.tool_input) ? optionalText(value.tool_input.skill) : null
21390
22402
  }
21391
22403
  };
21392
22404
  if (!SHELL_TOOLS.has(value.tool_name))
21393
22405
  return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
21394
22406
  const command = toolCommand(value);
21395
- return command ? { ok: true, event: { kind: "shell.pre", command, toolUseId, dialect: dialectOf2(value) } } : { ok: false, error: `tool_input.command is required for ${value.tool_name}` };
22407
+ return command ? {
22408
+ ok: true,
22409
+ event: {
22410
+ kind: "shell.pre",
22411
+ command,
22412
+ toolUseId,
22413
+ dialect: dialectOf2(value),
22414
+ toolInput: value.tool_input
22415
+ }
22416
+ } : { ok: false, error: `tool_input.command is required for ${value.tool_name}` };
21396
22417
  }
21397
22418
  case "PostToolUse": {
21398
22419
  const command = toolCommand(value);
21399
22420
  if (!SHELL_TOOLS.has(String(value.tool_name)) || !command)
21400
22421
  return { ok: false, error: "only shell PostToolUse is mapped" };
21401
- const response = isRecord4(value.tool_response) ? value.tool_response : {};
22422
+ const response = isRecord6(value.tool_response) ? value.tool_response : {};
21402
22423
  return {
21403
22424
  ok: true,
21404
22425
  event: {
@@ -21448,6 +22469,17 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) =
21448
22469
  };
21449
22470
  }
21450
22471
  }, CONTEXT_EVENTS, render = (decision, native) => {
22472
+ if (decision.kind === "rewrite" && native === "PreToolUse" && decision.input)
22473
+ return {
22474
+ json: {
22475
+ hookSpecificOutput: {
22476
+ hookEventName: "PreToolUse",
22477
+ updatedInput: decision.input,
22478
+ ...decision.text ? { additionalContext: decision.text } : {}
22479
+ }
22480
+ },
22481
+ exitCode: 0
22482
+ };
21451
22483
  if (decision.kind === "deny" && native === "PreToolUse")
21452
22484
  return {
21453
22485
  json: {
@@ -21491,7 +22523,12 @@ var init_claude_code = __esm(() => {
21491
22523
  "compact.pre": { support: "none", native: null },
21492
22524
  stop: { support: "native", native: "Stop" }
21493
22525
  },
21494
- shellPolicy: { deny: "native", channel: "permissionDecision", failClosed: false },
22526
+ shellPolicy: {
22527
+ deny: "native",
22528
+ channel: "permissionDecision",
22529
+ failClosed: false,
22530
+ rewrite: "native"
22531
+ },
21495
22532
  context: {
21496
22533
  sessionStart: "native",
21497
22534
  perTurn: "native",
@@ -21582,14 +22619,14 @@ var init_claude_code = __esm(() => {
21582
22619
  claudeCodeAdapter = {
21583
22620
  descriptor: CLAUDE_CODE_DESCRIPTOR,
21584
22621
  parse(raw) {
21585
- const native = isRecord4(raw) && Object.hasOwn(EVENTS, String(raw.hook_event_name)) ? raw.hook_event_name : null;
22622
+ const native = isRecord6(raw) && Object.hasOwn(EVENTS, String(raw.hook_event_name)) ? raw.hook_event_name : null;
21586
22623
  const fail = (error) => ({
21587
22624
  ok: false,
21588
22625
  error,
21589
22626
  native,
21590
22627
  event: native ? EVENTS[native] : null
21591
22628
  });
21592
- if (!isRecord4(raw) || !native)
22629
+ if (!isRecord6(raw) || !native)
21593
22630
  return fail("hook_event_name is required");
21594
22631
  if (!nonEmpty2(raw.session_id))
21595
22632
  return fail("session_id is required");
@@ -21626,7 +22663,7 @@ function detectCodexSurface(env) {
21626
22663
  return env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || Boolean(env.CODEX_ELECTRON_RESOURCES_PATH) ? "codex_desktop" : "codex_cli";
21627
22664
  }
21628
22665
  var CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name) => SHELL_TOOLS2.has(String(name).toLowerCase()), SOURCES2, parseCodexHookInput = (value) => {
21629
- if (!isRecord4(value) || !Object.hasOwn(EVENTS2, String(value.hook_event_name)))
22666
+ if (!isRecord6(value) || !Object.hasOwn(EVENTS2, String(value.hook_event_name)))
21630
22667
  return { ok: false, error: "hook_event_name is required" };
21631
22668
  const event = value.hook_event_name;
21632
22669
  const cwd = existingDirectory(value.cwd);
@@ -21635,7 +22672,7 @@ var CODEX_DESCRIPTOR, EVENTS2, SHELL_TOOLS2, isShellTool = (name) => SHELL_TOOLS
21635
22672
  const toolEvent = event === "PreToolUse" || event === "PostToolUse";
21636
22673
  if (toolEvent && !nonEmpty2(value.tool_name))
21637
22674
  return { ok: false, error: "tool_name is required" };
21638
- const command = isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null;
22675
+ const command = isRecord6(value.tool_input) ? commandText(value.tool_input.command) : null;
21639
22676
  if (toolEvent && isShellTool(value.tool_name) && !command)
21640
22677
  return { ok: false, error: "tool_input.command is required for shell tools" };
21641
22678
  if (event === "SubagentStart" && !nonEmpty2(value.agent_id))
@@ -21761,7 +22798,12 @@ var init_codex = __esm(() => {
21761
22798
  "compact.pre": { support: "none", native: null },
21762
22799
  stop: { support: "native", native: "Stop" }
21763
22800
  },
21764
- shellPolicy: { deny: "native", channel: "permissionDecision", failClosed: false },
22801
+ shellPolicy: {
22802
+ deny: "native",
22803
+ channel: "permissionDecision",
22804
+ failClosed: false,
22805
+ rewrite: "none"
22806
+ },
21765
22807
  context: {
21766
22808
  sessionStart: "native",
21767
22809
  perTurn: "native",
@@ -21857,7 +22899,7 @@ var init_codex = __esm(() => {
21857
22899
  const host = detectCodexSurface(env);
21858
22900
  const parsed = parseCodexHookInput(raw);
21859
22901
  if (!parsed.ok) {
21860
- const native = isRecord4(raw) && Object.hasOwn(EVENTS2, String(raw.hook_event_name)) ? raw.hook_event_name : null;
22902
+ const native = isRecord6(raw) && Object.hasOwn(EVENTS2, String(raw.hook_event_name)) ? raw.hook_event_name : null;
21861
22903
  return { ok: false, error: parsed.error, native, event: native ? EVENTS2[native] : null };
21862
22904
  }
21863
22905
  const input = parsed.data;
@@ -21885,10 +22927,10 @@ var init_codex = __esm(() => {
21885
22927
  });
21886
22928
 
21887
22929
  // packages/workit-core/src/hooks/hosts/cursor.ts
21888
- import fs18 from "node:fs";
21889
- import path25 from "node:path";
22930
+ import fs21 from "node:fs";
22931
+ import path29 from "node:path";
21890
22932
  var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
21891
- if (!isRecord4(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
22933
+ if (!isRecord6(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
21892
22934
  return { ok: false, error: "hook_event_name is required" };
21893
22935
  const roots = value.workspace_roots;
21894
22936
  if (!Array.isArray(roots) || roots.length === 0 || !roots.every(nonEmpty2))
@@ -21967,17 +23009,17 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
21967
23009
  };
21968
23010
  }
21969
23011
  }, TRANSCRIPT_TAIL_BYTES, lastAssistantText = (file) => {
21970
- if (!file || !path25.isAbsolute(file))
23012
+ if (!file || !path29.isAbsolute(file))
21971
23013
  return null;
21972
23014
  try {
21973
- const size = fs18.statSync(file).size;
23015
+ const size = fs21.statSync(file).size;
21974
23016
  const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
21975
23017
  const buffer = Buffer.alloc(size - start);
21976
- const fd = fs18.openSync(file, "r");
23018
+ const fd = fs21.openSync(file, "r");
21977
23019
  try {
21978
- fs18.readSync(fd, buffer, 0, buffer.length, start);
23020
+ fs21.readSync(fd, buffer, 0, buffer.length, start);
21979
23021
  } finally {
21980
- fs18.closeSync(fd);
23022
+ fs21.closeSync(fd);
21981
23023
  }
21982
23024
  const lines = buffer.toString("utf8").split(`
21983
23025
  `);
@@ -21988,14 +23030,14 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
21988
23030
  } catch {
21989
23031
  continue;
21990
23032
  }
21991
- if (!isRecord4(entry) || entry.role !== "assistant")
23033
+ if (!isRecord6(entry) || entry.role !== "assistant")
21992
23034
  continue;
21993
- const content = isRecord4(entry.message) ? entry.message.content : entry.content;
23035
+ const content = isRecord6(entry.message) ? entry.message.content : entry.content;
21994
23036
  if (typeof content === "string")
21995
23037
  return content;
21996
23038
  if (!Array.isArray(content))
21997
23039
  return null;
21998
- const text = content.filter((part) => isRecord4(part) && part.type === "text" && typeof part.text === "string").map((part) => part.text).join(`
23040
+ const text = content.filter((part) => isRecord6(part) && part.type === "text" && typeof part.text === "string").map((part) => part.text).join(`
21999
23041
  `);
22000
23042
  return text || null;
22001
23043
  }
@@ -22003,14 +23045,14 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
22003
23045
  } catch {
22004
23046
  return null;
22005
23047
  }
22006
- }, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path25.sep) ? root : `${root}${path25.sep}`), hookCwd = (input) => {
23048
+ }, within2 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path29.sep) ? root : `${root}${path29.sep}`), hookCwd = (input) => {
22007
23049
  const cwd = existingDirectory(input.cwd);
22008
23050
  if (cwd && input.hook_event_name === "beforeShellExecution")
22009
23051
  return cwd;
22010
23052
  return cursorWorkspaceRoot(input);
22011
23053
  }, cursorWorkspaceRoot = (input) => {
22012
23054
  const cwd = existingDirectory(input.cwd);
22013
- return cwd && input.workspace_roots.find((root) => within(root, cwd)) || input.workspace_roots[0];
23055
+ return cwd && input.workspace_roots.find((root) => within2(root, cwd)) || input.workspace_roots[0];
22014
23056
  }, cursorDeny = (reason) => ({
22015
23057
  permission: "deny",
22016
23058
  user_message: "Workit blocked this action",
@@ -22057,7 +23099,7 @@ var init_cursor = __esm(() => {
22057
23099
  "compact.pre": { support: "partial", native: "preCompact" },
22058
23100
  stop: { support: "native", native: "stop" }
22059
23101
  },
22060
- shellPolicy: { deny: "native", channel: "exit2+json", failClosed: false },
23102
+ shellPolicy: { deny: "native", channel: "exit2+json", failClosed: false, rewrite: "none" },
22061
23103
  context: {
22062
23104
  sessionStart: "native",
22063
23105
  perTurn: "none",
@@ -22156,7 +23198,7 @@ var init_cursor = __esm(() => {
22156
23198
  parse(raw) {
22157
23199
  const parsed = parseCursorHookInput(raw);
22158
23200
  if (!parsed.ok) {
22159
- const native = isRecord4(raw) && Object.hasOwn(EVENTS3, String(raw.hook_event_name)) ? raw.hook_event_name : null;
23201
+ const native = isRecord6(raw) && Object.hasOwn(EVENTS3, String(raw.hook_event_name)) ? raw.hook_event_name : null;
22160
23202
  return { ok: false, error: parsed.error, native, event: native ? EVENTS3[native] : null };
22161
23203
  }
22162
23204
  const input = parsed.data;
@@ -22184,19 +23226,21 @@ var init_cursor = __esm(() => {
22184
23226
  });
22185
23227
 
22186
23228
  // packages/workit-core/src/hooks/run.ts
22187
- async function runHookProcess(host, stdin, stdout, stderr = process.stderr, env = process.env) {
23229
+ async function runHookProcess(host, stdin, stdout, stderr = process.stderr, env = process.env, deadline = {}) {
22188
23230
  const adapter = typeof host === "string" ? HOOK_ADAPTERS[host] : host;
22189
- let text = "";
22190
- for await (const chunk of stdin)
22191
- text += String(chunk);
23231
+ const budget = hookBudget(env, deadline, adapter.descriptor.host);
23232
+ const text = await readWithin(stdin, budget);
22192
23233
  let raw;
22193
23234
  try {
22194
- raw = JSON.parse(text || "{}");
23235
+ raw = text === null ? undefined : JSON.parse(text || "{}");
22195
23236
  } catch {
22196
23237
  raw = undefined;
22197
23238
  }
22198
- const result = dispatchHook(adapter, raw, env);
22199
- if (raw === undefined || result.error)
23239
+ const result = text === null ? skippedHook(adapter, undefined, env, budget, "input") : await dispatchWithin(adapter, raw, env, budget, deadline.dispatch);
23240
+ if (result.skipped)
23241
+ stderr.write(`[workit] ${result.error}
23242
+ `);
23243
+ else if (raw === undefined || result.error)
22200
23244
  stderr.write(`[workit] hook input rejected: ${raw === undefined ? "invalid JSON hook input" : result.error}
22201
23245
  `);
22202
23246
  stdout.write(`${JSON.stringify(result.json)}
@@ -22205,7 +23249,7 @@ async function runHookProcess(host, stdin, stdout, stderr = process.stderr, env
22205
23249
  }
22206
23250
  var HOOK_ADAPTERS;
22207
23251
  var init_run = __esm(async () => {
22208
- init_handle();
23252
+ init_deadline();
22209
23253
  init_claude_code();
22210
23254
  init_codex();
22211
23255
  init_cursor();
@@ -22228,6 +23272,7 @@ var init_pi = () => {};
22228
23272
  var init_hooks = __esm(async () => {
22229
23273
  init_context();
22230
23274
  init_policy();
23275
+ init_heavy_gate();
22231
23276
  init_write_gate();
22232
23277
  init_live_session();
22233
23278
  init_stop();
@@ -22362,6 +23407,14 @@ var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapa
22362
23407
  return { json: {} };
22363
23408
  const note = `[workit: ${gap}; Workit checks are skipped for this action]`;
22364
23409
  return { json: { permission: "allow", user_message: note, agent_message: note } };
23410
+ }, subagentStartStep = (_adapter, raw) => {
23411
+ let json;
23412
+ try {
23413
+ json = handleSubagentStartHook(raw);
23414
+ } catch (error) {
23415
+ json = cursorDeny(`hook failure: ${String(error)}`);
23416
+ }
23417
+ return { json, exitCode: json.permission === "deny" ? 2 : 0, error: null };
22365
23418
  }, runCursorHook = async () => {
22366
23419
  let text = "";
22367
23420
  for await (const chunk of process.stdin)
@@ -22379,24 +23432,16 @@ var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapa
22379
23432
  process.exitCode = 0;
22380
23433
  return;
22381
23434
  }
22382
- if (isSubagentStart(raw)) {
22383
- let output;
22384
- try {
22385
- output = handleSubagentStartHook(raw);
22386
- } catch (error) {
22387
- output = cursorDeny(`hook failure: ${String(error)}`);
22388
- }
22389
- process.stdout.write(`${JSON.stringify(output)}
22390
- `);
22391
- process.exitCode = output.permission === "deny" ? 2 : 0;
22392
- return;
22393
- }
22394
- process.exitCode = await runHookProcess(cursorAdapter, [text], process.stdout);
23435
+ process.exitCode = await runHookProcess(cursorAdapter, [text], process.stdout, process.stderr, process.env, {
23436
+ startedAt: PROCESS_START,
23437
+ ...isSubagentStart(raw) ? { dispatch: subagentStartStep } : {}
23438
+ });
22395
23439
  };
22396
23440
  var init_workit_hook = __esm(async () => {
22397
23441
  init_task_contract();
22398
23442
  init_task_engine();
22399
23443
  init_task_store();
23444
+ init_deadline();
22400
23445
  await __promiseAll([
22401
23446
  init_hooks(),
22402
23447
  init_hooks()