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