@brainervirus/workit-codex 8.15.1 → 8.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ CHECKS_FILE,
4
+ repoHeavyCommands,
5
+ loadCheckConfig,
6
+ matchesNamedCheck,
7
+ gateCheckNames,
8
+ checkCommand,
9
+ maybeHeavy
10
+ } from "./workit-hook-rbb0m9zp.js";
2
11
  // packages/workit-core/src/hooks/descriptor.ts
3
12
  var support = (descriptor, axis) => {
4
13
  if (axis.startsWith("event:"))
@@ -25,8 +34,8 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
25
34
  });
26
35
  // packages/workit-core/src/hooks/context.ts
27
36
  import { createHash as createHash11 } from "node:crypto";
28
- import fs14 from "node:fs";
29
- import path18 from "node:path";
37
+ import fs13 from "node:fs";
38
+ import path17 from "node:path";
30
39
 
31
40
  // packages/workit-core/src/core/methods.ts
32
41
  var METHODS = {
@@ -8804,225 +8813,20 @@ function requirementId(input) {
8804
8813
 
8805
8814
  // packages/workit-core/src/core/task-evaluation.ts
8806
8815
  import { createHash as createHash10 } from "node:crypto";
8807
- import * as fs13 from "node:fs";
8808
- import { spawnSync as spawnSync10 } from "node:child_process";
8809
- import path16 from "node:path";
8810
-
8811
- // packages/workit-core/src/check-config.ts
8812
- import { spawnSync as spawnSync2 } from "node:child_process";
8813
- import fs2 from "node:fs";
8814
- import path2 from "node:path";
8815
- var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
8816
- var CHECKS_FILE = "workit.checks.json";
8817
- var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
8818
- var SHELL_META = /[|&;<>()$`\\*?[\]{}~!#]/u;
8819
- function splitCommand(command) {
8820
- const argv = [];
8821
- let current = "";
8822
- let quote = null;
8823
- let started = false;
8824
- for (const char of command) {
8825
- if (quote) {
8826
- if (char === quote)
8827
- quote = null;
8828
- else
8829
- current += char;
8830
- continue;
8831
- }
8832
- if (char === '"' || char === "'") {
8833
- quote = char;
8834
- started = true;
8835
- continue;
8836
- }
8837
- if (/\s/u.test(char)) {
8838
- if (started)
8839
- argv.push(current);
8840
- current = "";
8841
- started = false;
8842
- continue;
8843
- }
8844
- if (SHELL_META.test(char))
8845
- return new Error(`"${command}" uses shell syntax (${char}); configure an argv array instead`);
8846
- current += char;
8847
- started = true;
8848
- }
8849
- if (quote)
8850
- return new Error(`"${command}" has an unterminated quote`);
8851
- if (started)
8852
- argv.push(current);
8853
- return argv.length ? argv : new Error("empty command");
8854
- }
8855
- var git = (cwd, args) => {
8856
- const run = spawnSync2("git", args, {
8857
- cwd,
8858
- encoding: "utf8",
8859
- timeout: 20000,
8860
- killSignal: "SIGKILL",
8861
- windowsHide: true
8862
- });
8863
- const value = run.status === 0 ? (run.stdout ?? "").trim() : "";
8864
- return value || null;
8865
- };
8866
- function checkRoot(cwd) {
8867
- return path2.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
8868
- }
8869
- var packageManager = (root) => {
8870
- const has = (file) => fs2.existsSync(path2.join(root, file));
8871
- if (has("bun.lock") || has("bun.lockb"))
8872
- return "bun";
8873
- if (has("pnpm-lock.yaml"))
8874
- return "pnpm";
8875
- if (has("yarn.lock"))
8876
- return "yarn";
8877
- return "npm";
8878
- };
8879
- var scriptCheck = (pm, name) => {
8880
- const argv = [pm, "run", name];
8881
- const accepts = [argv];
8882
- if (pm === "pnpm" || pm === "yarn" || pm === "npm" && name === "test")
8883
- accepts.push([pm, name]);
8884
- return { name, argv, accepts };
8885
- };
8886
- var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
8887
- function detectedChecks(root) {
8888
- const read = (file) => {
8889
- try {
8890
- return fs2.readFileSync(path2.join(root, file), "utf8");
8891
- } catch {
8892
- return null;
8893
- }
8894
- };
8895
- const checks = [];
8896
- const add = (check) => {
8897
- if (!checks.some((item) => item.name === check.name))
8898
- checks.push(check);
8899
- };
8900
- const manifestText = read("package.json");
8901
- if (manifestText !== null) {
8902
- let manifest = null;
8903
- try {
8904
- manifest = JSON.parse(manifestText);
8905
- } catch {}
8906
- const scripts = isRecord(manifest) && isRecord(manifest.scripts) ? manifest.scripts : {};
8907
- const pm = packageManager(root);
8908
- for (const name of SCRIPT_CHECKS) {
8909
- const script = scripts[name];
8910
- if (typeof script === "string" && script.trim() !== "")
8911
- add(scriptCheck(pm, name));
8912
- }
8913
- }
8914
- const plain = (name, argv) => ({ name, argv, accepts: [argv] });
8915
- if (read("go.mod") !== null)
8916
- add(plain("test", ["go", "test", "./..."]));
8917
- if (read("Cargo.toml") !== null)
8918
- add(plain("test", ["cargo", "test"]));
8919
- if (read("pytest.ini") !== null || /\bpytest\b/u.test(read("pyproject.toml") ?? ""))
8920
- add(plain("test", ["pytest"]));
8921
- const makefile = read("Makefile") ?? read("makefile") ?? read("GNUmakefile");
8922
- if (makefile !== null && /^test\s*:(?!=)/mu.test(makefile))
8923
- add(plain("test", ["make", "test"]));
8924
- return checks;
8925
- }
8926
- var GATES = ["testing", "verification"];
8927
- function loadCheckConfig(cwd) {
8928
- const root = checkRoot(cwd);
8929
- const file = path2.join(root, CHECKS_FILE);
8930
- const none = (error = null) => ({
8931
- source: error ? CHECKS_FILE : "none",
8932
- root,
8933
- checks: [],
8934
- gates: {},
8935
- error
8936
- });
8937
- let text = null;
8938
- try {
8939
- text = fs2.readFileSync(file, "utf8");
8940
- } catch (error) {
8941
- if (error.code !== "ENOENT")
8942
- return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
8943
- }
8944
- if (text === null) {
8945
- const checks = detectedChecks(root);
8946
- return checks.length ? { source: "defaults", root, checks, gates: {}, error: null } : none();
8947
- }
8948
- let parsed;
8949
- try {
8950
- parsed = JSON.parse(text);
8951
- } catch {
8952
- return none(`${CHECKS_FILE} is not valid JSON`);
8953
- }
8954
- if (!isRecord(parsed) || !isRecord(parsed.checks))
8955
- return none(`${CHECKS_FILE} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
8956
- const checks = [];
8957
- for (const [name, value] of Object.entries(parsed.checks)) {
8958
- if (!CHECK_NAME.test(name))
8959
- return none(`${CHECKS_FILE}: check name "${name}" must match ${CHECK_NAME.source}`);
8960
- const argv = typeof value === "string" ? splitCommand(value) : Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && item.length > 0) ? value : new Error("expected a command string or a non-empty argv array");
8961
- if (argv instanceof Error)
8962
- return none(`${CHECKS_FILE}: check "${name}": ${argv.message}`);
8963
- checks.push({ name, argv, accepts: [argv] });
8964
- }
8965
- const gates = {};
8966
- if (parsed.gates !== undefined) {
8967
- if (!isRecord(parsed.gates))
8968
- return none(`${CHECKS_FILE}: gates must be {"testing":"<name>"|["<name>",…]}`);
8969
- for (const [gate, value] of Object.entries(parsed.gates)) {
8970
- if (!GATES.includes(gate))
8971
- return none(`${CHECKS_FILE}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
8972
- const names = typeof value === "string" ? [value] : value;
8973
- if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
8974
- return none(`${CHECKS_FILE}: gates.${gate} must name configured checks`);
8975
- gates[gate] = names;
8976
- }
8977
- }
8978
- return { source: CHECKS_FILE, root, checks, gates, error: null, resources: parsed.resources };
8979
- }
8980
- var sameArgv = (left, right) => left.length === right.length && left.every((item, index) => item === right[index]);
8981
- function matchesNamedCheck(config, name, argv, cwd) {
8982
- if (config.error || name === null || cwd !== ".")
8983
- return false;
8984
- const check = config.checks.find((item) => item.name === name);
8985
- return Boolean(check?.accepts.some((accepted) => sameArgv(accepted, argv)));
8986
- }
8987
- function gateCheckNames(config, dimension) {
8988
- const explicit = config.gates[dimension];
8989
- if (explicit)
8990
- return explicit;
8991
- const names = config.checks.map((check) => check.name);
8992
- if (dimension === "testing")
8993
- return names.includes("test") ? ["test"] : [];
8994
- return names;
8995
- }
8996
- var workitOnPath;
8997
- function workitAvailable(env = process.env) {
8998
- if (workitOnPath !== undefined)
8999
- return workitOnPath;
9000
- const windows = process.platform === "win32";
9001
- const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
9002
- const dirs = (env.PATH ?? env.Path ?? "").split(path2.delimiter).filter(Boolean);
9003
- workitOnPath = dirs.some((dir) => exts.some((ext) => {
9004
- try {
9005
- return fs2.statSync(path2.join(dir, `workit${ext}`)).isFile();
9006
- } catch {
9007
- return false;
9008
- }
9009
- }));
9010
- return workitOnPath;
9011
- }
9012
- function checkCommand(args, version, onPath = workitAvailable()) {
9013
- return onPath ? `workit check ${args}` : `npx -y @brainervirus/workit-cli@${version} check ${args}`;
9014
- }
8816
+ import * as fs12 from "node:fs";
8817
+ import { spawnSync as spawnSync9 } from "node:child_process";
8818
+ import path15 from "node:path";
9015
8819
 
9016
8820
  // packages/workit-core/src/git/rev.ts
9017
- import { spawnSync as spawnSync3 } from "node:child_process";
8821
+ import { spawnSync as spawnSync2 } from "node:child_process";
9018
8822
  import { createHash as createHash3 } from "node:crypto";
9019
- import fs3 from "node:fs";
8823
+ import fs2 from "node:fs";
9020
8824
  import os from "node:os";
9021
- import path3 from "node:path";
8825
+ import path2 from "node:path";
9022
8826
  var MAX_BUFFER = 256 * 1024 * 1024;
9023
8827
  var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
9024
- var git2 = (cwd, args, options = {}) => {
9025
- const result = spawnSync3("git", args, {
8828
+ var git = (cwd, args, options = {}) => {
8829
+ const result = spawnSync2("git", args, {
9026
8830
  cwd,
9027
8831
  encoding: "utf8",
9028
8832
  env: options.env ?? process.env,
@@ -9048,30 +8852,30 @@ var line = (run) => {
9048
8852
  };
9049
8853
  var safeArg = (value) => value.length > 0 && !value.startsWith("-");
9050
8854
  function headSha(cwd) {
9051
- return line(git2(cwd, ["rev-parse", "--verify", "-q", "HEAD^{commit}"]));
8855
+ return line(git(cwd, ["rev-parse", "--verify", "-q", "HEAD^{commit}"]));
9052
8856
  }
9053
- var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
8857
+ var emptyTree = (cwd) => line(git(cwd, ["mktree"], { input: "" }));
9054
8858
  var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
9055
8859
  var seedIndex = (from, to) => {
9056
- fs3.copyFileSync(from, to);
9057
- const { atimeNs, mtimeNs } = fs3.statSync(from, { bigint: true });
8860
+ fs2.copyFileSync(from, to);
8861
+ const { atimeNs, mtimeNs } = fs2.statSync(from, { bigint: true });
9058
8862
  const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
9059
- fs3.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
8863
+ fs2.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
9060
8864
  };
9061
8865
  function worktreeTree(cwd, options = {}) {
9062
- const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
9063
- const indexPath = line(git2(cwd, ["rev-parse", "--path-format=absolute", "--git-path", "index"]));
8866
+ const top = line(git(cwd, ["rev-parse", "--show-toplevel"]));
8867
+ const indexPath = line(git(cwd, ["rev-parse", "--path-format=absolute", "--git-path", "index"]));
9064
8868
  if (!top || !indexPath)
9065
8869
  return null;
9066
8870
  const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
9067
8871
  const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
9068
- const scratch = fs3.mkdtempSync(path3.join(os.tmpdir(), "workit-tree-"));
8872
+ const scratch = fs2.mkdtempSync(path2.join(os.tmpdir(), "workit-tree-"));
9069
8873
  try {
9070
- const tempIndex = path3.join(scratch, "index");
9071
- if (fs3.existsSync(indexPath))
8874
+ const tempIndex = path2.join(scratch, "index");
8875
+ if (fs2.existsSync(indexPath))
9072
8876
  seedIndex(indexPath, tempIndex);
9073
8877
  const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
9074
- const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
8878
+ const untracked = git(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
9075
8879
  env,
9076
8880
  timeoutMs
9077
8881
  });
@@ -9080,34 +8884,34 @@ function worktreeTree(cwd, options = {}) {
9080
8884
  const skipped = [];
9081
8885
  for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
9082
8886
  try {
9083
- const stat = fs3.statSync(path3.join(top, file));
8887
+ const stat = fs2.statSync(path2.join(top, file));
9084
8888
  if (stat.isFile() && stat.size > cap)
9085
8889
  skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
9086
8890
  } catch {}
9087
8891
  }
9088
8892
  skipped.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
9089
- const added = skipped.length ? git2(top, ["add", "-A", "--pathspec-from-file=-", "--pathspec-file-nul"], {
8893
+ const added = skipped.length ? git(top, ["add", "-A", "--pathspec-from-file=-", "--pathspec-file-nul"], {
9090
8894
  env,
9091
8895
  timeoutMs,
9092
8896
  input: [".", ...skipped.map((file) => `:(exclude,literal)${file.path}`)].join("\x00")
9093
- }) : git2(top, ["add", "-A"], { env, timeoutMs });
8897
+ }) : git(top, ["add", "-A"], { env, timeoutMs });
9094
8898
  if (!added.ok)
9095
8899
  return null;
9096
- const tree = line(git2(top, ["write-tree"], { env, timeoutMs }));
8900
+ const tree = line(git(top, ["write-tree"], { env, timeoutMs }));
9097
8901
  if (!tree)
9098
8902
  return null;
9099
- const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
8903
+ const base = line(git(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
9100
8904
  const key = skipped.length ? `sha256:${createHash3("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
9101
8905
  return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
9102
8906
  } finally {
9103
- fs3.rmSync(scratch, { recursive: true, force: true });
8907
+ fs2.rmSync(scratch, { recursive: true, force: true });
9104
8908
  }
9105
8909
  }
9106
8910
  function worktreeSignal(cwd, options = {}) {
9107
- const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
8911
+ const top = line(git(cwd, ["rev-parse", "--show-toplevel"]));
9108
8912
  if (!top)
9109
8913
  return null;
9110
- const status = git2(top, [
8914
+ const status = git(top, [
9111
8915
  "status",
9112
8916
  "--porcelain=v1",
9113
8917
  "-z",
@@ -9126,10 +8930,10 @@ function worktreeSignal(cwd, options = {}) {
9126
8930
  if (entry.length < 4)
9127
8931
  continue;
9128
8932
  const file = entry.slice(3);
9129
- const absolute = path3.join(top, file);
8933
+ const absolute = path2.join(top, file);
9130
8934
  let stat = "-";
9131
8935
  try {
9132
- const info = fs3.lstatSync(absolute, { bigint: true });
8936
+ const info = fs2.lstatSync(absolute, { bigint: true });
9133
8937
  stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
9134
8938
  if (info.isDirectory())
9135
8939
  stat += `:${headSha(absolute) ?? "-"}`;
@@ -9141,12 +8945,12 @@ function worktreeSignal(cwd, options = {}) {
9141
8945
  function mergeBase(cwd, a, b) {
9142
8946
  if (!safeArg(a) || !safeArg(b))
9143
8947
  return null;
9144
- return line(git2(cwd, ["merge-base", a, b]));
8948
+ return line(git(cwd, ["merge-base", a, b]));
9145
8949
  }
9146
8950
  function patchId(cwd, base, head) {
9147
8951
  if (!safeArg(base) || !safeArg(head))
9148
8952
  return null;
9149
- const diff = git2(cwd, [
8953
+ const diff = git(cwd, [
9150
8954
  "diff",
9151
8955
  "-U3",
9152
8956
  "--diff-algorithm=myers",
@@ -9163,20 +8967,20 @@ function patchId(cwd, base, head) {
9163
8967
  ]);
9164
8968
  if (!diff.ok || !diff.stdout.trim())
9165
8969
  return null;
9166
- const id = git2(cwd, ["patch-id", "--stable"], { input: diff.stdout });
8970
+ const id = git(cwd, ["patch-id", "--stable"], { input: diff.stdout });
9167
8971
  return line(id)?.split(/\s+/u)[0] ?? null;
9168
8972
  }
9169
8973
  function remoteNames(cwd) {
9170
- return (git2(cwd, ["remote"]).stdout ?? "").split(/\r?\n/u).map((value) => value.trim()).filter(Boolean);
8974
+ return (git(cwd, ["remote"]).stdout ?? "").split(/\r?\n/u).map((value) => value.trim()).filter(Boolean);
9171
8975
  }
9172
8976
  function resolveRef(cwd, ref) {
9173
8977
  if (!safeArg(ref))
9174
8978
  return null;
9175
- return line(git2(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
8979
+ return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
9176
8980
  }
9177
- var config2 = (cwd, key) => line(git2(cwd, ["config", "--get", key]));
8981
+ var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
9178
8982
  function currentBranch(cwd) {
9179
- return line(git2(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
8983
+ return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
9180
8984
  }
9181
8985
  function pushRemoteName(cwd, branch) {
9182
8986
  const name = branch ?? currentBranch(cwd);
@@ -9196,8 +9000,8 @@ function pushRemoteName(cwd, branch) {
9196
9000
 
9197
9001
  // packages/workit-core/src/core/task-store.ts
9198
9002
  import { createHash as createHash6, randomUUID as randomUUID3 } from "node:crypto";
9199
- import * as fs10 from "node:fs";
9200
- import path10 from "node:path";
9003
+ import * as fs9 from "node:fs";
9004
+ import path9 from "node:path";
9201
9005
 
9202
9006
  // node_modules/@openclaw/fs-safe/dist/errors.js
9203
9007
  var OPERATIONAL_CODES = new Set([
@@ -9248,10 +9052,10 @@ function sameFileIdentity(left, right, platform = process.platform) {
9248
9052
  // node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
9249
9053
  import { randomBytes } from "node:crypto";
9250
9054
  import fsSync from "node:fs";
9251
- import path4 from "node:path";
9055
+ import path3 from "node:path";
9252
9056
 
9253
9057
  // node_modules/@openclaw/fs-safe/dist/bounded-read.js
9254
- import fs4 from "node:fs";
9058
+ import fs3 from "node:fs";
9255
9059
 
9256
9060
  // node_modules/@openclaw/fs-safe/dist/byte-budget.js
9257
9061
  function normalizeMaxBytes(value, options = {}) {
@@ -9288,7 +9092,7 @@ function readFileDescriptorBoundedSync(fd, maxBytes) {
9288
9092
  let total = 0;
9289
9093
  while (true) {
9290
9094
  const length = nextReadLength(total, maxBytes, scratch.length);
9291
- const bytesRead = fs4.readSync(fd, scratch, 0, length, null);
9095
+ const bytesRead = fs3.readSync(fd, scratch, 0, length, null);
9292
9096
  if (bytesRead === 0) {
9293
9097
  return Buffer.concat(chunks, total);
9294
9098
  }
@@ -9297,9 +9101,9 @@ function readFileDescriptorBoundedSync(fd, maxBytes) {
9297
9101
  }
9298
9102
 
9299
9103
  // node_modules/@openclaw/fs-safe/dist/read-open-flags.js
9300
- import fs5 from "node:fs";
9104
+ import fs4 from "node:fs";
9301
9105
  function resolveReadOpenFlags(options) {
9302
- const constants = options?.constants ?? fs5.constants;
9106
+ const constants = options?.constants ?? fs4.constants;
9303
9107
  const noFollow = process.platform !== "win32" && options?.followSymlinks !== true && typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0;
9304
9108
  const nonBlocking = process.platform !== "win32" && typeof constants.O_NONBLOCK === "number" ? constants.O_NONBLOCK : 0;
9305
9109
  return constants.O_RDONLY | noFollow | nonBlocking;
@@ -9338,13 +9142,13 @@ ${ownershipToken}
9338
9142
  return { raw, ownershipToken };
9339
9143
  }
9340
9144
  function relativeSidecarLockPath(lockRoot, lockPath) {
9341
- const resolved = path4.resolve(lockPath);
9342
- const lexicalRelative = path4.relative(lockRoot.rootDir, resolved);
9343
- const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path4.sep}`) && !path4.isAbsolute(lexicalRelative) ? lexicalRelative : path4.relative(lockRoot.rootReal, resolved);
9344
- if (!relative || relative === ".." || relative.startsWith(`..${path4.sep}`) || path4.isAbsolute(relative)) {
9145
+ const resolved = path3.resolve(lockPath);
9146
+ const lexicalRelative = path3.relative(lockRoot.rootDir, resolved);
9147
+ const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path3.sep}`) && !path3.isAbsolute(lexicalRelative) ? lexicalRelative : path3.relative(lockRoot.rootReal, resolved);
9148
+ if (!relative || relative === ".." || relative.startsWith(`..${path3.sep}`) || path3.isAbsolute(relative)) {
9345
9149
  throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
9346
9150
  }
9347
- return relative.split(path4.sep).join(path4.posix.sep);
9151
+ return relative.split(path3.sep).join(path3.posix.sep);
9348
9152
  }
9349
9153
  function parseSidecarLockPayload(raw, parser) {
9350
9154
  if (parser) {
@@ -9490,8 +9294,8 @@ function getFsSafeLockConfig() {
9490
9294
  }
9491
9295
 
9492
9296
  // node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
9493
- import fs6 from "node:fs";
9494
- import path5 from "node:path";
9297
+ import fs5 from "node:fs";
9298
+ import path4 from "node:path";
9495
9299
 
9496
9300
  // node_modules/@openclaw/fs-safe/dist/timing.js
9497
9301
  function sleepSync(ms) {
@@ -9521,7 +9325,7 @@ function releaseAllSyncHeldLocks() {
9521
9325
  }
9522
9326
  try {
9523
9327
  if (held.fd !== undefined)
9524
- fs6.closeSync(held.fd);
9328
+ fs5.closeSync(held.fd);
9525
9329
  } catch {}
9526
9330
  try {
9527
9331
  removeSidecarLockIfUnchangedSync(held.lockPath, held.snapshot);
@@ -9556,7 +9360,7 @@ function releaseSyncHeldLock(held) {
9556
9360
  if (held.fd !== undefined) {
9557
9361
  const fd = held.fd;
9558
9362
  held.fd = undefined;
9559
- fs6.closeSync(fd);
9363
+ fs5.closeSync(fd);
9560
9364
  }
9561
9365
  removeSidecarLockIfUnchangedSync(held.lockPath, held.snapshot);
9562
9366
  heldLocks.delete(held.normalizedTargetPath);
@@ -9579,29 +9383,29 @@ function createSyncHeldLockHandle(held) {
9579
9383
  };
9580
9384
  }
9581
9385
  function canonicalLockParentSync(parent) {
9582
- return process.platform === "win32" ? fs6.realpathSync.native(parent) : fs6.realpathSync(parent);
9386
+ return process.platform === "win32" ? fs5.realpathSync.native(parent) : fs5.realpathSync(parent);
9583
9387
  }
9584
9388
  function normalizeTargetPath(targetPath) {
9585
- const resolved = path5.resolve(targetPath);
9586
- fs6.mkdirSync(path5.dirname(resolved), { recursive: true });
9389
+ const resolved = path4.resolve(targetPath);
9390
+ fs5.mkdirSync(path4.dirname(resolved), { recursive: true });
9587
9391
  try {
9588
- return path5.join(canonicalLockParentSync(path5.dirname(resolved)), path5.basename(resolved));
9392
+ return path4.join(canonicalLockParentSync(path4.dirname(resolved)), path4.basename(resolved));
9589
9393
  } catch {
9590
9394
  return resolved;
9591
9395
  }
9592
9396
  }
9593
9397
  function boundedLockPath(lockPath, lockRoot) {
9594
- const resolved = path5.resolve(lockPath);
9398
+ const resolved = path4.resolve(lockPath);
9595
9399
  if (!lockRoot)
9596
9400
  return resolved;
9597
9401
  relativeSidecarLockPath(lockRoot, resolved);
9598
- const parent = path5.dirname(resolved);
9402
+ const parent = path4.dirname(resolved);
9599
9403
  const parentReal = canonicalLockParentSync(parent);
9600
- const parentRelative = path5.relative(lockRoot.rootReal, parentReal);
9601
- if (parentRelative === ".." || parentRelative.startsWith(`..${path5.sep}`) || path5.isAbsolute(parentRelative)) {
9404
+ const parentRelative = path4.relative(lockRoot.rootReal, parentReal);
9405
+ if (parentRelative === ".." || parentRelative.startsWith(`..${path4.sep}`) || path4.isAbsolute(parentRelative)) {
9602
9406
  throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
9603
9407
  }
9604
- return path5.join(parentReal, path5.basename(resolved));
9408
+ return path4.join(parentReal, path4.basename(resolved));
9605
9409
  }
9606
9410
  function defaultShouldReclaim(snapshot, staleMs, nowMs) {
9607
9411
  const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
@@ -9611,7 +9415,7 @@ function defaultShouldReclaim(snapshot, staleMs, nowMs) {
9611
9415
  }
9612
9416
  function reclaimGuardExists(reclaimGuardPath) {
9613
9417
  try {
9614
- fs6.lstatSync(reclaimGuardPath);
9418
+ fs5.lstatSync(reclaimGuardPath);
9615
9419
  return true;
9616
9420
  } catch (error) {
9617
9421
  if (error.code === "ENOENT")
@@ -9675,19 +9479,19 @@ function acquireFileLockSync(targetPath, options) {
9675
9479
  const { raw, ownershipToken } = serializeSidecarLockPayload(payload);
9676
9480
  let lockFileCreateDenied = false;
9677
9481
  try {
9678
- const noFollow = process.platform !== "win32" && typeof fs6.constants.O_NOFOLLOW === "number" ? fs6.constants.O_NOFOLLOW : 0;
9482
+ const noFollow = process.platform !== "win32" && typeof fs5.constants.O_NOFOLLOW === "number" ? fs5.constants.O_NOFOLLOW : 0;
9679
9483
  try {
9680
- fd = fs6.openSync(lockPath, fs6.constants.O_WRONLY | fs6.constants.O_CREAT | fs6.constants.O_EXCL | noFollow, 384);
9484
+ fd = fs5.openSync(lockPath, fs5.constants.O_WRONLY | fs5.constants.O_CREAT | fs5.constants.O_EXCL | noFollow, 384);
9681
9485
  } catch (error) {
9682
9486
  lockFileCreateDenied = isTransientLockFileDenial(error, lockPath);
9683
9487
  throw error;
9684
9488
  }
9685
- fs6.writeFileSync(fd, raw, "utf8");
9686
- fs6.fsyncSync(fd);
9489
+ fs5.writeFileSync(fd, raw, "utf8");
9490
+ fs5.fsyncSync(fd);
9687
9491
  const snapshot = {
9688
9492
  raw,
9689
9493
  payload,
9690
- stat: fs6.fstatSync(fd),
9494
+ stat: fs5.fstatSync(fd),
9691
9495
  ownershipToken
9692
9496
  };
9693
9497
  const createdHeld = {
@@ -9722,8 +9526,8 @@ function acquireFileLockSync(targetPath, options) {
9722
9526
  return returnedHandle;
9723
9527
  } catch (error) {
9724
9528
  if (fd !== undefined) {
9725
- const failed = { payload: null, stat: fs6.fstatSync(fd) };
9726
- fs6.closeSync(fd);
9529
+ const failed = { payload: null, stat: fs5.fstatSync(fd) };
9530
+ fs5.closeSync(fd);
9727
9531
  fd = undefined;
9728
9532
  removeSidecarLockIfUnchangedSync(lockPath, failed);
9729
9533
  }
@@ -9772,7 +9576,7 @@ function acquireFileLockSync(targetPath, options) {
9772
9576
  })) {
9773
9577
  let ownsReclaimGuard = false;
9774
9578
  try {
9775
- fs6.mkdirSync(reclaimGuardPath);
9579
+ fs5.mkdirSync(reclaimGuardPath);
9776
9580
  ownsReclaimGuard = true;
9777
9581
  if (removeSidecarLockIfUnchangedSync(lockPath, snapshot))
9778
9582
  continue;
@@ -9785,7 +9589,7 @@ function acquireFileLockSync(targetPath, options) {
9785
9589
  } finally {
9786
9590
  if (ownsReclaimGuard) {
9787
9591
  try {
9788
- fs6.rmdirSync(reclaimGuardPath);
9592
+ fs5.rmdirSync(reclaimGuardPath);
9789
9593
  } catch {}
9790
9594
  }
9791
9595
  }
@@ -9802,12 +9606,12 @@ function acquireFileLockSync(targetPath, options) {
9802
9606
  }
9803
9607
  // packages/workit-core/src/host-session.ts
9804
9608
  import { createHash as createHash4, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
9805
- import fs7 from "node:fs";
9806
- import path7 from "node:path";
9609
+ import fs6 from "node:fs";
9610
+ import path6 from "node:path";
9807
9611
 
9808
9612
  // packages/workit-core/src/core/logger.ts
9809
9613
  import os2 from "node:os";
9810
- import path6 from "node:path";
9614
+ import path5 from "node:path";
9811
9615
  var SENSITIVE_WORDS = new Set([
9812
9616
  "token",
9813
9617
  "secret",
@@ -9852,14 +9656,14 @@ var resolveStateDir = () => {
9852
9656
  return override;
9853
9657
  const home = os2.homedir();
9854
9658
  if (process.env.XDG_STATE_HOME)
9855
- return path6.join(process.env.XDG_STATE_HOME, "workit");
9659
+ return path5.join(process.env.XDG_STATE_HOME, "workit");
9856
9660
  switch (os2.platform()) {
9857
9661
  case "darwin":
9858
- return path6.join(home, "Library", "Application Support", "workit");
9662
+ return path5.join(home, "Library", "Application Support", "workit");
9859
9663
  case "win32":
9860
- return path6.join(process.env.LOCALAPPDATA ?? path6.join(home, "AppData", "Local"), "workit");
9664
+ return path5.join(process.env.LOCALAPPDATA ?? path5.join(home, "AppData", "Local"), "workit");
9861
9665
  default:
9862
- return path6.join(process.env.HOME ?? home, ".local", "state", "workit");
9666
+ return path5.join(process.env.HOME ?? home, ".local", "state", "workit");
9863
9667
  }
9864
9668
  };
9865
9669
  var homePattern = new RegExp(`${escapeRegExp(os2.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
@@ -9895,8 +9699,8 @@ function hostSessionFromEnv(env) {
9895
9699
  var ISSUED_PER_LEAD = 64;
9896
9700
  var ISSUED_SESSIONS_MARKER = "workit-issued-sessions/3";
9897
9701
  var hash = (value) => createHash4("sha256").update(value).digest("hex").slice(0, 32);
9898
- var userIssuedRoot = () => path7.join(resolveStateDir(), "issued");
9899
- var userTokenRoot = () => path7.join(resolveStateDir(), "issued-tokens");
9702
+ var userIssuedRoot = () => path6.join(resolveStateDir(), "issued");
9703
+ var userTokenRoot = () => path6.join(resolveStateDir(), "issued-tokens");
9900
9704
  function migrateRepoIssued(cwd) {
9901
9705
  if (cwd === undefined)
9902
9706
  return;
@@ -9905,27 +9709,27 @@ function migrateRepoIssued(cwd) {
9905
9709
  const location = resolveStore(cwd);
9906
9710
  if (location instanceof Error)
9907
9711
  return;
9908
- old = path7.join(location.dir, "hooks", "issued");
9909
- if (!fs7.existsSync(old))
9712
+ old = path6.join(location.dir, "hooks", "issued");
9713
+ if (!fs6.existsSync(old))
9910
9714
  return;
9911
9715
  } catch {
9912
9716
  return;
9913
9717
  }
9914
9718
  for (const lead of safeList(old)) {
9915
9719
  try {
9916
- const from = path7.join(old, lead);
9917
- if (!fs7.statSync(from).isDirectory())
9720
+ const from = path6.join(old, lead);
9721
+ if (!fs6.statSync(from).isDirectory())
9918
9722
  continue;
9919
- const to = path7.join(userIssuedRoot(), lead);
9920
- fs7.mkdirSync(to, { recursive: true, mode: 448 });
9921
- for (const id of fs7.readdirSync(from)) {
9723
+ const to = path6.join(userIssuedRoot(), lead);
9724
+ fs6.mkdirSync(to, { recursive: true, mode: 448 });
9725
+ for (const id of fs6.readdirSync(from)) {
9922
9726
  try {
9923
- fs7.copyFileSync(path7.join(from, id), path7.join(to, id), fs7.constants.COPYFILE_EXCL);
9727
+ fs6.copyFileSync(path6.join(from, id), path6.join(to, id), fs6.constants.COPYFILE_EXCL);
9924
9728
  } catch (error) {
9925
9729
  if (error.code !== "EEXIST")
9926
9730
  throw error;
9927
9731
  }
9928
- fs7.rmSync(path7.join(from, id), { force: true });
9732
+ fs6.rmSync(path6.join(from, id), { force: true });
9929
9733
  }
9930
9734
  removeIfEmpty(from);
9931
9735
  } catch {}
@@ -9934,14 +9738,14 @@ function migrateRepoIssued(cwd) {
9934
9738
  }
9935
9739
  var safeList = (dir) => {
9936
9740
  try {
9937
- return fs7.readdirSync(dir);
9741
+ return fs6.readdirSync(dir);
9938
9742
  } catch {
9939
9743
  return [];
9940
9744
  }
9941
9745
  };
9942
9746
  var removeIfEmpty = (dir) => {
9943
9747
  try {
9944
- fs7.rmdirSync(dir);
9748
+ fs6.rmdirSync(dir);
9945
9749
  } catch {}
9946
9750
  };
9947
9751
  var tokenHash = (token) => createHash4("sha256").update(token).digest("hex");
@@ -9950,13 +9754,13 @@ function recordIssuedSession(cwd, lead, issued) {
9950
9754
  try {
9951
9755
  migrateRepoIssued(cwd);
9952
9756
  const own = hash(issued);
9953
- const tokens = path7.join(userTokenRoot(), hash(lead));
9954
- fs7.mkdirSync(tokens, { recursive: true, mode: 448 });
9955
- fs7.writeFileSync(path7.join(tokens, own), `${tokenHash(token)}
9757
+ const tokens = path6.join(userTokenRoot(), hash(lead));
9758
+ fs6.mkdirSync(tokens, { recursive: true, mode: 448 });
9759
+ fs6.writeFileSync(path6.join(tokens, own), `${tokenHash(token)}
9956
9760
  `, { mode: 384, flag: "wx" });
9957
- const dir = path7.join(userIssuedRoot(), hash(lead));
9958
- fs7.mkdirSync(dir, { recursive: true, mode: 448 });
9959
- fs7.writeFileSync(path7.join(dir, own), `${ISSUED_SESSIONS_MARKER}
9761
+ const dir = path6.join(userIssuedRoot(), hash(lead));
9762
+ fs6.mkdirSync(dir, { recursive: true, mode: 448 });
9763
+ fs6.writeFileSync(path6.join(dir, own), `${ISSUED_SESSIONS_MARKER}
9960
9764
  `, { mode: 384 });
9961
9765
  pruneIssued(dir, own, tokens);
9962
9766
  return token;
@@ -9966,13 +9770,13 @@ function recordIssuedSession(cwd, lead, issued) {
9966
9770
  }
9967
9771
  function pruneIssued(dir, own, tokens) {
9968
9772
  try {
9969
- const entries = fs7.readdirSync(dir);
9773
+ const entries = fs6.readdirSync(dir);
9970
9774
  if (entries.length <= ISSUED_PER_LEAD)
9971
9775
  return;
9972
- const byAge = entries.filter((name) => name !== own).map((name) => ({ name, at: fs7.statSync(path7.join(dir, name)).mtimeMs })).toSorted((a, b) => a.at - b.at);
9776
+ const byAge = entries.filter((name) => name !== own).map((name) => ({ name, at: fs6.statSync(path6.join(dir, name)).mtimeMs })).toSorted((a, b) => a.at - b.at);
9973
9777
  for (const { name } of byAge.slice(0, entries.length - ISSUED_PER_LEAD)) {
9974
- fs7.rmSync(path7.join(dir, name), { force: true });
9975
- fs7.rmSync(path7.join(tokens, name), { force: true });
9778
+ fs6.rmSync(path6.join(dir, name), { force: true });
9779
+ fs6.rmSync(path6.join(tokens, name), { force: true });
9976
9780
  }
9977
9781
  } catch {}
9978
9782
  }
@@ -9982,7 +9786,7 @@ var issuedRecord = (cwd, claimed) => {
9982
9786
  return null;
9983
9787
  migrateRepoIssued(cwd);
9984
9788
  try {
9985
- return fs7.readFileSync(path7.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
9789
+ return fs6.readFileSync(path6.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
9986
9790
  } catch {
9987
9791
  return null;
9988
9792
  }
@@ -9992,9 +9796,9 @@ var issuedToken = (claimed) => {
9992
9796
  const cut = claimed.lastIndexOf(":");
9993
9797
  if (cut <= 0)
9994
9798
  return { stored: null, leadBound: false };
9995
- const dir = path7.join(userTokenRoot(), hash(claimed.slice(0, cut)));
9799
+ const dir = path6.join(userTokenRoot(), hash(claimed.slice(0, cut)));
9996
9800
  try {
9997
- const text = fs7.readFileSync(path7.join(dir, hash(claimed)), "utf8");
9801
+ const text = fs6.readFileSync(path6.join(dir, hash(claimed)), "utf8");
9998
9802
  return { stored: TOKEN_HASH.exec(text)?.[1] ?? null, leadBound: true };
9999
9803
  } catch {
10000
9804
  return { stored: null, leadBound: safeList(dir).length > 0 };
@@ -10013,21 +9817,21 @@ var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
10013
9817
 
10014
9818
  // packages/workit-core/src/core/package-root.ts
10015
9819
  import { existsSync } from "node:fs";
10016
- import path8 from "node:path";
9820
+ import path7 from "node:path";
10017
9821
  import { fileURLToPath } from "node:url";
10018
9822
  var packageRoot = () => {
10019
- let dir = path8.dirname(fileURLToPath(import.meta.url));
9823
+ let dir = path7.dirname(fileURLToPath(import.meta.url));
10020
9824
  while (true) {
10021
- const parent = path8.dirname(dir);
10022
- if (existsSync(path8.join(dir, "package.json")) || parent === dir)
9825
+ const parent = path7.dirname(dir);
9826
+ if (existsSync(path7.join(dir, "package.json")) || parent === dir)
10023
9827
  return dir;
10024
9828
  dir = parent;
10025
9829
  }
10026
9830
  };
10027
9831
 
10028
9832
  // packages/workit-core/src/core/store-lock.ts
10029
- import { spawnSync as spawnSync4 } from "node:child_process";
10030
- import fs8 from "node:fs";
9833
+ import { spawnSync as spawnSync3 } from "node:child_process";
9834
+ import fs7 from "node:fs";
10031
9835
  import { hostname } from "node:os";
10032
9836
  var metadataLockSchema = object({
10033
9837
  pid: number2().int().nonnegative().safe(),
@@ -10071,14 +9875,14 @@ var parseProcStatStart = (stat) => {
10071
9875
  var processStartOf = (pid) => {
10072
9876
  if (process.platform === "linux") {
10073
9877
  try {
10074
- return parseProcStatStart(fs8.readFileSync(`/proc/${pid}/stat`, "utf8"));
9878
+ return parseProcStatStart(fs7.readFileSync(`/proc/${pid}/stat`, "utf8"));
10075
9879
  } catch {
10076
9880
  return null;
10077
9881
  }
10078
9882
  }
10079
9883
  if (process.platform === "darwin" || process.platform === "freebsd") {
10080
9884
  try {
10081
- const run = spawnSync4("ps", ["-o", "lstart=", "-p", String(pid)], {
9885
+ const run = spawnSync3("ps", ["-o", "lstart=", "-p", String(pid)], {
10082
9886
  encoding: "utf8",
10083
9887
  timeout: 1000
10084
9888
  });
@@ -10092,7 +9896,7 @@ var processStartOf = (pid) => {
10092
9896
  };
10093
9897
  var readTrimmed = (file) => {
10094
9898
  try {
10095
- return fs8.readFileSync(file, "utf8").trim() || null;
9899
+ return fs7.readFileSync(file, "utf8").trim() || null;
10096
9900
  } catch {
10097
9901
  return null;
10098
9902
  }
@@ -10103,7 +9907,7 @@ var localLockHost = () => {
10103
9907
  return cachedLockHost;
10104
9908
  let pidns = null;
10105
9909
  try {
10106
- pidns = /\[(\d+)\]/.exec(fs8.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
9910
+ pidns = /\[(\d+)\]/.exec(fs7.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
10107
9911
  } catch {}
10108
9912
  const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
10109
9913
  cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
@@ -10149,7 +9953,7 @@ var classifyLockOwner = (payload, ageMs, localHost = localLockHost(), options =
10149
9953
  };
10150
9954
  var ageOf = (file, nowMs) => {
10151
9955
  try {
10152
- return nowMs - fs8.lstatSync(file).mtimeMs;
9956
+ return nowMs - fs7.lstatSync(file).mtimeMs;
10153
9957
  } catch {
10154
9958
  return null;
10155
9959
  }
@@ -10160,7 +9964,7 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
10160
9964
  if (age === null || age <= RECLAIM_GUARD_TTL_MS)
10161
9965
  return false;
10162
9966
  try {
10163
- fs8.rmdirSync(guard);
9967
+ fs7.rmdirSync(guard);
10164
9968
  return true;
10165
9969
  } catch {
10166
9970
  return false;
@@ -10169,8 +9973,8 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
10169
9973
 
10170
9974
  // packages/workit-core/src/store/event-log.ts
10171
9975
  import { randomUUID as randomUUID2 } from "node:crypto";
10172
- import fs9 from "node:fs";
10173
- import path9 from "node:path";
9976
+ import fs8 from "node:fs";
9977
+ import path8 from "node:path";
10174
9978
  var EVENT_VERSION = 1;
10175
9979
 
10176
9980
  class LogDamage extends Error {
@@ -10199,7 +10003,7 @@ var parseEvent = (line, file, offset) => {
10199
10003
  function readLog(file, from = 0, expectSeq = null) {
10200
10004
  let fd;
10201
10005
  try {
10202
- fd = fs9.openSync(file, "r");
10006
+ fd = fs8.openSync(file, "r");
10203
10007
  } catch (error) {
10204
10008
  if (error.code === "ENOENT")
10205
10009
  return null;
@@ -10208,13 +10012,13 @@ function readLog(file, from = 0, expectSeq = null) {
10208
10012
  let bytes;
10209
10013
  let size;
10210
10014
  try {
10211
- size = fs9.fstatSync(fd).size;
10015
+ size = fs8.fstatSync(fd).size;
10212
10016
  if (from > size)
10213
10017
  throw new LogDamage(`${file}: shorter than its snapshot`);
10214
10018
  bytes = Buffer.alloc(size - from);
10215
10019
  let read = 0;
10216
10020
  while (read < bytes.length) {
10217
- const got = fs9.readSync(fd, bytes, read, bytes.length - read, from + read);
10021
+ const got = fs8.readSync(fd, bytes, read, bytes.length - read, from + read);
10218
10022
  if (got === 0)
10219
10023
  break;
10220
10024
  read += got;
@@ -10222,7 +10026,7 @@ function readLog(file, from = 0, expectSeq = null) {
10222
10026
  bytes = bytes.subarray(0, read);
10223
10027
  size = from + read;
10224
10028
  } finally {
10225
- fs9.closeSync(fd);
10029
+ fs8.closeSync(fd);
10226
10030
  }
10227
10031
  const events = [];
10228
10032
  let start = 0;
@@ -10247,11 +10051,11 @@ function readLog(file, from = 0, expectSeq = null) {
10247
10051
  }
10248
10052
  var fsyncDirectory = (dir) => {
10249
10053
  try {
10250
- const fd = fs9.openSync(dir, "r");
10054
+ const fd = fs8.openSync(dir, "r");
10251
10055
  try {
10252
- fs9.fsyncSync(fd);
10056
+ fs8.fsyncSync(fd);
10253
10057
  } finally {
10254
- fs9.closeSync(fd);
10058
+ fs8.closeSync(fd);
10255
10059
  }
10256
10060
  } catch (error) {
10257
10061
  if (process.platform !== "win32")
@@ -10261,46 +10065,46 @@ var fsyncDirectory = (dir) => {
10261
10065
  function appendEvent(file, event, validEnd) {
10262
10066
  const line = Buffer.from(`${JSON.stringify(event)}
10263
10067
  `, "utf8");
10264
- const created = !fs9.existsSync(file);
10265
- if (!created && fs9.statSync(file).size !== validEnd) {
10266
- const cut = fs9.openSync(file, "r+");
10068
+ const created = !fs8.existsSync(file);
10069
+ if (!created && fs8.statSync(file).size !== validEnd) {
10070
+ const cut = fs8.openSync(file, "r+");
10267
10071
  try {
10268
- fs9.ftruncateSync(cut, validEnd);
10269
- fs9.fsyncSync(cut);
10072
+ fs8.ftruncateSync(cut, validEnd);
10073
+ fs8.fsyncSync(cut);
10270
10074
  } finally {
10271
- fs9.closeSync(cut);
10075
+ fs8.closeSync(cut);
10272
10076
  }
10273
10077
  }
10274
- const fd = fs9.openSync(file, "a", 384);
10078
+ const fd = fs8.openSync(file, "a", 384);
10275
10079
  try {
10276
10080
  let written = 0;
10277
10081
  while (written < line.length)
10278
- written += fs9.writeSync(fd, line, written, line.length - written);
10279
- fs9.fsyncSync(fd);
10082
+ written += fs8.writeSync(fd, line, written, line.length - written);
10083
+ fs8.fsyncSync(fd);
10280
10084
  } finally {
10281
- fs9.closeSync(fd);
10085
+ fs8.closeSync(fd);
10282
10086
  }
10283
10087
  if (created)
10284
- fsyncDirectory(path9.dirname(file));
10088
+ fsyncDirectory(path8.dirname(file));
10285
10089
  return validEnd;
10286
10090
  }
10287
10091
  function replaceFile(file, bytes, durable) {
10288
10092
  const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
10289
10093
  try {
10290
- const fd = fs9.openSync(temporary, "wx", 384);
10094
+ const fd = fs8.openSync(temporary, "wx", 384);
10291
10095
  try {
10292
- fs9.writeSync(fd, bytes);
10096
+ fs8.writeSync(fd, bytes);
10293
10097
  if (durable)
10294
- fs9.fsyncSync(fd);
10098
+ fs8.fsyncSync(fd);
10295
10099
  } finally {
10296
- fs9.closeSync(fd);
10100
+ fs8.closeSync(fd);
10297
10101
  }
10298
10102
  renameWithRetry(temporary, file);
10299
10103
  if (durable)
10300
- fsyncDirectory(path9.dirname(file));
10104
+ fsyncDirectory(path8.dirname(file));
10301
10105
  } catch (error) {
10302
10106
  try {
10303
- fs9.unlinkSync(temporary);
10107
+ fs8.unlinkSync(temporary);
10304
10108
  } catch {}
10305
10109
  throw error;
10306
10110
  }
@@ -10325,7 +10129,7 @@ var retryTransient = (run) => {
10325
10129
  }
10326
10130
  }
10327
10131
  };
10328
- var renameWithRetry = (from, to) => retryTransient(() => fs9.renameSync(from, to));
10132
+ var renameWithRetry = (from, to) => retryTransient(() => fs8.renameSync(from, to));
10329
10133
 
10330
10134
  // packages/workit-core/src/store/patch.ts
10331
10135
  var isPlainObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -10622,7 +10426,7 @@ var indexEntry = (task, key, legacy, file) => {
10622
10426
  };
10623
10427
  var fileSignature = (file) => {
10624
10428
  try {
10625
- const stat = fs10.statSync(file, { bigint: true });
10429
+ const stat = fs9.statSync(file, { bigint: true });
10626
10430
  return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
10627
10431
  } catch {
10628
10432
  return null;
@@ -10640,7 +10444,7 @@ var cachedRuntimeVersion = null;
10640
10444
  var runtimeVersion = () => {
10641
10445
  if (cachedRuntimeVersion === null) {
10642
10446
  try {
10643
- const pkg = JSON.parse(fs10.readFileSync(path10.join(packageRoot(), "package.json"), "utf8"));
10447
+ const pkg = JSON.parse(fs9.readFileSync(path9.join(packageRoot(), "package.json"), "utf8"));
10644
10448
  cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
10645
10449
  } catch {
10646
10450
  cachedRuntimeVersion = "0.0.0";
@@ -10666,14 +10470,14 @@ var isNewerVersion = (candidate, current) => {
10666
10470
  var validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value);
10667
10471
  var readText = (file) => {
10668
10472
  try {
10669
- return fs10.readFileSync(file, "utf8");
10473
+ return fs9.readFileSync(file, "utf8");
10670
10474
  } catch {
10671
10475
  return "";
10672
10476
  }
10673
10477
  };
10674
10478
  var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
10675
10479
  var branchStart = (location, branch, aliases) => {
10676
- const first = readText(path10.join(path10.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
10480
+ const first = readText(path9.join(path9.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
10677
10481
  `, 1)[0];
10678
10482
  const created = REFLOG_LINE.exec(first);
10679
10483
  const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
@@ -10683,7 +10487,7 @@ var branchStart = (location, branch, aliases) => {
10683
10487
  return start;
10684
10488
  if (!location.git)
10685
10489
  return null;
10686
- for (const line of readText(path10.join(location.git.gitDir, "logs", "HEAD")).split(`
10490
+ for (const line of readText(path9.join(location.git.gitDir, "logs", "HEAD")).split(`
10687
10491
  `)) {
10688
10492
  const entry = REFLOG_LINE.exec(line);
10689
10493
  const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
@@ -10693,23 +10497,23 @@ var branchStart = (location, branch, aliases) => {
10693
10497
  }
10694
10498
  return null;
10695
10499
  };
10696
- var refExists = (common, ref) => fs10.existsSync(path10.join(common, ref)) || readText(path10.join(common, "packed-refs")).includes(` ${ref}
10500
+ var refExists = (common, ref) => fs9.existsSync(path9.join(common, ref)) || readText(path9.join(common, "packed-refs")).includes(` ${ref}
10697
10501
  `);
10698
10502
  var origins = new Map;
10699
10503
  var sameDirectoryIdentity = (left, right) => {
10700
- if (!path10.isAbsolute(left) || !path10.isAbsolute(right))
10504
+ if (!path9.isAbsolute(left) || !path9.isAbsolute(right))
10701
10505
  return false;
10702
- const normalizedLeft = path10.resolve(left);
10703
- const normalizedRight = path10.resolve(right);
10506
+ const normalizedLeft = path9.resolve(left);
10507
+ const normalizedRight = path9.resolve(right);
10704
10508
  try {
10705
- const a = fs10.statSync(normalizedLeft, { bigint: true });
10706
- const b = fs10.statSync(normalizedRight, { bigint: true });
10509
+ const a = fs9.statSync(normalizedLeft, { bigint: true });
10510
+ const b = fs9.statSync(normalizedRight, { bigint: true });
10707
10511
  if (!a.isDirectory() || !b.isDirectory())
10708
10512
  return false;
10709
10513
  if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
10710
10514
  return true;
10711
- const canonicalLeft = fs10.realpathSync(normalizedLeft);
10712
- const canonicalRight = fs10.realpathSync(normalizedRight);
10515
+ const canonicalLeft = fs9.realpathSync(normalizedLeft);
10516
+ const canonicalRight = fs9.realpathSync(normalizedRight);
10713
10517
  if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
10714
10518
  return true;
10715
10519
  return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
@@ -10746,8 +10550,8 @@ class TaskStore {
10746
10550
  migrationChecked = false;
10747
10551
  compactDue = new Set;
10748
10552
  constructor(root, options = {}) {
10749
- const resolved = path10.resolve(root);
10750
- this.origin = fs10.existsSync(resolved) ? fs10.realpathSync(resolved) : resolved;
10553
+ const resolved = path9.resolve(root);
10554
+ this.origin = fs9.existsSync(resolved) ? fs9.realpathSync(resolved) : resolved;
10751
10555
  this.located = resolveStore(this.origin);
10752
10556
  this.root = checkoutRootOf(this.origin, this.located);
10753
10557
  this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
@@ -10755,7 +10559,7 @@ class TaskStore {
10755
10559
  this.migrateOnRead = options.migrateOnRead;
10756
10560
  }
10757
10561
  location() {
10758
- if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs10.existsSync(path10.join(this.root, ".git"))) {
10562
+ if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs9.existsSync(path9.join(this.root, ".git"))) {
10759
10563
  this.located = undefined;
10760
10564
  this.migrationChecked = false;
10761
10565
  }
@@ -10819,7 +10623,7 @@ class TaskStore {
10819
10623
  const tasksDir = this.paths().tasks;
10820
10624
  let names;
10821
10625
  try {
10822
- names = fs10.existsSync(tasksDir) ? fs10.readdirSync(tasksDir).filter((name) => validId(name)) : [];
10626
+ names = fs9.existsSync(tasksDir) ? fs9.readdirSync(tasksDir).filter((name) => validId(name)) : [];
10823
10627
  } catch (error) {
10824
10628
  return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
10825
10629
  path: tasksDir
@@ -10860,7 +10664,7 @@ class TaskStore {
10860
10664
  const file = this.paths().workspace;
10861
10665
  let bytes;
10862
10666
  try {
10863
- bytes = retryTransient(() => fs10.readFileSync(file, "utf8"));
10667
+ bytes = retryTransient(() => fs9.readFileSync(file, "utf8"));
10864
10668
  } catch (error) {
10865
10669
  if (error.code === "ENOENT")
10866
10670
  return success(null, null, null);
@@ -10911,7 +10715,7 @@ class TaskStore {
10911
10715
  const moved = this.bindLocked(again.data.id, key, input.now);
10912
10716
  return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
10913
10717
  }
10914
- const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path10.basename(this.root) || this.root;
10718
+ const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path9.basename(this.root) || this.root;
10915
10719
  const intent = {
10916
10720
  objective: input.objective?.trim() || `Work on ${label}`,
10917
10721
  scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
@@ -10977,14 +10781,14 @@ class TaskStore {
10977
10781
  const branch = key.ok ? key.data.branch : null;
10978
10782
  if (!branch || !location.ok)
10979
10783
  return success(null, null, null);
10980
- const common = path10.dirname(location.data.dir);
10981
- const heads = path10.join(common, "logs", "refs", "heads");
10982
- const headLog = location.data.git ? path10.join(location.data.git.gitDir, "logs", "HEAD") : "";
10784
+ const common = path9.dirname(location.data.dir);
10785
+ const heads = path9.join(common, "logs", "refs", "heads");
10786
+ const headLog = location.data.git ? path9.join(location.data.git.gitDir, "logs", "HEAD") : "";
10983
10787
  const signatures = [
10984
- path10.join(heads, branch),
10788
+ path9.join(heads, branch),
10985
10789
  headLog,
10986
10790
  heads,
10987
- path10.join(common, "packed-refs")
10791
+ path9.join(common, "packed-refs")
10988
10792
  ].map((file) => fileSignature(file) ?? "-");
10989
10793
  const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
10990
10794
  let resolved = origins.get(slot);
@@ -10996,7 +10800,7 @@ class TaskStore {
10996
10800
  aliases.push(rename.from);
10997
10801
  const start = branchStart(location.data, branch, aliases);
10998
10802
  const slash = start?.indexOf("/") ?? -1;
10999
- const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path10.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
10803
+ const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path9.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
11000
10804
  const name = start && (remote ? start.slice(slash + 1) : start);
11001
10805
  resolved = {
11002
10806
  signature: signatures.join("|"),
@@ -11234,13 +11038,13 @@ class TaskStore {
11234
11038
  if (!ready.ok)
11235
11039
  return ready;
11236
11040
  const dirs = this.paths();
11237
- const recovery = path10.join(this.root, ".workit", "recovery");
11238
- if (fs10.existsSync(recovery)) {
11041
+ const recovery = path9.join(this.root, ".workit", "recovery");
11042
+ if (fs9.existsSync(recovery)) {
11239
11043
  let files = 0;
11240
11044
  let bytes = 0;
11241
11045
  try {
11242
- for (const name of fs10.readdirSync(recovery)) {
11243
- const stat = fs10.lstatSync(path10.join(recovery, name));
11046
+ for (const name of fs9.readdirSync(recovery)) {
11047
+ const stat = fs9.lstatSync(path9.join(recovery, name));
11244
11048
  if (!stat.isFile())
11245
11049
  continue;
11246
11050
  files += 1;
@@ -11249,17 +11053,17 @@ class TaskStore {
11249
11053
  } catch {}
11250
11054
  const remove = options.pruneRecovery === true && !dryRun;
11251
11055
  if (remove)
11252
- fs10.rmSync(recovery, { recursive: true, force: true });
11056
+ fs9.rmSync(recovery, { recursive: true, force: true });
11253
11057
  report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
11254
11058
  }
11255
- if (!fs10.existsSync(dirs.tasks))
11059
+ if (!fs9.existsSync(dirs.tasks))
11256
11060
  return success(null, null, report);
11257
- const names = fs10.readdirSync(dirs.tasks).filter((name) => validId(name));
11061
+ const names = fs9.readdirSync(dirs.tasks).filter((name) => validId(name));
11258
11062
  for (const name of names) {
11259
11063
  const file = this.eventsPath(name);
11260
11064
  let size = 0;
11261
11065
  try {
11262
- size = fs10.statSync(file).size;
11066
+ size = fs9.statSync(file).size;
11263
11067
  } catch {
11264
11068
  continue;
11265
11069
  }
@@ -11287,16 +11091,16 @@ class TaskStore {
11287
11091
  const references = new Set;
11288
11092
  for (const name of names)
11289
11093
  try {
11290
- for (const match of fs10.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
11094
+ for (const match of fs9.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
11291
11095
  references.add(match[1]);
11292
11096
  } catch {}
11293
- if (fs10.existsSync(dirs.blobs))
11294
- for (const name of fs10.readdirSync(dirs.blobs)) {
11097
+ if (fs9.existsSync(dirs.blobs))
11098
+ for (const name of fs9.readdirSync(dirs.blobs)) {
11295
11099
  const digest = name.replace(/\.json$/, "");
11296
- const file = path10.join(dirs.blobs, name);
11100
+ const file = path9.join(dirs.blobs, name);
11297
11101
  let stat;
11298
11102
  try {
11299
- stat = fs10.lstatSync(file);
11103
+ stat = fs9.lstatSync(file);
11300
11104
  } catch {
11301
11105
  continue;
11302
11106
  }
@@ -11307,11 +11111,11 @@ class TaskStore {
11307
11111
  report.blobs.removed += 1;
11308
11112
  report.blobs.removedBytes += stat.size;
11309
11113
  if (!dryRun)
11310
- fs10.rmSync(file, { force: true });
11114
+ fs9.rmSync(file, { force: true });
11311
11115
  }
11312
11116
  return success(null, null, null);
11313
11117
  };
11314
- const swept = dryRun ? sweep() : this.withLock(path10.join(dirs.dir, "gc.lock"), sweep);
11118
+ const swept = dryRun ? sweep() : this.withLock(path9.join(dirs.dir, "gc.lock"), sweep);
11315
11119
  if (!swept.ok)
11316
11120
  return swept;
11317
11121
  const temporaryDirs = [
@@ -11319,17 +11123,17 @@ class TaskStore {
11319
11123
  dirs.tasks,
11320
11124
  dirs.blobs,
11321
11125
  dirs.checkout,
11322
- ...names.map((name) => path10.join(dirs.tasks, name))
11126
+ ...names.map((name) => path9.join(dirs.tasks, name))
11323
11127
  ];
11324
11128
  for (const directory of temporaryDirs) {
11325
- if (!fs10.existsSync(directory))
11129
+ if (!fs9.existsSync(directory))
11326
11130
  continue;
11327
- for (const name of fs10.readdirSync(directory)) {
11131
+ for (const name of fs9.readdirSync(directory)) {
11328
11132
  if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
11329
11133
  continue;
11330
- const file = path10.join(directory, name);
11134
+ const file = path9.join(directory, name);
11331
11135
  try {
11332
- const stat = fs10.lstatSync(file);
11136
+ const stat = fs9.lstatSync(file);
11333
11137
  if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
11334
11138
  continue;
11335
11139
  } catch {
@@ -11337,37 +11141,37 @@ class TaskStore {
11337
11141
  }
11338
11142
  report.temporary.removed += 1;
11339
11143
  if (!dryRun)
11340
- fs10.rmSync(file, { force: true });
11144
+ fs9.rmSync(file, { force: true });
11341
11145
  }
11342
11146
  }
11343
11147
  return success(null, null, report);
11344
11148
  }
11345
11149
  paths() {
11346
11150
  const location = this.location();
11347
- const dir = location.ok ? location.data.dir : path10.join(this.root, ".workit");
11348
- const checkout = path10.join(dir, "checkouts", checkoutSlug(this.root));
11151
+ const dir = location.ok ? location.data.dir : path9.join(this.root, ".workit");
11152
+ const checkout = path9.join(dir, "checkouts", checkoutSlug(this.root));
11349
11153
  return {
11350
11154
  dir,
11351
11155
  shared: location.ok ? location.data.shared : false,
11352
- marker: path10.join(dir, "store.json"),
11156
+ marker: path9.join(dir, "store.json"),
11353
11157
  checkout,
11354
- workspace: path10.join(checkout, "workspace.json"),
11355
- checkoutLock: path10.join(checkout, "metadata.lock"),
11356
- tasks: path10.join(dir, "tasks"),
11357
- index: path10.join(dir, "task-index.json"),
11358
- blobs: path10.join(dir, "blobs", "candidates"),
11359
- legacy: path10.join(dir, "legacy", checkoutSlug(this.root), "v2"),
11360
- v2: path10.join(this.root, ".workit")
11158
+ workspace: path9.join(checkout, "workspace.json"),
11159
+ checkoutLock: path9.join(checkout, "metadata.lock"),
11160
+ tasks: path9.join(dir, "tasks"),
11161
+ index: path9.join(dir, "task-index.json"),
11162
+ blobs: path9.join(dir, "blobs", "candidates"),
11163
+ legacy: path9.join(dir, "legacy", checkoutSlug(this.root), "v2"),
11164
+ v2: path9.join(this.root, ".workit")
11361
11165
  };
11362
11166
  }
11363
11167
  eventsPath(taskId) {
11364
- return path10.join(this.paths().tasks, taskId, "events.jsonl");
11168
+ return path9.join(this.paths().tasks, taskId, "events.jsonl");
11365
11169
  }
11366
11170
  snapshotPath(taskId) {
11367
- return path10.join(this.paths().tasks, taskId, "snapshot.json");
11171
+ return path9.join(this.paths().tasks, taskId, "snapshot.json");
11368
11172
  }
11369
11173
  taskLockPath(taskId) {
11370
- return path10.join(this.paths().tasks, taskId, "lock");
11174
+ return path9.join(this.paths().tasks, taskId, "lock");
11371
11175
  }
11372
11176
  ready(write = false) {
11373
11177
  const location = this.location();
@@ -11377,7 +11181,7 @@ class TaskStore {
11377
11181
  return success(null, null, null);
11378
11182
  const dirs = this.paths();
11379
11183
  try {
11380
- const marker = JSON.parse(fs10.readFileSync(dirs.marker, "utf8"));
11184
+ const marker = JSON.parse(fs9.readFileSync(dirs.marker, "utf8"));
11381
11185
  if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
11382
11186
  return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
11383
11187
  } catch {}
@@ -11406,21 +11210,21 @@ class TaskStore {
11406
11210
  return {
11407
11211
  read: (digest) => {
11408
11212
  try {
11409
- return JSON.parse(fs10.readFileSync(path10.join(dir, `${digest}.json`), "utf8"));
11213
+ return JSON.parse(fs9.readFileSync(path9.join(dir, `${digest}.json`), "utf8"));
11410
11214
  } catch (error) {
11411
11215
  throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
11412
11216
  }
11413
11217
  },
11414
11218
  write: (digest, value) => {
11415
- const file = path10.join(dir, `${digest}.json`);
11416
- if (fs10.existsSync(file)) {
11219
+ const file = path9.join(dir, `${digest}.json`);
11220
+ if (fs9.existsSync(file)) {
11417
11221
  const stamp = new Date;
11418
11222
  try {
11419
- fs10.utimesSync(file, stamp, stamp);
11223
+ fs9.utimesSync(file, stamp, stamp);
11420
11224
  return;
11421
11225
  } catch {}
11422
11226
  }
11423
- fs10.mkdirSync(dir, { recursive: true });
11227
+ fs9.mkdirSync(dir, { recursive: true });
11424
11228
  replaceFile(file, JSON.stringify(value), true);
11425
11229
  }
11426
11230
  };
@@ -11502,7 +11306,7 @@ class TaskStore {
11502
11306
  }
11503
11307
  readSnapshot(taskId) {
11504
11308
  try {
11505
- const value = JSON.parse(fs10.readFileSync(this.snapshotPath(taskId), "utf8"));
11309
+ const value = JSON.parse(fs9.readFileSync(this.snapshotPath(taskId), "utf8"));
11506
11310
  if (!isObject4(value) || value.v !== 1 || value.task !== taskId || typeof value.seq !== "number" || typeof value.lastId !== "string" || typeof value.lastStart !== "number" || typeof value.end !== "number" || !isObject4(value.record))
11507
11311
  return null;
11508
11312
  return value;
@@ -11604,8 +11408,8 @@ class TaskStore {
11604
11408
  }
11605
11409
  }
11606
11410
  openTask(record, type, key, legacy = null) {
11607
- const dir = path10.join(this.paths().tasks, record.id);
11608
- fs10.mkdirSync(dir, { recursive: true });
11411
+ const dir = path9.join(this.paths().tasks, record.id);
11412
+ fs9.mkdirSync(dir, { recursive: true });
11609
11413
  const blobs = this.blobStore();
11610
11414
  const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
11611
11415
  const event = this.event(record.id, 1, type, {
@@ -11657,11 +11461,11 @@ class TaskStore {
11657
11461
  } catch {}
11658
11462
  if (!latest || latest.events[0]?.id !== last.id)
11659
11463
  return failure2("busy", "the task log changed during compaction; retry", { taskId });
11660
- const appended = fs10.readFileSync(file).subarray(log.end, latest.end);
11464
+ const appended = fs9.readFileSync(file).subarray(log.end, latest.end);
11661
11465
  const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
11662
11466
  replaceFile(file, bytes.toString("utf8"), true);
11663
11467
  try {
11664
- fs10.rmSync(this.snapshotPath(taskId), { force: true });
11468
+ fs9.rmSync(this.snapshotPath(taskId), { force: true });
11665
11469
  } catch {}
11666
11470
  const reloaded = this.load(taskId);
11667
11471
  if (reloaded.ok && reloaded.data)
@@ -11674,7 +11478,7 @@ class TaskStore {
11674
11478
  }
11675
11479
  countEvents(file) {
11676
11480
  try {
11677
- const bytes = fs10.readFileSync(file);
11481
+ const bytes = fs9.readFileSync(file);
11678
11482
  let count = 0;
11679
11483
  for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
11680
11484
  count += 1;
@@ -11697,7 +11501,7 @@ class TaskStore {
11697
11501
  }
11698
11502
  withKeyLock(key, operation) {
11699
11503
  const digest = createHash6("sha256").update(key).digest("hex").slice(0, 32);
11700
- return this.withLock(path10.join(this.paths().dir, "keys", `${digest}.lock`), operation);
11504
+ return this.withLock(path9.join(this.paths().dir, "keys", `${digest}.lock`), operation);
11701
11505
  }
11702
11506
  bindLocked(taskId, key, timestamp) {
11703
11507
  const current = this.readWorkspace();
@@ -11838,7 +11642,7 @@ class TaskStore {
11838
11642
  writeWorkspace(record) {
11839
11643
  const file = this.paths().workspace;
11840
11644
  try {
11841
- fs10.mkdirSync(path10.dirname(file), { recursive: true });
11645
+ fs9.mkdirSync(path9.dirname(file), { recursive: true });
11842
11646
  replaceFile(file, `${canonicalJson(record)}
11843
11647
  `, true);
11844
11648
  return success(null, null, null);
@@ -11848,7 +11652,7 @@ class TaskStore {
11848
11652
  }
11849
11653
  readIndex() {
11850
11654
  try {
11851
- const value = JSON.parse(fs10.readFileSync(this.paths().index, "utf8"));
11655
+ const value = JSON.parse(fs9.readFileSync(this.paths().index, "utf8"));
11852
11656
  if (!isObject4(value) || value.version !== INDEX_VERSION || !isObject4(value.tasks))
11853
11657
  return {};
11854
11658
  const tasks = {};
@@ -11862,7 +11666,7 @@ class TaskStore {
11862
11666
  }
11863
11667
  writeIndex(entries) {
11864
11668
  try {
11865
- fs10.mkdirSync(this.paths().tasks, { recursive: true });
11669
+ fs9.mkdirSync(this.paths().tasks, { recursive: true });
11866
11670
  replaceFile(this.paths().index, JSON.stringify({
11867
11671
  version: INDEX_VERSION,
11868
11672
  tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
@@ -11916,23 +11720,23 @@ class TaskStore {
11916
11720
  return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
11917
11721
  }
11918
11722
  v2Dirs() {
11919
- return [...new Set([this.root, this.origin])].map((dir) => path10.join(dir, ".workit"));
11723
+ return [...new Set([this.root, this.origin])].map((dir) => path9.join(dir, ".workit"));
11920
11724
  }
11921
11725
  pendingMigrations() {
11922
11726
  return this.v2Dirs().filter((dir) => {
11923
- const raw = this.readV2Workspace(path10.join(dir, "workspace.json"));
11727
+ const raw = this.readV2Workspace(path9.join(dir, "workspace.json"));
11924
11728
  return raw !== null && raw !== TOMBSTONE;
11925
11729
  });
11926
11730
  }
11927
11731
  migrateV2(v2) {
11928
11732
  const dirs = this.paths();
11929
- const v2Workspace = path10.join(v2, "workspace.json");
11930
- const v2Tasks = path10.join(v2, "tasks");
11931
- const legacy = path10.join(dirs.dir, "legacy", checkoutSlug(path10.dirname(v2)), "v2");
11733
+ const v2Workspace = path9.join(v2, "workspace.json");
11734
+ const v2Tasks = path9.join(v2, "tasks");
11735
+ const legacy = path9.join(dirs.dir, "legacy", checkoutSlug(path9.dirname(v2)), "v2");
11932
11736
  const raw = this.readV2Workspace(v2Workspace);
11933
11737
  if (raw === null || raw === TOMBSTONE)
11934
11738
  return success(null, null, null);
11935
- return this.withLock(dirs.checkoutLock, () => this.withLock(path10.join(v2, "metadata.lock"), () => {
11739
+ return this.withLock(dirs.checkoutLock, () => this.withLock(path9.join(v2, "metadata.lock"), () => {
11936
11740
  const workspace = this.readV2Workspace(v2Workspace);
11937
11741
  if (workspace === null || workspace === TOMBSTONE)
11938
11742
  return success(null, null, null);
@@ -11941,7 +11745,7 @@ class TaskStore {
11941
11745
  return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
11942
11746
  let files = [];
11943
11747
  try {
11944
- files = fs10.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path10.join(v2Tasks, name)));
11748
+ files = fs9.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path9.join(v2Tasks, name)));
11945
11749
  } catch {}
11946
11750
  const current = this.readWorkspace();
11947
11751
  if (!current.ok)
@@ -11959,22 +11763,22 @@ class TaskStore {
11959
11763
  let skipped = 0;
11960
11764
  const done = new Map;
11961
11765
  try {
11962
- fs10.mkdirSync(path10.join(legacy, "tasks"), { recursive: true });
11766
+ fs9.mkdirSync(path9.join(legacy, "tasks"), { recursive: true });
11963
11767
  for (const name of ["workspace.json", "index.json"])
11964
- if (fs10.existsSync(path10.join(v2, name)))
11965
- fs10.copyFileSync(path10.join(v2, name), path10.join(legacy, name));
11768
+ if (fs9.existsSync(path9.join(v2, name)))
11769
+ fs9.copyFileSync(path9.join(v2, name), path9.join(legacy, name));
11966
11770
  for (const name of files) {
11967
11771
  const id = name.slice(0, -5);
11968
- const file = path10.join(v2Tasks, name);
11969
- const bytes = fs10.readFileSync(file, "utf8");
11772
+ const file = path9.join(v2Tasks, name);
11773
+ const bytes = fs9.readFileSync(file, "utf8");
11970
11774
  const digest = sha256(bytes);
11971
- fs10.writeFileSync(path10.join(legacy, "tasks", name), bytes);
11775
+ fs9.writeFileSync(path9.join(legacy, "tasks", name), bytes);
11972
11776
  const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
11973
11777
  if (!parsed.ok || parsed.data.id !== id)
11974
11778
  return failure2("recovery_required", `cannot migrate 2.x task ${id}: ${parsed.ok ? "file name and record ID differ" : parsed.error}; move ${file} aside to continue`, { path: file });
11975
11779
  const record = { ...parsed.data, workspaceId: target.id };
11976
- const origin = { path: path10.join(legacy, "tasks", name), digest };
11977
- if (!fs10.existsSync(this.eventsPath(id))) {
11780
+ const origin = { path: path9.join(legacy, "tasks", name), digest };
11781
+ if (!fs9.existsSync(this.eventsPath(id))) {
11978
11782
  this.openTask(record, "migrated.from_v2", null, origin);
11979
11783
  migrated += 1;
11980
11784
  } else {
@@ -12009,8 +11813,8 @@ class TaskStore {
12009
11813
  try {
12010
11814
  let complete = true;
12011
11815
  for (const [name, digest] of done) {
12012
- const file = path10.join(v2Tasks, name);
12013
- if (sha256(fs10.readFileSync(file, "utf8")) !== digest) {
11816
+ const file = path9.join(v2Tasks, name);
11817
+ if (sha256(fs9.readFileSync(file, "utf8")) !== digest) {
12014
11818
  complete = false;
12015
11819
  continue;
12016
11820
  }
@@ -12020,7 +11824,7 @@ class TaskStore {
12020
11824
  return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
12021
11825
  path: v2
12022
11826
  });
12023
- fs10.rmSync(path10.join(v2, "index.json"), { force: true });
11827
+ fs9.rmSync(path9.join(v2, "index.json"), { force: true });
12024
11828
  this.writeTombstone(v2, target.id);
12025
11829
  } catch (error) {
12026
11830
  return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
@@ -12040,45 +11844,45 @@ class TaskStore {
12040
11844
  }
12041
11845
  adoptLocalStore() {
12042
11846
  const dirs = this.paths();
12043
- const marker = path10.join(dirs.v2, "store.json");
12044
- if (!dirs.shared || !fs10.existsSync(marker))
11847
+ const marker = path9.join(dirs.v2, "store.json");
11848
+ if (!dirs.shared || !fs9.existsSync(marker))
12045
11849
  return success(null, null, null);
12046
11850
  return this.withLock(dirs.checkoutLock, () => {
12047
- if (!fs10.existsSync(marker))
11851
+ if (!fs9.existsSync(marker))
12048
11852
  return success(null, null, null);
12049
11853
  let tasks = 0;
12050
11854
  try {
12051
11855
  const merge = (from, to) => {
12052
- if (!fs10.existsSync(to)) {
12053
- fs10.mkdirSync(path10.dirname(to), { recursive: true });
11856
+ if (!fs9.existsSync(to)) {
11857
+ fs9.mkdirSync(path9.dirname(to), { recursive: true });
12054
11858
  try {
12055
- fs10.renameSync(from, to);
11859
+ fs9.renameSync(from, to);
12056
11860
  } catch {
12057
- fs10.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
12058
- fs10.rmSync(from, { recursive: true, force: true });
11861
+ fs9.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
11862
+ fs9.rmSync(from, { recursive: true, force: true });
12059
11863
  }
12060
11864
  return;
12061
11865
  }
12062
- if (fs10.lstatSync(from).isDirectory() && fs10.lstatSync(to).isDirectory())
12063
- for (const name of fs10.readdirSync(from))
12064
- merge(path10.join(from, name), path10.join(to, name));
11866
+ if (fs9.lstatSync(from).isDirectory() && fs9.lstatSync(to).isDirectory())
11867
+ for (const name of fs9.readdirSync(from))
11868
+ merge(path9.join(from, name), path9.join(to, name));
12065
11869
  };
12066
11870
  for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
12067
- const from = path10.join(dirs.v2, sub);
12068
- if (!fs10.existsSync(from))
11871
+ const from = path9.join(dirs.v2, sub);
11872
+ if (!fs9.existsSync(from))
12069
11873
  continue;
12070
11874
  if (sub === "tasks") {
12071
- for (const name of fs10.readdirSync(from))
12072
- if (validId(name) && !fs10.existsSync(path10.join(dirs.dir, sub, name)))
11875
+ for (const name of fs9.readdirSync(from))
11876
+ if (validId(name) && !fs9.existsSync(path9.join(dirs.dir, sub, name)))
12073
11877
  tasks += 1;
12074
11878
  }
12075
- merge(from, path10.join(dirs.dir, sub));
11879
+ merge(from, path9.join(dirs.dir, sub));
12076
11880
  }
12077
- const ledger = path10.join(dirs.v2, "ledger");
12078
- if (fs10.existsSync(ledger) && !fs10.existsSync(path10.join(dirs.dir, "ledger")))
12079
- merge(ledger, path10.join(dirs.dir, "ledger"));
12080
- fs10.rmSync(path10.join(dirs.v2, "task-index.json"), { force: true });
12081
- fs10.renameSync(marker, path10.join(dirs.v2, "store.moved.json"));
11881
+ const ledger = path9.join(dirs.v2, "ledger");
11882
+ if (fs9.existsSync(ledger) && !fs9.existsSync(path9.join(dirs.dir, "ledger")))
11883
+ merge(ledger, path9.join(dirs.dir, "ledger"));
11884
+ fs9.rmSync(path9.join(dirs.v2, "task-index.json"), { force: true });
11885
+ fs9.renameSync(marker, path9.join(dirs.v2, "store.moved.json"));
12082
11886
  } catch (error) {
12083
11887
  return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
12084
11888
  path: dirs.v2
@@ -12098,7 +11902,7 @@ class TaskStore {
12098
11902
  readV2Workspace(file) {
12099
11903
  let bytes;
12100
11904
  try {
12101
- bytes = fs10.readFileSync(file, "utf8");
11905
+ bytes = fs9.readFileSync(file, "utf8");
12102
11906
  } catch {
12103
11907
  return null;
12104
11908
  }
@@ -12116,7 +11920,7 @@ class TaskStore {
12116
11920
  schemaVersion: SCHEMA_VERSION,
12117
11921
  id: workspaceId ?? newId(),
12118
11922
  revision: newRevision(),
12119
- root: path10.dirname(v2),
11923
+ root: path9.dirname(v2),
12120
11924
  runtime: { createdWith: version, updatedWith: version },
12121
11925
  writer: null,
12122
11926
  store: {
@@ -12127,12 +11931,12 @@ class TaskStore {
12127
11931
  },
12128
11932
  critical: ["store"]
12129
11933
  };
12130
- fs10.mkdirSync(v2, { recursive: true });
12131
- const ignore = path10.join(v2, ".gitignore");
12132
- if (!fs10.existsSync(ignore))
12133
- fs10.writeFileSync(ignore, `*
11934
+ fs9.mkdirSync(v2, { recursive: true });
11935
+ const ignore = path9.join(v2, ".gitignore");
11936
+ if (!fs9.existsSync(ignore))
11937
+ fs9.writeFileSync(ignore, `*
12134
11938
  `);
12135
- replaceFile(path10.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
11939
+ replaceFile(path9.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
12136
11940
  `, true);
12137
11941
  }
12138
11942
  stubFor(taskId) {
@@ -12148,7 +11952,7 @@ class TaskStore {
12148
11952
  }
12149
11953
  isStub(file) {
12150
11954
  try {
12151
- const value = JSON.parse(fs10.readFileSync(file, "utf8"));
11955
+ const value = JSON.parse(fs9.readFileSync(file, "utf8"));
12152
11956
  return isObject4(value) && isObject4(value.store) && value.store.format === "workit-store";
12153
11957
  } catch {
12154
11958
  return false;
@@ -12156,32 +11960,32 @@ class TaskStore {
12156
11960
  }
12157
11961
  writeTombstoneIfMissing() {
12158
11962
  try {
12159
- const v2 = path10.join(this.root, ".workit");
12160
- if (fs10.existsSync(path10.join(v2, "workspace.json")))
11963
+ const v2 = path9.join(this.root, ".workit");
11964
+ if (fs9.existsSync(path9.join(v2, "workspace.json")))
12161
11965
  return;
12162
11966
  let id = null;
12163
11967
  try {
12164
- id = JSON.parse(fs10.readFileSync(this.paths().workspace, "utf8")).id ?? null;
11968
+ id = JSON.parse(fs9.readFileSync(this.paths().workspace, "utf8")).id ?? null;
12165
11969
  } catch {}
12166
11970
  this.writeTombstone(v2, id);
12167
11971
  } catch {}
12168
11972
  }
12169
11973
  initializeStorage() {
12170
11974
  const dirs = this.paths();
12171
- retryTransient(() => fs10.mkdirSync(dirs.checkout, { recursive: true }));
12172
- retryTransient(() => fs10.mkdirSync(dirs.tasks, { recursive: true }));
12173
- if (!fs10.existsSync(dirs.marker))
11975
+ retryTransient(() => fs9.mkdirSync(dirs.checkout, { recursive: true }));
11976
+ retryTransient(() => fs9.mkdirSync(dirs.tasks, { recursive: true }));
11977
+ if (!fs9.existsSync(dirs.marker))
12174
11978
  replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
12175
11979
  `, false);
12176
11980
  if (!dirs.shared) {
12177
- const ignore = path10.join(dirs.dir, ".gitignore");
11981
+ const ignore = path9.join(dirs.dir, ".gitignore");
12178
11982
  let current = null;
12179
11983
  try {
12180
- current = fs10.readFileSync(ignore, "utf8");
11984
+ current = fs9.readFileSync(ignore, "utf8");
12181
11985
  } catch {}
12182
11986
  if (current !== `*
12183
11987
  `)
12184
- retryTransient(() => fs10.writeFileSync(ignore, `*
11988
+ retryTransient(() => fs9.writeFileSync(ignore, `*
12185
11989
  `));
12186
11990
  }
12187
11991
  this.writeTombstoneIfMissing();
@@ -12197,7 +12001,7 @@ class TaskStore {
12197
12001
  shouldReclaim: ({ payload, nowMs }) => {
12198
12002
  let ageMs = null;
12199
12003
  try {
12200
- ageMs = nowMs - fs10.lstatSync(lockPath).mtimeMs;
12004
+ ageMs = nowMs - fs9.lstatSync(lockPath).mtimeMs;
12201
12005
  } catch {}
12202
12006
  return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
12203
12007
  },
@@ -12234,7 +12038,7 @@ class TaskStore {
12234
12038
  withLock(lockPath, operation) {
12235
12039
  try {
12236
12040
  this.initializeStorage();
12237
- fs10.mkdirSync(path10.dirname(lockPath), { recursive: true });
12041
+ fs9.mkdirSync(path9.dirname(lockPath), { recursive: true });
12238
12042
  } catch (error) {
12239
12043
  return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
12240
12044
  path: this.paths().dir
@@ -12274,18 +12078,18 @@ class TaskStore {
12274
12078
  lockDirWritable(lockPath) {
12275
12079
  const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
12276
12080
  try {
12277
- fs10.closeSync(fs10.openSync(probe, "wx", 384));
12081
+ fs9.closeSync(fs9.openSync(probe, "wx", 384));
12278
12082
  } catch {
12279
12083
  return false;
12280
12084
  }
12281
12085
  try {
12282
- fs10.rmSync(probe, { force: true });
12086
+ fs9.rmSync(probe, { force: true });
12283
12087
  } catch {}
12284
12088
  return true;
12285
12089
  }
12286
12090
  lockHolderPresent(lockPath) {
12287
12091
  try {
12288
- fs10.lstatSync(lockPath);
12092
+ fs9.lstatSync(lockPath);
12289
12093
  return true;
12290
12094
  } catch (error) {
12291
12095
  return error?.code !== "ENOENT";
@@ -12297,12 +12101,12 @@ class TaskStore {
12297
12101
  if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
12298
12102
  return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
12299
12103
  path: lockPath,
12300
- guidance: `Check permissions and the read-only attribute on ${path10.dirname(lockPath)}.`
12104
+ guidance: `Check permissions and the read-only attribute on ${path9.dirname(lockPath)}.`
12301
12105
  });
12302
12106
  if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
12303
12107
  let lock = null;
12304
12108
  try {
12305
- lock = parseMetadataLockOrNull(fs10.readFileSync(lockPath, "utf8"));
12109
+ lock = parseMetadataLockOrNull(fs9.readFileSync(lockPath, "utf8"));
12306
12110
  } catch {}
12307
12111
  return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
12308
12112
  outcome: "not_started",
@@ -12317,18 +12121,18 @@ class TaskStore {
12317
12121
 
12318
12122
  // packages/workit-core/src/core/policy/derive.ts
12319
12123
  import { existsSync as existsSync4 } from "node:fs";
12320
- import path15 from "node:path";
12124
+ import path14 from "node:path";
12321
12125
 
12322
12126
  // packages/workit-core/src/ledger.ts
12323
- import { spawnSync as spawnSync9 } from "node:child_process";
12127
+ import { spawnSync as spawnSync8 } from "node:child_process";
12324
12128
  import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
12325
- import fs12 from "node:fs";
12326
- import path14 from "node:path";
12327
-
12328
- // packages/workit-core/src/core/vcs-config.ts
12329
12129
  import fs11 from "node:fs";
12330
12130
  import path13 from "node:path";
12331
- import { spawnSync as spawnSync8 } from "node:child_process";
12131
+
12132
+ // packages/workit-core/src/core/vcs-config.ts
12133
+ import fs10 from "node:fs";
12134
+ import path12 from "node:path";
12135
+ import { spawnSync as spawnSync7 } from "node:child_process";
12332
12136
 
12333
12137
  // packages/workit-core/src/core/config.ts
12334
12138
  import {
@@ -12341,7 +12145,7 @@ import {
12341
12145
  writeFileSync as writeFileSync2
12342
12146
  } from "node:fs";
12343
12147
  import os3 from "node:os";
12344
- import path11 from "node:path";
12148
+ import path10 from "node:path";
12345
12149
 
12346
12150
  // packages/workit-core/src/core/boundary.ts
12347
12151
  var EVENT = {
@@ -12385,7 +12189,7 @@ var mergePreset = (preset, input = {}, current = {
12385
12189
  protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
12386
12190
  };
12387
12191
  };
12388
- var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workit");
12192
+ var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path10.join(process.env.XDG_CONFIG_HOME || path10.join(os3.homedir(), ".config"), "workit");
12389
12193
  var migratedDir = null;
12390
12194
  var migrationFailed = false;
12391
12195
  var ensureConfigDir = (dir = resolveConfigDir()) => {
@@ -12395,7 +12199,7 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
12395
12199
  migratedDir = dir;
12396
12200
  return dir;
12397
12201
  }
12398
- const legacy = path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workflow-toolkit");
12202
+ const legacy = path10.join(process.env.XDG_CONFIG_HOME || path10.join(os3.homedir(), ".config"), "workflow-toolkit");
12399
12203
  if (!existsSync3(legacy)) {
12400
12204
  migratedDir = dir;
12401
12205
  return dir;
@@ -12408,8 +12212,8 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
12408
12212
  mkdirSync2(dir, { recursive: true });
12409
12213
  diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
12410
12214
  for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
12411
- const src = path11.join(legacy, entry.name);
12412
- const dest = path11.join(dir, entry.name);
12215
+ const src = path10.join(legacy, entry.name);
12216
+ const dest = path10.join(dir, entry.name);
12413
12217
  if (existsSync3(dest))
12414
12218
  continue;
12415
12219
  try {
@@ -12481,7 +12285,7 @@ var parseConfigResult = (raw, file) => {
12481
12285
  return { status: "valid", path: file, config };
12482
12286
  };
12483
12287
  var readConfigTyped = (dir) => {
12484
- const file = path11.join(dir ?? configDir(), "config.json");
12288
+ const file = path10.join(dir ?? configDir(), "config.json");
12485
12289
  return parseConfigResult(readSafe(file), file);
12486
12290
  };
12487
12291
  var readConfig = () => {
@@ -12490,7 +12294,7 @@ var readConfig = () => {
12490
12294
  throw new Error(result.error);
12491
12295
  return result.config ?? DEFAULTS;
12492
12296
  };
12493
- var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path11.join(configDir(), "vcs.json");
12297
+ var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path10.join(configDir(), "vcs.json");
12494
12298
  var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
12495
12299
  var resolveBranchPolicy = (config, workspace, repoPreset) => {
12496
12300
  const wp = workspace?.branchPolicy ?? {};
@@ -12531,13 +12335,13 @@ var resolveCommitPolicy = (config, workspace) => {
12531
12335
  };
12532
12336
 
12533
12337
  // packages/workit-core/src/core/workspaces.ts
12534
- import { spawnSync as spawnSync6 } from "node:child_process";
12338
+ import { spawnSync as spawnSync5 } from "node:child_process";
12535
12339
  import { createHash as createHash7 } from "node:crypto";
12536
12340
  import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
12537
- import path12 from "node:path";
12341
+ import path11 from "node:path";
12538
12342
 
12539
12343
  // packages/workit-core/src/core/branch-policy.ts
12540
- import { spawnSync as spawnSync5 } from "node:child_process";
12344
+ import { spawnSync as spawnSync4 } from "node:child_process";
12541
12345
  var PREFIXES = {
12542
12346
  feature: "feature/*",
12543
12347
  bugfix: "bugfix/*",
@@ -12545,7 +12349,7 @@ var PREFIXES = {
12545
12349
  hotfix: "hotfix/*"
12546
12350
  };
12547
12351
  var branchNames = (workspaceRoot) => {
12548
- const r = spawnSync5("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/remotes/origin"], { cwd: workspaceRoot, encoding: "utf8" });
12352
+ const r = spawnSync4("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/remotes/origin"], { cwd: workspaceRoot, encoding: "utf8" });
12549
12353
  if (r.status !== 0)
12550
12354
  return new Set;
12551
12355
  return new Set((r.stdout ?? "").split(`
@@ -12604,7 +12408,7 @@ var repoBranchPreset = (workspaceRoot) => {
12604
12408
  var releaseTagPrefixOf = (workspaceRoot, policyPrefix) => {
12605
12409
  if (typeof policyPrefix === "string")
12606
12410
  return policyPrefix.trim();
12607
- const r = spawnSync5("git", ["config", "--get", "gitflow.prefix.versiontag"], {
12411
+ const r = spawnSync4("git", ["config", "--get", "gitflow.prefix.versiontag"], {
12608
12412
  cwd: workspaceRoot,
12609
12413
  encoding: "utf8"
12610
12414
  });
@@ -12669,7 +12473,7 @@ var releaseTrackSchema = object({
12669
12473
  context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
12670
12474
  }
12671
12475
  }
12672
- if (track.versionSource?.kind === "package-json" && (path12.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
12476
+ if (track.versionSource?.kind === "package-json" && (path11.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
12673
12477
  context.addIssue({
12674
12478
  code: "custom",
12675
12479
  path: ["versionSource", "path"],
@@ -12689,6 +12493,7 @@ var workspaceConfigSchema = object({
12689
12493
  autoApprove: union([boolean2(), array(nonBlank)]).optional(),
12690
12494
  defaultEndpoint: unknown().optional(),
12691
12495
  verification: _enum(["self", "independent"]).optional(),
12496
+ delegation: unknown().optional(),
12692
12497
  trailers: boolean2().optional(),
12693
12498
  liveSessionGuard: boolean2().optional(),
12694
12499
  fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
@@ -12769,7 +12574,7 @@ var validateWorkspacesDocument = (parsed, file) => {
12769
12574
  };
12770
12575
  };
12771
12576
  var readWorkspacesResult = (dir = configDir()) => {
12772
- const file = path12.join(dir, "workspaces.json");
12577
+ const file = path11.join(dir, "workspaces.json");
12773
12578
  let raw;
12774
12579
  try {
12775
12580
  raw = readFileSync3(file, "utf8");
@@ -12868,7 +12673,7 @@ var mainCheckoutOf = (cwd) => {
12868
12673
  for (const name of ["GIT_DIR", "GIT_WORK_TREE", "GIT_COMMON_DIR", "GIT_INDEX_FILE"])
12869
12674
  delete env[name];
12870
12675
  const git = (args) => {
12871
- const run = spawnSync6("git", args, {
12676
+ const run = spawnSync5("git", args, {
12872
12677
  cwd,
12873
12678
  env,
12874
12679
  encoding: "utf8",
@@ -12880,9 +12685,9 @@ var mainCheckoutOf = (cwd) => {
12880
12685
  let main = null;
12881
12686
  const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
12882
12687
  const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
12883
- if (gitDir && common && path12.resolve(gitDir) !== path12.resolve(common)) {
12688
+ if (gitDir && common && path11.resolve(gitDir) !== path11.resolve(common)) {
12884
12689
  const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
12885
- main = listed?.trim() || (path12.basename(common) === ".git" ? path12.dirname(common) : common);
12690
+ main = listed?.trim() || (path11.basename(common) === ".git" ? path11.dirname(common) : common);
12886
12691
  }
12887
12692
  mainCheckouts.set(cwd, main);
12888
12693
  return main;
@@ -12973,7 +12778,7 @@ var runtimeWorkspaceIndexSchema = object({
12973
12778
  workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
12974
12779
  }).passthrough();
12975
12780
  var matchRuntimeWorkspace = (cwd, dir = configDir()) => {
12976
- const file = path12.join(dir, "workspaces.json");
12781
+ const file = path11.join(dir, "workspaces.json");
12977
12782
  let raw;
12978
12783
  try {
12979
12784
  raw = readFileSync3(file, "utf8");
@@ -13082,7 +12887,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
13082
12887
 
13083
12888
  // packages/workit-core/src/core/release-tracks.ts
13084
12889
  import { createHash as createHash8 } from "node:crypto";
13085
- import { spawnSync as spawnSync7 } from "node:child_process";
12890
+ import { spawnSync as spawnSync6 } from "node:child_process";
13086
12891
  class ReleaseTrackError extends Error {
13087
12892
  }
13088
12893
  var KNOWN_FIELDS = new Set([
@@ -13101,7 +12906,7 @@ var KNOWN_FIELDS = new Set([
13101
12906
  var BRANCH_FIELDS = ["productionBranch", "integrationBranch", "baseBranch", "pullRequestTarget"];
13102
12907
  var PLACEHOLDER = /\{[^}]*\}/gu;
13103
12908
  var HAS_PLACEHOLDER = /\{[^}]*\}/u;
13104
- var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
12909
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
13105
12910
  var branchName = (value) => typeof value === "string" && value.trim() && !value.trim().startsWith("-") ? value.trim() : null;
13106
12911
  var namedBranches = (entry) => [
13107
12912
  ...BRANCH_FIELDS.map((field) => branchName(entry[field])),
@@ -13112,7 +12917,7 @@ function readReleaseTracks(raw) {
13112
12917
  const out = { tracks: [], issues: [], error: null, protectedBranches: [] };
13113
12918
  if (raw === undefined || raw === null)
13114
12919
  return out;
13115
- if (!isRecord2(raw)) {
12920
+ if (!isRecord(raw)) {
13116
12921
  out.issues.push("releaseTracks is not an object; ignored");
13117
12922
  return out;
13118
12923
  }
@@ -13120,7 +12925,7 @@ function readReleaseTracks(raw) {
13120
12925
  const protectedNames = new Set;
13121
12926
  for (const [name, entry] of Object.entries(raw).toSorted(([a], [b]) => a < b ? -1 : 1)) {
13122
12927
  const label = `release track ${JSON.stringify(name)}`;
13123
- if (!isRecord2(entry)) {
12928
+ if (!isRecord(entry)) {
13124
12929
  out.issues.push(`${label} is not an object; ignored`);
13125
12930
  continue;
13126
12931
  }
@@ -13171,7 +12976,7 @@ function readReleaseTracks(raw) {
13171
12976
  }
13172
12977
  const naming = {};
13173
12978
  if (entry.naming !== undefined) {
13174
- if (!isRecord2(entry.naming))
12979
+ if (!isRecord(entry.naming))
13175
12980
  problem("naming", "not an object; ignored");
13176
12981
  else
13177
12982
  for (const kind of ["feature", "release", "hotfix"]) {
@@ -13366,8 +13171,8 @@ function resolveReleaseTrack(input) {
13366
13171
  const newest = forks.filter((fork) => !forks.some((other) => descends(fork.sha, other.sha)));
13367
13172
  return fallback(`${branch} has the same or unrelated fork points with ${[...new Set(newest.map((fork) => `${fork.ref} (${fork.track.name === IMPLICIT ? "workspace default" : fork.track.name})`))].join(" and ")}`);
13368
13173
  }
13369
- var git3 = (cwd, args) => {
13370
- const run = spawnSync7("git", args, {
13174
+ var git2 = (cwd, args) => {
13175
+ const run = spawnSync6("git", args, {
13371
13176
  cwd,
13372
13177
  encoding: "utf8",
13373
13178
  timeout: 20000,
@@ -13378,7 +13183,7 @@ var git3 = (cwd, args) => {
13378
13183
  };
13379
13184
  var recordedBaseKey = (branch) => `branch.${branch}.workitBase`;
13380
13185
  var escapeRegExp2 = (text) => text.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
13381
- var reflog = (cwd, ref) => (git3(cwd, ["log", "-g", "--date=unix", "--format=%H%x09%gd%x09%gs", ref, "--"]) ?? "").split(`
13186
+ var reflog = (cwd, ref) => (git2(cwd, ["log", "-g", "--date=unix", "--format=%H%x09%gd%x09%gs", ref, "--"]) ?? "").split(`
13382
13187
  `).map((line) => {
13383
13188
  const [sha = "", selector = "", ...subject] = line.split("\t");
13384
13189
  return {
@@ -13396,9 +13201,9 @@ function gitTrackProbe(cwd) {
13396
13201
  };
13397
13202
  return {
13398
13203
  remotes: () => remotes ??= remoteNames(cwd),
13399
- recordedBase: (branch) => git3(cwd, ["config", "--get", recordedBaseKey(branch)]) || null,
13204
+ recordedBase: (branch) => git2(cwd, ["config", "--get", recordedBaseKey(branch)]) || null,
13400
13205
  createdFrom: (branch) => {
13401
- const log = git3(cwd, ["reflog", "show", "--format=%gs", `refs/heads/${branch}`, "--"]);
13206
+ const log = git2(cwd, ["reflog", "show", "--format=%gs", `refs/heads/${branch}`, "--"]);
13402
13207
  const oldest = log?.split(`
13403
13208
  `).findLast(Boolean) ?? "";
13404
13209
  const match = /^branch: Created from (\S+)$/u.exec(oldest);
@@ -13417,7 +13222,7 @@ function gitTrackProbe(cwd) {
13417
13222
  const base = tip(trackBranch);
13418
13223
  return base && resolveRef(cwd, `refs/heads/${branch}`) ? mergeBase(cwd, base, `refs/heads/${branch}`) : null;
13419
13224
  },
13420
- isAncestor: (ancestor, descendant) => git3(cwd, ["merge-base", "--is-ancestor", ancestor, descendant]) !== null
13225
+ isAncestor: (ancestor, descendant) => git2(cwd, ["merge-base", "--is-ancestor", ancestor, descendant]) !== null
13421
13226
  };
13422
13227
  }
13423
13228
  function workspaceReleaseTracks(cwd) {
@@ -13444,15 +13249,15 @@ function resolveTrackFor(cwd, options) {
13444
13249
  };
13445
13250
  let key = null;
13446
13251
  if (!options.probe) {
13447
- const refs = git3(cwd, [
13252
+ const refs = git2(cwd, [
13448
13253
  "for-each-ref",
13449
13254
  "--format=%(refname) %(objectname)",
13450
13255
  "refs/heads",
13451
13256
  "refs/remotes"
13452
13257
  ]) ?? "";
13453
- const head = git3(cwd, ["reflog", "show", "-n", "1", "--format=%H %gs", "HEAD", "--"]) ?? "";
13258
+ const head = git2(cwd, ["reflog", "show", "-n", "1", "--format=%H %gs", "HEAD", "--"]) ?? "";
13454
13259
  const own = branch ? [
13455
- git3(cwd, [
13260
+ git2(cwd, [
13456
13261
  "log",
13457
13262
  "-g",
13458
13263
  "-n",
@@ -13462,7 +13267,7 @@ function resolveTrackFor(cwd, options) {
13462
13267
  `refs/heads/${branch}`,
13463
13268
  "--"
13464
13269
  ]) ?? "",
13465
- git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
13270
+ git2(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
13466
13271
  ] : [];
13467
13272
  key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
13468
13273
  const hit = memo2.get(key);
@@ -13479,10 +13284,10 @@ function resolveTrackFor(cwd, options) {
13479
13284
  }
13480
13285
 
13481
13286
  // packages/workit-core/src/core/vcs-config.ts
13482
- var workspacesPath = () => path13.join(configDir(), "workspaces.json");
13287
+ var workspacesPath = () => path12.join(configDir(), "workspaces.json");
13483
13288
  var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
13484
13289
  var remoteProvider = (cwd) => {
13485
- const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
13290
+ const r = spawnSync7("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
13486
13291
  if (r.status !== 0)
13487
13292
  return null;
13488
13293
  const url = (r.stdout ?? "").trim();
@@ -13496,7 +13301,7 @@ var readVcsConfig = () => {
13496
13301
  const file = vcsConfigPath();
13497
13302
  let raw;
13498
13303
  try {
13499
- raw = fs11.readFileSync(file, "utf8");
13304
+ raw = fs10.readFileSync(file, "utf8");
13500
13305
  } catch {
13501
13306
  return { status: "missing", path: file, config: {} };
13502
13307
  }
@@ -13621,7 +13426,7 @@ function vcsConfig(mode, cwd, options = {}) {
13621
13426
  const prov = cfg[provider] ?? {};
13622
13427
  const out = {
13623
13428
  ok: true,
13624
- configPath: path13.resolve(cfgPath),
13429
+ configPath: path12.resolve(cfgPath),
13625
13430
  provider,
13626
13431
  defaultTargetBranch: workspaceDefault,
13627
13432
  pr: cfg.pr ?? {},
@@ -13662,7 +13467,7 @@ var err = (code, error, unblock) => ({
13662
13467
  ...unblock ? { unblock } : {}
13663
13468
  });
13664
13469
  var gitRun2 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
13665
- const run = spawnSync9("git", args, {
13470
+ const run = spawnSync8("git", args, {
13666
13471
  cwd,
13667
13472
  encoding: "utf8",
13668
13473
  timeout: timeoutMs,
@@ -13683,7 +13488,7 @@ var gitRun2 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
13683
13488
  stderr: run.error ? String(run.error.message) : run.stderr ?? ""
13684
13489
  };
13685
13490
  };
13686
- var git4 = (cwd, args) => {
13491
+ var git3 = (cwd, args) => {
13687
13492
  const run = gitRun2(cwd, args);
13688
13493
  const value = run.ok ? run.stdout.trim() : "";
13689
13494
  return value ? value : null;
@@ -13692,7 +13497,7 @@ var safeRef = (value) => value.length > 0 && !value.startsWith("-");
13692
13497
  function resolveCommit(cwd, ref) {
13693
13498
  if (!safeRef(ref))
13694
13499
  return null;
13695
- return git4(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
13500
+ return git3(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
13696
13501
  }
13697
13502
  function branchHead(cwd, branch) {
13698
13503
  if (!safeRef(branch))
@@ -13717,7 +13522,7 @@ function defaultBase(cwd) {
13717
13522
  if (resolveCommit(cwd, candidate))
13718
13523
  return candidate;
13719
13524
  }
13720
- const originHead = git4(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]);
13525
+ const originHead = git3(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]);
13721
13526
  if (originHead && resolveCommit(cwd, originHead))
13722
13527
  return originHead;
13723
13528
  for (const candidate of [
@@ -13765,14 +13570,14 @@ function diffHash(cwd, base, head) {
13765
13570
  return `sha256:${hash.digest("hex")}`;
13766
13571
  }
13767
13572
  function storeRoot(cwd) {
13768
- const location = resolveStore(path14.resolve(cwd));
13573
+ const location = resolveStore(path13.resolve(cwd));
13769
13574
  if (location instanceof Error)
13770
13575
  return err("unavailable", location.message.replace(/; fix the repository.*$/u, ""), "fix the repository (git status should work here) or run from a non-git directory");
13771
13576
  return { ok: true, value: { root: location.dir, shared: location.shared } };
13772
13577
  }
13773
13578
  function ledgerPath(cwd) {
13774
13579
  const root = storeRoot(cwd);
13775
- return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
13580
+ return root.ok ? { ok: true, value: path13.join(root.value.root, "ledger", "ledger.jsonl") } : root;
13776
13581
  }
13777
13582
  var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
13778
13583
  var NETWORK_FS_TYPES = new Set([
@@ -13792,7 +13597,7 @@ var onNetworkFs = (dir) => {
13792
13597
  if (process.platform !== "linux")
13793
13598
  return false;
13794
13599
  try {
13795
- return isNetworkFsType(fs12.statfsSync(dir).type);
13600
+ return isNetworkFsType(fs11.statfsSync(dir).type);
13796
13601
  } catch {
13797
13602
  return false;
13798
13603
  }
@@ -13802,15 +13607,15 @@ var LOCK_STALE_MS = 30000;
13802
13607
  var LOCK_OWNER = "owner";
13803
13608
  var lockToken = (lock) => {
13804
13609
  try {
13805
- return fs12.readFileSync(path14.join(lock, LOCK_OWNER), "utf8");
13610
+ return fs11.readFileSync(path13.join(lock, LOCK_OWNER), "utf8");
13806
13611
  } catch {
13807
13612
  return null;
13808
13613
  }
13809
13614
  };
13810
13615
  var lockAgeMs = (lock) => {
13811
- for (const target of [path14.join(lock, LOCK_OWNER), lock]) {
13616
+ for (const target of [path13.join(lock, LOCK_OWNER), lock]) {
13812
13617
  try {
13813
- return Date.now() - fs12.statSync(target).mtimeMs;
13618
+ return Date.now() - fs11.statSync(target).mtimeMs;
13814
13619
  } catch {}
13815
13620
  }
13816
13621
  return null;
@@ -13818,14 +13623,14 @@ var lockAgeMs = (lock) => {
13818
13623
  var ledgerLock = {
13819
13624
  acquire(lock) {
13820
13625
  try {
13821
- fs12.mkdirSync(lock);
13626
+ fs11.mkdirSync(lock);
13822
13627
  } catch (error) {
13823
13628
  if (error.code === "EEXIST")
13824
13629
  return null;
13825
13630
  throw error;
13826
13631
  }
13827
13632
  const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
13828
- fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
13633
+ fs11.writeFileSync(path13.join(lock, LOCK_OWNER), token);
13829
13634
  return token;
13830
13635
  },
13831
13636
  release(lock, token) {
@@ -13833,35 +13638,35 @@ var ledgerLock = {
13833
13638
  return false;
13834
13639
  const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
13835
13640
  try {
13836
- fs12.renameSync(lock, tomb);
13641
+ fs11.renameSync(lock, tomb);
13837
13642
  } catch {
13838
13643
  return false;
13839
13644
  }
13840
13645
  if (lockToken(tomb) !== token) {
13841
13646
  try {
13842
- fs12.renameSync(tomb, lock);
13647
+ fs11.renameSync(tomb, lock);
13843
13648
  } catch {}
13844
13649
  return false;
13845
13650
  }
13846
- fs12.rmSync(tomb, { recursive: true, force: true });
13651
+ fs11.rmSync(tomb, { recursive: true, force: true });
13847
13652
  return true;
13848
13653
  },
13849
13654
  token: lockToken,
13850
13655
  reap(lock, observed) {
13851
13656
  const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
13852
13657
  try {
13853
- fs12.renameSync(lock, tomb);
13658
+ fs11.renameSync(lock, tomb);
13854
13659
  } catch {
13855
13660
  return false;
13856
13661
  }
13857
13662
  if (lockToken(tomb) === observed) {
13858
- fs12.rmSync(tomb, { recursive: true, force: true });
13663
+ fs11.rmSync(tomb, { recursive: true, force: true });
13859
13664
  return true;
13860
13665
  }
13861
13666
  try {
13862
- fs12.renameSync(tomb, lock);
13667
+ fs11.renameSync(tomb, lock);
13863
13668
  } catch {
13864
- fs12.rmSync(tomb, { recursive: true, force: true });
13669
+ fs11.rmSync(tomb, { recursive: true, force: true });
13865
13670
  }
13866
13671
  return false;
13867
13672
  }
@@ -13895,7 +13700,7 @@ var TAIL_BYTES = 64 * 1024;
13895
13700
  var lastLine = (fd, size) => {
13896
13701
  for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
13897
13702
  const buffer = Buffer.alloc(span);
13898
- fs12.readSync(fd, buffer, 0, span, size - span);
13703
+ fs11.readSync(fd, buffer, 0, span, size - span);
13899
13704
  const end = buffer.lastIndexOf(10);
13900
13705
  if (end < 0) {
13901
13706
  if (span === size)
@@ -13926,21 +13731,21 @@ var lastLine = (fd, size) => {
13926
13731
  };
13927
13732
  var tighten = (fd) => {
13928
13733
  try {
13929
- if ((fs12.fstatSync(fd).mode & 63) !== 0)
13930
- fs12.fchmodSync(fd, 384);
13734
+ if ((fs11.fstatSync(fd).mode & 63) !== 0)
13735
+ fs11.fchmodSync(fd, 384);
13931
13736
  } catch {}
13932
13737
  };
13933
13738
  function appendLine(file, build, options) {
13934
13739
  const write = () => {
13935
- const fd = fs12.openSync(file, "a+", 384);
13740
+ const fd = fs11.openSync(file, "a+", 384);
13936
13741
  try {
13937
13742
  if (process.platform !== "win32")
13938
13743
  tighten(fd);
13939
- const size = fs12.fstatSync(fd).size;
13744
+ const size = fs11.fstatSync(fd).size;
13940
13745
  let fence = "";
13941
13746
  if (size > 0) {
13942
13747
  const last = Buffer.alloc(1);
13943
- fs12.readSync(fd, last, 0, 1, size - 1);
13748
+ fs11.readSync(fd, last, 0, 1, size - 1);
13944
13749
  if (last[0] !== 10)
13945
13750
  fence = `
13946
13751
  `;
@@ -13949,17 +13754,17 @@ function appendLine(file, build, options) {
13949
13754
  if (!line.ok)
13950
13755
  return line;
13951
13756
  const bytes = Buffer.from(fence + line.value);
13952
- const written = fs12.writeSync(fd, bytes);
13757
+ const written = fs11.writeSync(fd, bytes);
13953
13758
  if (written !== bytes.length)
13954
13759
  return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
13955
13760
  return { ok: true, value: undefined };
13956
13761
  } finally {
13957
- fs12.closeSync(fd);
13762
+ fs11.closeSync(fd);
13958
13763
  }
13959
13764
  };
13960
13765
  try {
13961
- fs12.mkdirSync(path14.dirname(file), { recursive: true });
13962
- if (!options.forceLock && !onNetworkFs(path14.dirname(file)))
13766
+ fs11.mkdirSync(path13.dirname(file), { recursive: true });
13767
+ if (!options.forceLock && !onNetworkFs(path13.dirname(file)))
13963
13768
  return write();
13964
13769
  const locked = withLedgerLock(file, write);
13965
13770
  return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
@@ -14002,10 +13807,10 @@ function appendObserved(cwd, row, options = {}) {
14002
13807
  function appendHookObserved(cwd, row) {
14003
13808
  return appendRaw(cwd, { ...row, observer: "host_hook" }, {});
14004
13809
  }
14005
- var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
13810
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
14006
13811
  var str = (value) => typeof value === "string" ? value : null;
14007
13812
  var normalizeActor = (value) => {
14008
- const actor = isRecord3(value) ? value : {};
13813
+ const actor = isRecord2(value) ? value : {};
14009
13814
  return {
14010
13815
  host: str(actor.host) ?? "unknown",
14011
13816
  session: str(actor.session),
@@ -14032,8 +13837,8 @@ function readLedger(cwd, options = {}) {
14032
13837
  let text = "";
14033
13838
  let stat = null;
14034
13839
  try {
14035
- stat = fs12.statSync(file.value);
14036
- text = fs12.readFileSync(file.value, "utf8");
13840
+ stat = fs11.statSync(file.value);
13841
+ text = fs11.readFileSync(file.value, "utf8");
14037
13842
  } catch (error) {
14038
13843
  if (error.code === "ENOENT")
14039
13844
  return {
@@ -14068,7 +13873,7 @@ function readLedger(cwd, options = {}) {
14068
13873
  skipped += 1;
14069
13874
  continue;
14070
13875
  }
14071
- if (!isRecord3(parsed) || !str(parsed.id) || !str(parsed.type) || !str(parsed.at)) {
13876
+ if (!isRecord2(parsed) || !str(parsed.id) || !str(parsed.type) || !str(parsed.at)) {
14072
13877
  skipped += 1;
14073
13878
  continue;
14074
13879
  }
@@ -14168,7 +13973,7 @@ function ledgerTampering(cwd) {
14168
13973
  if (!file.ok)
14169
13974
  return [];
14170
13975
  try {
14171
- const stat = fs12.statSync(file.value);
13976
+ const stat = fs11.statSync(file.value);
14172
13977
  const cached = tamperCache.get(file.value);
14173
13978
  if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
14174
13979
  return cached.breaks;
@@ -14195,7 +14000,7 @@ var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.resul
14195
14000
  function trailerSessions(cwd, base, head) {
14196
14001
  if (!base || !head || !safeRef(base) || !safeRef(head))
14197
14002
  return [];
14198
- const out = git4(cwd, [
14003
+ const out = git3(cwd, [
14199
14004
  "log",
14200
14005
  "--format=%(trailers:key=Workit-Session,valueonly,separator=%x00)",
14201
14006
  `${base}..${head}`
@@ -14207,7 +14012,7 @@ function branchScope(cwd, branch) {
14207
14012
  const renamedAt = new Map;
14208
14013
  let since = Number.NEGATIVE_INFINITY;
14209
14014
  if (safeRef(branch)) {
14210
- const log = git4(cwd, [
14015
+ const log = git3(cwd, [
14211
14016
  "log",
14212
14017
  "-g",
14213
14018
  "--date=unix",
@@ -14647,7 +14452,7 @@ var productDecision = (task, ledger) => {
14647
14452
  };
14648
14453
  };
14649
14454
  var planRecorded = (task, root) => {
14650
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path15.resolve(root, ref.path)));
14455
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path14.resolve(root, ref.path)));
14651
14456
  if (cited)
14652
14457
  return {
14653
14458
  met: true,
@@ -14707,14 +14512,14 @@ function writeBlockers(task, root) {
14707
14512
 
14708
14513
  // packages/workit-core/src/core/task-evaluation.ts
14709
14514
  var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
14710
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
14711
- var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
14515
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path15.sep}`);
14516
+ var relative = (root, candidate) => path15.relative(root, candidate).split(path15.sep).join("/") || ".";
14712
14517
  var canonicalPath = (value) => {
14713
14518
  if (typeof value !== "string" || !value)
14714
14519
  return null;
14715
14520
  if (value.includes("\\") || value.split("/").includes(".."))
14716
14521
  return null;
14717
- const normalized = path16.posix.normalize(value);
14522
+ const normalized = path15.posix.normalize(value);
14718
14523
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
14719
14524
  return null;
14720
14525
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -14750,7 +14555,7 @@ var compareCodeUnits2 = (left, right) => {
14750
14555
  return left.length - right.length;
14751
14556
  };
14752
14557
  var gitPaths = (root) => {
14753
- const result = spawnSync10("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
14558
+ const result = spawnSync9("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
14754
14559
  cwd: root,
14755
14560
  encoding: "buffer"
14756
14561
  });
@@ -14762,21 +14567,21 @@ var gitPaths = (root) => {
14762
14567
  git: !/not a git repository/i.test(stderr)
14763
14568
  };
14764
14569
  }
14765
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
14766
- const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
14570
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path15.sep).join("/")) : [];
14571
+ const stagedDeleted = spawnSync9("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
14767
14572
  if (stagedDeleted.status !== 0) {
14768
14573
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
14769
14574
  if (!/not a git repository/i.test(stderr))
14770
14575
  return { paths, uncertain: true, git: true };
14771
14576
  } else if (stagedDeleted.stdout) {
14772
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
14577
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path15.sep).join("/")));
14773
14578
  }
14774
14579
  return { paths: [...new Set(paths)], uncertain: false, git: true };
14775
14580
  };
14776
14581
  var walk = (root, directory, output) => {
14777
14582
  let names;
14778
14583
  try {
14779
- names = fs13.readdirSync(directory);
14584
+ names = fs12.readdirSync(directory);
14780
14585
  } catch {
14781
14586
  return true;
14782
14587
  }
@@ -14784,10 +14589,10 @@ var walk = (root, directory, output) => {
14784
14589
  for (const name of names) {
14785
14590
  if (name === ".git" || name === ".workit")
14786
14591
  continue;
14787
- const target = path16.join(directory, name);
14592
+ const target = path15.join(directory, name);
14788
14593
  let stat;
14789
14594
  try {
14790
- stat = fs13.lstatSync(target);
14595
+ stat = fs12.lstatSync(target);
14791
14596
  } catch {
14792
14597
  uncertain = true;
14793
14598
  continue;
@@ -14802,14 +14607,14 @@ var walk = (root, directory, output) => {
14802
14607
  var scopeRoots = (root, scope) => {
14803
14608
  const roots = [];
14804
14609
  for (const item of scope.paths.length ? scope.paths : ["."]) {
14805
- const target = path16.resolve(root, item);
14610
+ const target = path15.resolve(root, item);
14806
14611
  if (!inside(root, target))
14807
14612
  return failure2("invalid_input", `candidate scope escapes checkout: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} is outside the checkout` }] });
14808
14613
  let ancestor = target;
14809
- while (!fs13.existsSync(ancestor) && ancestor !== root)
14810
- ancestor = path16.dirname(ancestor);
14614
+ while (!fs12.existsSync(ancestor) && ancestor !== root)
14615
+ ancestor = path15.dirname(ancestor);
14811
14616
  try {
14812
- if (!inside(root, fs13.realpathSync(ancestor)))
14617
+ if (!inside(root, fs12.realpathSync(ancestor)))
14813
14618
  return failure2("invalid_input", `candidate scope escapes checkout through a symlink: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} resolves outside the checkout` }] });
14814
14619
  } catch {
14815
14620
  return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
@@ -14821,7 +14626,7 @@ var scopeRoots = (root, scope) => {
14821
14626
  function captureCandidate(root, scope, environment = []) {
14822
14627
  let checkout;
14823
14628
  try {
14824
- checkout = fs13.realpathSync(root);
14629
+ checkout = fs12.realpathSync(root);
14825
14630
  } catch {
14826
14631
  return failure2("invalid_input", "candidate root does not exist");
14827
14632
  }
@@ -14839,11 +14644,11 @@ function captureCandidate(root, scope, environment = []) {
14839
14644
  if (item === ".git" || item === ".workit")
14840
14645
  continue;
14841
14646
  names.add(item);
14842
- if (!fs13.existsSync(target))
14647
+ if (!fs12.existsSync(target))
14843
14648
  continue;
14844
14649
  let stat;
14845
14650
  try {
14846
- stat = fs13.lstatSync(target);
14651
+ stat = fs12.lstatSync(target);
14847
14652
  } catch {
14848
14653
  uncertain = true;
14849
14654
  continue;
@@ -14859,17 +14664,17 @@ function captureCandidate(root, scope, environment = []) {
14859
14664
  for (const item of [...names].toSorted()) {
14860
14665
  if (!scopeMatches(item, normalizedScope))
14861
14666
  continue;
14862
- const target = path16.join(checkout, item);
14667
+ const target = path15.join(checkout, item);
14863
14668
  try {
14864
- const stat = fs13.lstatSync(target);
14669
+ const stat = fs12.lstatSync(target);
14865
14670
  if (stat.isSymbolicLink()) {
14866
- const link = fs13.readlinkSync(target);
14671
+ const link = fs12.readlinkSync(target);
14867
14672
  files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
14868
14673
  } else if (stat.isFile()) {
14869
14674
  files.push({
14870
14675
  path: item,
14871
14676
  kind: "file",
14872
- digest: digestBytes(fs13.readFileSync(target)),
14677
+ digest: digestBytes(fs12.readFileSync(target)),
14873
14678
  executable: (stat.mode & 73) !== 0
14874
14679
  });
14875
14680
  } else if (!stat.isDirectory()) {
@@ -14894,7 +14699,7 @@ function captureCandidate(root, scope, environment = []) {
14894
14699
  const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
14895
14700
  if (values.some(({ value }) => value === null))
14896
14701
  completeness = "uncertain";
14897
- const headResult = spawnSync10("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
14702
+ const headResult = spawnSync9("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
14898
14703
  if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
14899
14704
  completeness = "uncertain";
14900
14705
  const head = headResult.status === 0 ? headResult.stdout.trim() : null;
@@ -15034,7 +14839,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
15034
14839
  var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15035
14840
  let root;
15036
14841
  try {
15037
- root = fs13.realpathSync(checkoutRoot);
14842
+ root = fs12.realpathSync(checkoutRoot);
15038
14843
  } catch {
15039
14844
  return failure2("permission_denied", "checkout root is unavailable");
15040
14845
  }
@@ -15043,23 +14848,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15043
14848
  continue;
15044
14849
  if (!reference.digest)
15045
14850
  return failure2("invalid_input", "document references require a byte digest");
15046
- const target = path16.resolve(root, reference.path);
15047
- if (target !== root && !target.startsWith(`${root}${path16.sep}`))
14851
+ const target = path15.resolve(root, reference.path);
14852
+ if (target !== root && !target.startsWith(`${root}${path15.sep}`))
15048
14853
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
15049
14854
  fields: [
15050
14855
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
15051
14856
  ]
15052
14857
  });
15053
14858
  try {
15054
- const below = path16.relative(root, target);
14859
+ const below = path15.relative(root, target);
15055
14860
  let current = root;
15056
- for (const segment of below.split(path16.sep)) {
14861
+ for (const segment of below.split(path15.sep)) {
15057
14862
  if (!segment)
15058
14863
  continue;
15059
- current = path16.join(current, segment);
15060
- const stat = fs13.lstatSync(current);
15061
- const real = fs13.realpathSync(current);
15062
- if (real !== root && !real.startsWith(`${root}${path16.sep}`))
14864
+ current = path15.join(current, segment);
14865
+ const stat = fs12.lstatSync(current);
14866
+ const real = fs12.realpathSync(current);
14867
+ if (real !== root && !real.startsWith(`${root}${path15.sep}`))
15063
14868
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
15064
14869
  fields: [
15065
14870
  {
@@ -15075,7 +14880,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15075
14880
  if (current !== target && !stat.isDirectory())
15076
14881
  return failure2("invalid_input", "document reference ancestor is not a directory");
15077
14882
  }
15078
- if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
14883
+ if (digestBytes(fs12.readFileSync(target)) !== reference.digest)
15079
14884
  return failure2("permission_denied", "decision document bytes have changed");
15080
14885
  } catch {
15081
14886
  return failure2("invalid_input", "decision document is unavailable");
@@ -15370,6 +15175,54 @@ function evaluateClosure(requestedOutcome, view) {
15370
15175
  requirementIds: evaluations.map((item) => item.requirementId)
15371
15176
  });
15372
15177
  }
15178
+ var BRIEF_TEXT = 250;
15179
+ var BRIEF_BLOCKERS = 3;
15180
+ var BRIEF_OPEN = 8;
15181
+ var clip = (value) => {
15182
+ const fits = (text) => Buffer.byteLength(JSON.stringify(text)) <= BRIEF_TEXT;
15183
+ if (fits(value))
15184
+ return value;
15185
+ let length = Math.min(value.length, BRIEF_TEXT);
15186
+ while (length > 0 && !fits(`${value.slice(0, length)}…`))
15187
+ length -= 1;
15188
+ return `${value.slice(0, length)}…`;
15189
+ };
15190
+ function boundTaskSummary(summary) {
15191
+ const rules = new Map((summary.policy?.requirements ?? []).map((item) => [item.id, item]));
15192
+ const unmet = summary.requirements.filter((item) => item.status === "unsatisfied" || item.status === "unavailable");
15193
+ const { progress, closure } = summary;
15194
+ const next = progress.nextAction;
15195
+ const open = unmet.slice(0, BRIEF_OPEN).map((item) => ({
15196
+ dimension: rules.get(item.requirementId)?.dimension ?? "verification",
15197
+ before: rules.get(item.requirementId)?.before ?? "close",
15198
+ status: item.status
15199
+ }));
15200
+ const brief = {
15201
+ id: summary.id,
15202
+ revision: summary.revision,
15203
+ workspaceRevision: summary.workspaceRevision,
15204
+ updatedAt: summary.updatedAt,
15205
+ objective: clip(summary.objective),
15206
+ status: summary.status,
15207
+ closure: closure && {
15208
+ outcome: closure.outcome,
15209
+ at: closure.at,
15210
+ summary: clip(closure.summary)
15211
+ },
15212
+ progress: {
15213
+ summary: clip(progress.summary),
15214
+ nextAction: next && clip(next),
15215
+ blockers: progress.blockers.slice(0, BRIEF_BLOCKERS).map((item) => ({
15216
+ reason: clip(item.reason),
15217
+ dependentAction: clip(item.dependentAction)
15218
+ }))
15219
+ },
15220
+ requirements: { total: summary.requirements.length, unmet: unmet.length, open },
15221
+ truncated: false
15222
+ };
15223
+ brief.truncated = JSON.stringify(brief).includes("…") || progress.blockers.length > BRIEF_BLOCKERS || unmet.length > BRIEF_OPEN;
15224
+ return brief;
15225
+ }
15373
15226
 
15374
15227
  // packages/workit-core/src/core/task-engine.ts
15375
15228
  import { realpathSync as realpathSync4 } from "node:fs";
@@ -15932,7 +15785,7 @@ var judgeInputSummary = (raw) => {
15932
15785
 
15933
15786
  // packages/workit-core/src/autonomy.ts
15934
15787
  import os4 from "node:os";
15935
- import path17 from "node:path";
15788
+ import path16 from "node:path";
15936
15789
  var accountHome = () => {
15937
15790
  try {
15938
15791
  return os4.userInfo().homedir;
@@ -15941,10 +15794,10 @@ var accountHome = () => {
15941
15794
  }
15942
15795
  };
15943
15796
  var grantsHome = { resolve: accountHome };
15944
- var grantsDir = () => path17.join(grantsHome.resolve(), ".config", "workit");
15797
+ var grantsDir = () => path16.join(grantsHome.resolve(), ".config", "workit");
15945
15798
  var grantsOverride = () => {
15946
- const configured = path17.resolve(resolveConfigDir());
15947
- return configured === path17.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path17.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
15799
+ const configured = path16.resolve(resolveConfigDir());
15800
+ return configured === path16.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path16.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
15948
15801
  };
15949
15802
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
15950
15803
  var DEFAULT_GRANTS = Object.freeze({
@@ -16756,6 +16609,15 @@ class WorkitCore {
16756
16609
  if (!parsed.ok)
16757
16610
  return parsed;
16758
16611
  const input = parsed.data;
16612
+ if (this.context.readOnlyChild) {
16613
+ if (input.action !== "inspect")
16614
+ return failure2("permission_denied", "child sessions can only inspect the task summary");
16615
+ const task = this.store.readTask(input.taskId);
16616
+ if (!task.ok)
16617
+ return task;
16618
+ const summary = this.summary(task.data);
16619
+ return summary.ok ? { ...summary, data: boundTaskSummary(summary.data) } : summary;
16620
+ }
16759
16621
  if ((this.context.workerId ?? null) !== null) {
16760
16622
  if (input.action !== "inspect" && input.action !== "list")
16761
16623
  return failure2("permission_denied", "helpers cannot control task lifecycle or scope");
@@ -17948,7 +17810,7 @@ function entryCompactContext(store, entry, session, context) {
17948
17810
  return null;
17949
17811
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
17950
17812
  const observed = task?.data.evidence.some(isObservedCheck) ?? false;
17951
- const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path18.resolve(store.root, ref.path)] : []));
17813
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path17.resolve(store.root, ref.path)] : []));
17952
17814
  const observedFiles = filesKey(files);
17953
17815
  const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
17954
17816
  if (!compact.ok || !compact.data)
@@ -18026,32 +17888,32 @@ var turnMarker = (input) => {
18026
17888
  if (location instanceof Error)
18027
17889
  return null;
18028
17890
  const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
18029
- return path18.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
17891
+ return path17.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
18030
17892
  } catch {
18031
17893
  return null;
18032
17894
  }
18033
17895
  };
18034
17896
  var writeTurnDigest = (file, digest) => {
18035
17897
  try {
18036
- fs14.mkdirSync(path18.dirname(file), { recursive: true });
18037
- fs14.writeFileSync(file, `${JSON.stringify({ digest })}
17898
+ fs13.mkdirSync(path17.dirname(file), { recursive: true });
17899
+ fs13.writeFileSync(file, `${JSON.stringify({ digest })}
18038
17900
  `);
18039
17901
  } catch {}
18040
17902
  };
18041
17903
  var clearTurnDigest = (file) => {
18042
17904
  try {
18043
- fs14.rmSync(file, { force: true });
17905
+ fs13.rmSync(file, { force: true });
18044
17906
  } catch {}
18045
17907
  };
18046
17908
  var pruneTurnDigests = (dir, now) => {
18047
17909
  try {
18048
- for (const name of fs14.readdirSync(dir)) {
17910
+ for (const name of fs13.readdirSync(dir)) {
18049
17911
  if (!name.startsWith("turn-"))
18050
17912
  continue;
18051
- const file = path18.join(dir, name);
17913
+ const file = path17.join(dir, name);
18052
17914
  try {
18053
- if (now - fs14.statSync(file).mtimeMs > TURN_TTL_MS)
18054
- fs14.rmSync(file, { force: true });
17915
+ if (now - fs13.statSync(file).mtimeMs > TURN_TTL_MS)
17916
+ fs13.rmSync(file, { force: true });
18055
17917
  } catch {}
18056
17918
  }
18057
17919
  } catch {}
@@ -18067,7 +17929,7 @@ var changedTurnContext = (input, text) => {
18067
17929
  return text;
18068
17930
  const digest = digestOf(text);
18069
17931
  try {
18070
- const previous = JSON.parse(fs14.readFileSync(file, "utf8"));
17932
+ const previous = JSON.parse(fs13.readFileSync(file, "utf8"));
18071
17933
  if (previous.digest === digest)
18072
17934
  return null;
18073
17935
  } catch {}
@@ -18078,23 +17940,23 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
18078
17940
  const file = turnMarker(input);
18079
17941
  if (!file)
18080
17942
  return;
18081
- pruneTurnDigests(path18.dirname(file), Date.now());
17943
+ pruneTurnDigests(path17.dirname(file), Date.now());
18082
17944
  if (text)
18083
17945
  writeTurnDigest(file, digestOf(text));
18084
17946
  else
18085
17947
  clearTurnDigest(file);
18086
17948
  };
18087
17949
  // packages/workit-core/src/core/route-intent.ts
18088
- import { spawnSync as spawnSync12 } from "node:child_process";
17950
+ import { spawnSync as spawnSync11 } from "node:child_process";
18089
17951
  import { existsSync as existsSync6 } from "node:fs";
18090
- import path21 from "node:path";
17952
+ import path20 from "node:path";
18091
17953
 
18092
17954
  // packages/workit-core/src/hooks/raw-git.ts
18093
- import { spawnSync as spawnSync11 } from "node:child_process";
17955
+ import { spawnSync as spawnSync10 } from "node:child_process";
18094
17956
  import { createHash as createHash14 } from "node:crypto";
18095
- import fs16 from "node:fs";
17957
+ import fs15 from "node:fs";
18096
17958
  import os5 from "node:os";
18097
- import path20 from "node:path";
17959
+ import path19 from "node:path";
18098
17960
 
18099
17961
  // packages/workit-core/src/core/branch.ts
18100
17962
  import { createHash as createHash12 } from "node:crypto";
@@ -18306,8 +18168,8 @@ var globOf = (pattern) => pattern.source.replace(/^\^|\$$/gu, "").replaceAll(".*
18306
18168
 
18307
18169
  // packages/workit-core/src/hooks/session-marker.ts
18308
18170
  import { createHash as createHash13 } from "node:crypto";
18309
- import fs15 from "node:fs";
18310
- import path19 from "node:path";
18171
+ import fs14 from "node:fs";
18172
+ import path18 from "node:path";
18311
18173
  var sessionMarkerFile = (input, session, store) => {
18312
18174
  let dir = store;
18313
18175
  if (dir === undefined) {
@@ -18317,12 +18179,12 @@ var sessionMarkerFile = (input, session, store) => {
18317
18179
  dir = location.dir;
18318
18180
  }
18319
18181
  const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
18320
- return path19.join(dir, "hooks", `skills-${name}.json`);
18182
+ return path18.join(dir, "hooks", `skills-${name}.json`);
18321
18183
  };
18322
18184
  var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
18323
18185
  var readSessionMarker = (file) => {
18324
18186
  try {
18325
- const value = JSON.parse(fs15.readFileSync(file, "utf8"));
18187
+ const value = JSON.parse(fs14.readFileSync(file, "utf8"));
18326
18188
  return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
18327
18189
  } catch {
18328
18190
  return { nudged: [], loaded: [], raw: [] };
@@ -18330,8 +18192,8 @@ var readSessionMarker = (file) => {
18330
18192
  };
18331
18193
  var writeSessionMarker = (file, marker) => {
18332
18194
  try {
18333
- fs15.mkdirSync(path19.dirname(file), { recursive: true });
18334
- fs15.writeFileSync(file, JSON.stringify(marker));
18195
+ fs14.mkdirSync(path18.dirname(file), { recursive: true });
18196
+ fs14.writeFileSync(file, JSON.stringify(marker));
18335
18197
  } catch {}
18336
18198
  };
18337
18199
 
@@ -18828,14 +18690,14 @@ var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
18828
18690
  var ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
18829
18691
  var MAX_DEPTH = 4;
18830
18692
  var MAX_EXPANSIONS = 16;
18831
- var isAbsolute = (value) => path20.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
18693
+ var isAbsolute = (value) => path19.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
18832
18694
  var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
18833
18695
  var joinDir = (base, raw) => {
18834
18696
  const next = raw.replace(HERE, ".");
18835
18697
  if (base === undefined || /[$`*?]/.test(next) || next === "-")
18836
18698
  return;
18837
- const expanded = next === "~" || next.startsWith("~/") ? path20.join(os5.homedir(), next.slice(1)) : next;
18838
- return base === null || isAbsolute(expanded) ? expanded : path20.join(base, expanded);
18699
+ const expanded = next === "~" || next.startsWith("~/") ? path19.join(os5.homedir(), next.slice(1)) : next;
18700
+ return base === null || isAbsolute(expanded) ? expanded : path19.join(base, expanded);
18839
18701
  };
18840
18702
  var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
18841
18703
  var cdTarget = (words) => {
@@ -19173,7 +19035,16 @@ function rawInvocations(command, dialect = "posix", depth = 0) {
19173
19035
  return found;
19174
19036
  }
19175
19037
  var RECENT = new Map;
19176
- function walk2(command, dialect, depth) {
19038
+ function commandLines(command, dialect = "posix") {
19039
+ const lines = [];
19040
+ try {
19041
+ walk2(command, dialect, 0, lines);
19042
+ } catch (error) {
19043
+ throw error instanceof ShellAnalysisError ? error : new ShellAnalysisError(error instanceof Error ? error.message : String(error));
19044
+ }
19045
+ return lines;
19046
+ }
19047
+ function walk2(command, dialect, depth, seen) {
19177
19048
  const found = [];
19178
19049
  let dir = null;
19179
19050
  const groups = [];
@@ -19184,7 +19055,8 @@ function walk2(command, dialect, depth) {
19184
19055
  const nested = (script, inner) => {
19185
19056
  if (!script || depth >= MAX_DEPTH)
19186
19057
  return;
19187
- for (const invocation of rawInvocations(script, inner, depth + 1))
19058
+ const inside = seen ? walk2(script, inner, depth + 1, seen) : rawInvocations(script, inner, depth + 1);
19059
+ for (const invocation of inside)
19188
19060
  found.push({ ...invocation, dir: rebase(dir, invocation.dir) });
19189
19061
  };
19190
19062
  const visit = (input, stdin, hops) => {
@@ -19196,11 +19068,17 @@ function walk2(command, dialect, depth) {
19196
19068
  const name = commandName(words[0]);
19197
19069
  if (!name)
19198
19070
  return;
19071
+ seen?.push(words);
19072
+ if (name === "-c" || name === "--command")
19073
+ return nested(words[1], "posix");
19199
19074
  const runs = (lines) => {
19200
19075
  if (hops < MAX_DEPTH)
19201
19076
  for (const next of lines)
19202
19077
  visit(next, undefined, hops + 1);
19203
19078
  };
19079
+ const wrapped = seen ? null : workitCheckTail(words);
19080
+ if (wrapped)
19081
+ return "script" in wrapped ? nested(wrapped.script, "posix") : runs([wrapped.argv]);
19204
19082
  if (CD.has(name)) {
19205
19083
  const target = cdTarget(words);
19206
19084
  dir = target === null ? os5.homedir() : joinDir(dir, target);
@@ -19291,6 +19169,51 @@ var LAUNCHERS = new Set([
19291
19169
  "command"
19292
19170
  ]);
19293
19171
  var WORKIT_BIN = /(?:^|[\\/])workit(?:\.exe)?$|^@brainervirus\/workit-cli$/;
19172
+ var WORKIT_PACKAGE = /^@brainervirus\/workit-cli(?:@\S*)?$/;
19173
+ var PACKAGE_RUNNERS = new Set(["npx", "bunx", "pnpx"]);
19174
+ function workitCheckTail(words) {
19175
+ let at = 0;
19176
+ if (PACKAGE_RUNNERS.has(commandName(words[0]))) {
19177
+ at = words.findIndex((word, index) => index > 0 && !word.startsWith("-"));
19178
+ if (at < 0 || !WORKIT_PACKAGE.test(words[at]))
19179
+ return null;
19180
+ } else if (!/^workit(?:\.exe|\.cmd)?$/.test(commandName(words[0])))
19181
+ return null;
19182
+ const args = words.slice(at + 1);
19183
+ if (args[0] !== "check" && !(args[0] === "lock" && args[1] === "run"))
19184
+ return null;
19185
+ const split = args.indexOf("--");
19186
+ const tail = split < 0 ? [] : args.slice(split + 1);
19187
+ if (!tail.length)
19188
+ return null;
19189
+ return args.slice(0, split).includes("--shell") ? { script: tail.join(" ") } : { argv: tail };
19190
+ }
19191
+ function simpleCommand(command, dialect = "posix", depth = 0) {
19192
+ let segments;
19193
+ try {
19194
+ segments = segmentsOf(command, dialect);
19195
+ } catch {
19196
+ return null;
19197
+ }
19198
+ if (segments.length !== 1)
19199
+ return null;
19200
+ const [segment] = segments;
19201
+ if (segment.group || segment.pipe || segment.unanalyzable || segment.redirects.length || segment.subs?.length || segment.stdin?.length)
19202
+ return null;
19203
+ const { words } = stripPrefixes(segment.words);
19204
+ const name = commandName(words[0]);
19205
+ if (!name || depth >= MAX_DEPTH)
19206
+ return null;
19207
+ if (SHELLS.has(name)) {
19208
+ const script = shellScript(words, undefined);
19209
+ return script ? simpleCommand(script, "posix", depth + 1) : null;
19210
+ }
19211
+ if (name === "-c" || name === "--command")
19212
+ return words[1] ? simpleCommand(words[1], "posix", depth + 1) : null;
19213
+ if (EVALS.has(name) || POWERSHELLS.has(name) || ["xargs", "find", "parallel", "script", "cmd", "source", "."].includes(name))
19214
+ return null;
19215
+ return words;
19216
+ }
19294
19217
  function workitInvocations(command, dialect = "posix", depth = 0) {
19295
19218
  const found = [];
19296
19219
  for (const { words } of segmentsOf(command, dialect)) {
@@ -19586,7 +19509,7 @@ var repoAt = (dir) => {
19586
19509
  return { location, dir, branch: resolveTaskKey(dir, location).branch };
19587
19510
  };
19588
19511
  var isWorkitWorkspace = (repo) => {
19589
- if (fs16.existsSync(repo.location.dir))
19512
+ if (fs15.existsSync(repo.location.dir))
19590
19513
  return true;
19591
19514
  try {
19592
19515
  return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
@@ -19607,17 +19530,17 @@ var headSha2 = (repo) => {
19607
19530
  if (!git || git.reftable)
19608
19531
  return null;
19609
19532
  try {
19610
- const head = fs16.readFileSync(path20.join(git.gitDir, "HEAD"), "utf8").trim();
19533
+ const head = fs15.readFileSync(path19.join(git.gitDir, "HEAD"), "utf8").trim();
19611
19534
  if (/^[0-9a-f]{40,64}$/.test(head))
19612
19535
  return head;
19613
19536
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
19614
19537
  if (!ref)
19615
19538
  return null;
19616
- const common = path20.dirname(repo.location.dir);
19539
+ const common = path19.dirname(repo.location.dir);
19617
19540
  try {
19618
- return fs16.readFileSync(path20.join(common, ref), "utf8").trim() || null;
19541
+ return fs15.readFileSync(path19.join(common, ref), "utf8").trim() || null;
19619
19542
  } catch {}
19620
- const packed = fs16.readFileSync(path20.join(common, "packed-refs"), "utf8");
19543
+ const packed = fs15.readFileSync(path19.join(common, "packed-refs"), "utf8");
19621
19544
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
19622
19545
  } catch {
19623
19546
  return null;
@@ -19629,7 +19552,7 @@ var gitConfig = (dir, invocation, cache) => {
19629
19552
  if (cached)
19630
19553
  return cached;
19631
19554
  const config = new Map;
19632
- const listed = spawnSync11("git", [...invocation.config ?? [], "config", "-z", "--list"], {
19555
+ const listed = spawnSync10("git", [...invocation.config ?? [], "config", "-z", "--list"], {
19633
19556
  cwd: dir,
19634
19557
  encoding: "utf8",
19635
19558
  timeout: 3000,
@@ -19705,8 +19628,8 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
19705
19628
  const sub = invocation.args[0];
19706
19629
  if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
19707
19630
  return [invocation];
19708
- const dir = path20.resolve(root, invocation.dir ?? ".");
19709
- if (!fs16.existsSync(dir))
19631
+ const dir = path19.resolve(root, invocation.dir ?? ".");
19632
+ if (!fs15.existsSync(dir))
19710
19633
  return [invocation];
19711
19634
  const config = gitConfig(dir, invocation, cache);
19712
19635
  const alias = configValue(config, `alias.${sub.toLowerCase()}`);
@@ -19728,11 +19651,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
19728
19651
  var normalizedUrl = (url, dir) => {
19729
19652
  const trimmed = url.trim();
19730
19653
  if (/^file:\/\//i.test(trimmed))
19731
- return path20.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
19654
+ return path19.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
19732
19655
  const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
19733
19656
  const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
19734
19657
  if (!scheme && !scp)
19735
- return path20.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
19658
+ return path19.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
19736
19659
  const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
19737
19660
  return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
19738
19661
  };
@@ -19793,7 +19716,7 @@ var refusal = (action, branch, dir) => {
19793
19716
  };
19794
19717
  var refFacts = (repo, remote) => {
19795
19718
  const facts = { heads: new Set, tags: new Set, remoteHeads: new Set };
19796
- const listed = spawnSync11("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/tags", `refs/remotes/${remote}`], { cwd: repo.dir, encoding: "utf8", timeout: 5000 });
19719
+ const listed = spawnSync10("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/tags", `refs/remotes/${remote}`], { cwd: repo.dir, encoding: "utf8", timeout: 5000 });
19797
19720
  const remotePrefix = `refs/remotes/${remote}/`;
19798
19721
  for (const ref of (listed.stdout ?? "").split(`
19799
19722
  `)) {
@@ -19810,7 +19733,7 @@ var globBranches = (repo, glob) => {
19810
19733
  const source = glob.source.startsWith("refs/") ? glob.source : `refs/heads/${glob.source}`;
19811
19734
  const [sourcePrefix, sourceSuffix = ""] = source.split("*");
19812
19735
  const [destinationPrefix, destinationSuffix = ""] = glob.destination.split("*");
19813
- const listed = spawnSync11("git", [
19736
+ const listed = spawnSync10("git", [
19814
19737
  "for-each-ref",
19815
19738
  "--format=%(refname)",
19816
19739
  sourcePrefix.slice(0, sourcePrefix.lastIndexOf("/") + 1)
@@ -19887,7 +19810,7 @@ var tagRefusal = (action, facts, dir) => {
19887
19810
  return `workit: raw ${quote(`git push${action.allTags ? " --tags" : ""}`)} of ${tags.length ? `tag ${tags.join(", ")}` : "tags"} bypasses the release grant. Push a release tag with ${quote(`workit git push --release ${named}`)} (${shape} tags whose commit the production branch already has; a tag never moves), under the ${quote("release")} grant (if refused, ask the user to run ${quote("workit grant set <workspace> release=true")}).`;
19888
19811
  };
19889
19812
  var remoteHeads = (repo, remote) => {
19890
- const listed = spawnSync11("git", ["ls-remote", "--heads", remote], {
19813
+ const listed = spawnSync10("git", ["ls-remote", "--heads", remote], {
19891
19814
  cwd: repo.dir,
19892
19815
  encoding: "utf8",
19893
19816
  timeout: 3000,
@@ -19956,7 +19879,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
19956
19879
  const out = [];
19957
19880
  const repos = new Map;
19958
19881
  const repoFor = (relative) => {
19959
- const dir = path20.resolve(cwd, relative ?? ".");
19882
+ const dir = path19.resolve(cwd, relative ?? ".");
19960
19883
  if (!repos.has(dir)) {
19961
19884
  const repo = repoAt(dir);
19962
19885
  repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
@@ -20042,7 +19965,7 @@ var analysisDenial = (error) => ({
20042
19965
  });
20043
19966
  var NEXT_COMMAND = "next";
20044
19967
  var MARKER_TTL_SECONDS = 3600;
20045
- var markerFile = (repo, session, key) => path20.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
19968
+ var markerFile = (repo, session, key) => path19.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
20046
19969
  var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
20047
19970
  function noteRawCommit(input, command, key) {
20048
19971
  try {
@@ -20053,14 +19976,14 @@ function noteRawCommit(input, command, key) {
20053
19976
  if (action.kind !== "commit")
20054
19977
  continue;
20055
19978
  const file = markerFile(repo, input.session.id, key);
20056
- fs16.mkdirSync(path20.dirname(file), { recursive: true });
20057
- fs16.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
19979
+ fs15.mkdirSync(path19.dirname(file), { recursive: true });
19980
+ fs15.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
20058
19981
  `);
20059
19982
  }
20060
19983
  } catch {}
20061
19984
  }
20062
19985
  var headCommit = (dir) => {
20063
- const run = spawnSync11("git", [
19986
+ const run = spawnSync10("git", [
20064
19987
  "log",
20065
19988
  "-1",
20066
19989
  "--format=%H%x1f%ct%x1f%s%x1f%(trailers:key=Workit-Session,valueonly,separator=%x2c)"
@@ -20077,11 +20000,11 @@ var headCommit = (dir) => {
20077
20000
  var settle2 = (input, repo, file) => {
20078
20001
  let marker;
20079
20002
  try {
20080
- marker = JSON.parse(fs16.readFileSync(file, "utf8"));
20003
+ marker = JSON.parse(fs15.readFileSync(file, "utf8"));
20081
20004
  } catch {
20082
20005
  return;
20083
20006
  }
20084
- fs16.rmSync(file, { force: true });
20007
+ fs15.rmSync(file, { force: true });
20085
20008
  const session = input.session.id;
20086
20009
  const now = Date.now() / 1000;
20087
20010
  if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
@@ -20130,7 +20053,7 @@ function settlePendingCommit(input) {
20130
20053
  if (!repo)
20131
20054
  return;
20132
20055
  const file = markerFile(repo, input.session.id, NEXT_COMMAND);
20133
- if (fs16.existsSync(file))
20056
+ if (fs15.existsSync(file))
20134
20057
  settle2(input, repo, file);
20135
20058
  } catch {}
20136
20059
  }
@@ -20274,7 +20197,7 @@ var targetsOf = (invocations) => invocations.flatMap((invocation) => invocation.
20274
20197
  branch,
20275
20198
  dir: invocation.dir
20276
20199
  })) : []);
20277
- var localBranchExists = (dir, branch) => spawnSync12("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
20200
+ var localBranchExists = (dir, branch) => spawnSync11("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
20278
20201
  cwd: dir,
20279
20202
  stdio: "ignore",
20280
20203
  timeout: 5000
@@ -20284,7 +20207,7 @@ var judgedDir = (root, dir) => {
20284
20207
  return null;
20285
20208
  if (dir === null)
20286
20209
  return root;
20287
- const resolved = path21.resolve(root, dir);
20210
+ const resolved = path20.resolve(root, dir);
20288
20211
  return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
20289
20212
  };
20290
20213
  var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
@@ -20318,22 +20241,771 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
20318
20241
  }
20319
20242
  return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
20320
20243
  };
20244
+ // packages/workit-core/src/core/heavy-rules.ts
20245
+ import fs16 from "node:fs";
20246
+ import os6 from "node:os";
20247
+ import path21 from "node:path";
20248
+ var EMPTY = () => ({ deny: [], allow: [] });
20249
+ var readText2 = (file) => {
20250
+ try {
20251
+ return fs16.readFileSync(file, "utf8");
20252
+ } catch (error) {
20253
+ if (error.code === "ENOENT")
20254
+ return null;
20255
+ throw error;
20256
+ }
20257
+ };
20258
+ var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
20259
+ function parseJsonc(text) {
20260
+ let out = "";
20261
+ for (let index = 0;index < text.length; index += 1) {
20262
+ const char = text[index];
20263
+ if (char === '"') {
20264
+ const end = /"(?:[^"\\]|\\.)*"/y;
20265
+ end.lastIndex = index;
20266
+ const match = end.exec(text);
20267
+ if (!match)
20268
+ throw new Error("unterminated string");
20269
+ out += match[0];
20270
+ index += match[0].length - 1;
20271
+ } else if (char === "/" && text[index + 1] === "/") {
20272
+ while (index < text.length && text[index] !== `
20273
+ `)
20274
+ index += 1;
20275
+ out += `
20276
+ `;
20277
+ } else if (char === "/" && text[index + 1] === "*") {
20278
+ const close = text.indexOf("*/", index + 2);
20279
+ if (close < 0)
20280
+ throw new Error("unterminated comment");
20281
+ index = close + 1;
20282
+ } else
20283
+ out += char;
20284
+ }
20285
+ return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
20286
+ }
20287
+ function gitTop(cwd) {
20288
+ for (let dir = path21.resolve(cwd);; dir = path21.dirname(dir)) {
20289
+ if (fs16.existsSync(path21.join(dir, ".git")))
20290
+ return dir;
20291
+ if (path21.dirname(dir) === dir)
20292
+ return null;
20293
+ }
20294
+ }
20295
+ var projectDirs = (cwd) => {
20296
+ const top = gitTop(cwd);
20297
+ const dirs = [];
20298
+ for (let dir = path21.resolve(cwd);; dir = path21.dirname(dir)) {
20299
+ dirs.push(dir);
20300
+ if (!top || dir === top || path21.dirname(dir) === dir)
20301
+ return dirs;
20302
+ }
20303
+ };
20304
+ var homeOf = (env) => env.HOME || os6.homedir();
20305
+ var CLAUDE_MANAGED = {
20306
+ linux: "/etc/claude-code/managed-settings.json",
20307
+ darwin: "/Library/Application Support/ClaudeCode/managed-settings.json",
20308
+ win32: "C:\\Program Files\\ClaudeCode\\managed-settings.json"
20309
+ };
20310
+ function claudeSettingsFiles(cwd, env, platform = process.platform) {
20311
+ const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
20312
+ const user = env.CLAUDE_CONFIG_DIR || path21.join(homeOf(env), ".claude");
20313
+ return [
20314
+ ...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
20315
+ path21.join(user, "settings.json"),
20316
+ path21.join(project, ".claude", "settings.json"),
20317
+ path21.join(project, ".claude", "settings.local.json")
20318
+ ];
20319
+ }
20320
+ var bashPattern = (entry) => {
20321
+ const match = /^Bash(?:\((.*)\))?$/su.exec(entry.trim());
20322
+ return match ? match[1] ?? "" : null;
20323
+ };
20324
+ function claudeRules(cwd, env = process.env) {
20325
+ const rules = EMPTY();
20326
+ try {
20327
+ for (const file of claudeSettingsFiles(cwd, env)) {
20328
+ const text = readText2(file);
20329
+ if (text === null)
20330
+ continue;
20331
+ const value = JSON.parse(text);
20332
+ if (!isRecord3(value))
20333
+ return "unreadable";
20334
+ const permissions = value.permissions;
20335
+ if (permissions === undefined)
20336
+ continue;
20337
+ if (!isRecord3(permissions))
20338
+ return "unreadable";
20339
+ for (const [key, bucket] of [
20340
+ ["deny", rules.deny],
20341
+ ["ask", rules.deny],
20342
+ ["allow", rules.allow]
20343
+ ]) {
20344
+ const list = permissions[key];
20345
+ if (list === undefined)
20346
+ continue;
20347
+ if (!Array.isArray(list))
20348
+ return "unreadable";
20349
+ for (const entry of list) {
20350
+ if (typeof entry !== "string")
20351
+ return "unreadable";
20352
+ const pattern = bashPattern(entry);
20353
+ if (pattern !== null)
20354
+ bucket.push(pattern);
20355
+ }
20356
+ }
20357
+ }
20358
+ } catch {
20359
+ return "unreadable";
20360
+ }
20361
+ return rules;
20362
+ }
20363
+ var OPENCODE_NAMES = ["opencode.json", "opencode.jsonc"];
20364
+ var opencodePermission = (permission, rules) => {
20365
+ if (permission === undefined)
20366
+ return true;
20367
+ if (typeof permission === "string") {
20368
+ (permission === "allow" ? rules.allow : rules.deny).push("");
20369
+ return ["allow", "ask", "deny"].includes(permission);
20370
+ }
20371
+ if (!isRecord3(permission))
20372
+ return false;
20373
+ for (const key of ["bash", "shell"]) {
20374
+ const value = permission[key];
20375
+ if (value === undefined)
20376
+ continue;
20377
+ if (typeof value === "string") {
20378
+ if (!["allow", "ask", "deny"].includes(value))
20379
+ return false;
20380
+ (value === "allow" ? rules.allow : rules.deny).push("");
20381
+ continue;
20382
+ }
20383
+ if (!isRecord3(value))
20384
+ return false;
20385
+ for (const [pattern, action] of Object.entries(value)) {
20386
+ if (action !== "allow" && action !== "ask" && action !== "deny")
20387
+ return false;
20388
+ (action === "allow" ? rules.allow : rules.deny).push(pattern);
20389
+ }
20390
+ }
20391
+ return true;
20392
+ };
20393
+ var opencodeConfig = (value, rules) => {
20394
+ if (!isRecord3(value))
20395
+ return false;
20396
+ if (!opencodePermission(value.permission, rules))
20397
+ return false;
20398
+ if (value.agent === undefined)
20399
+ return true;
20400
+ if (!isRecord3(value.agent))
20401
+ return false;
20402
+ return Object.values(value.agent).every((agent) => isRecord3(agent) && opencodePermission(agent.permission, rules));
20403
+ };
20404
+ function opencodeRules(cwd, env = process.env) {
20405
+ const rules = EMPTY();
20406
+ try {
20407
+ const config = env.XDG_CONFIG_HOME || path21.join(homeOf(env), ".config");
20408
+ const files = [
20409
+ ...OPENCODE_NAMES.map((name) => path21.join(config, "opencode", name)),
20410
+ ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path21.join(dir, name), path21.join(dir, ".opencode", name)])),
20411
+ ...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
20412
+ ];
20413
+ for (const file of files) {
20414
+ const text = readText2(file);
20415
+ if (text !== null && !opencodeConfig(parseJsonc(text), rules))
20416
+ return "unreadable";
20417
+ }
20418
+ if (env.OPENCODE_CONFIG_CONTENT && !opencodeConfig(parseJsonc(env.OPENCODE_CONFIG_CONTENT), rules))
20419
+ return "unreadable";
20420
+ } catch {
20421
+ return "unreadable";
20422
+ }
20423
+ return rules;
20424
+ }
20425
+ var codexRule = (body) => {
20426
+ const open = /\bpattern\s*=\s*\[/u.exec(body);
20427
+ if (!open)
20428
+ return null;
20429
+ const words = [];
20430
+ let index = open.index + open[0].length;
20431
+ for (;; ) {
20432
+ while (/[\s,]/u.test(body[index] ?? ""))
20433
+ index += 1;
20434
+ if (body[index] === "]")
20435
+ break;
20436
+ if (body[index] !== '"')
20437
+ return null;
20438
+ const literal = /"((?:[^"\\]|\\.)*)"/y;
20439
+ literal.lastIndex = index;
20440
+ const match = literal.exec(body);
20441
+ if (!match)
20442
+ return null;
20443
+ words.push(JSON.parse(`"${match[1]}"`));
20444
+ index = literal.lastIndex;
20445
+ }
20446
+ const decision = /\bdecision\s*=\s*"(\w+)"/u.exec(body)?.[1];
20447
+ if (!words.length || !decision || !["allow", "prompt", "forbidden"].includes(decision))
20448
+ return null;
20449
+ return { pattern: words.join(" "), decision };
20450
+ };
20451
+ function codexRules(cwd, env = process.env) {
20452
+ const rules = EMPTY();
20453
+ try {
20454
+ const home = env.CODEX_HOME || path21.join(homeOf(env), ".codex");
20455
+ const top = gitTop(cwd) ?? cwd;
20456
+ for (const dir of [path21.join(home, "rules"), path21.join(top, ".codex", "rules")]) {
20457
+ let files;
20458
+ try {
20459
+ files = fs16.readdirSync(dir).filter((name) => name.endsWith(".rules"));
20460
+ } catch (error) {
20461
+ if (error.code === "ENOENT")
20462
+ continue;
20463
+ return "unreadable";
20464
+ }
20465
+ for (const name of files) {
20466
+ const text = readText2(path21.join(dir, name)) ?? "";
20467
+ for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
20468
+ const rule = codexRule(part);
20469
+ if (!rule)
20470
+ return "unreadable";
20471
+ (rule.decision === "allow" ? rules.allow : rules.deny).push(rule.pattern);
20472
+ }
20473
+ }
20474
+ }
20475
+ } catch {
20476
+ return "unreadable";
20477
+ }
20478
+ return rules;
20479
+ }
20480
+ function cursorRules(cwd, env = process.env) {
20481
+ const rules = EMPTY();
20482
+ try {
20483
+ const top = gitTop(cwd) ?? cwd;
20484
+ for (const file of [
20485
+ path21.join(homeOf(env), ".cursor", "cli-config.json"),
20486
+ path21.join(top, ".cursor", "cli.json")
20487
+ ]) {
20488
+ const text = readText2(file);
20489
+ if (text === null)
20490
+ continue;
20491
+ const value = JSON.parse(text);
20492
+ if (!isRecord3(value))
20493
+ return "unreadable";
20494
+ if (value.permissions === undefined)
20495
+ continue;
20496
+ if (!isRecord3(value.permissions))
20497
+ return "unreadable";
20498
+ for (const [key, bucket] of [
20499
+ ["deny", rules.deny],
20500
+ ["allow", rules.allow]
20501
+ ]) {
20502
+ const list = value.permissions[key];
20503
+ if (list === undefined)
20504
+ continue;
20505
+ if (!Array.isArray(list))
20506
+ return "unreadable";
20507
+ for (const entry of list) {
20508
+ if (typeof entry !== "string")
20509
+ return "unreadable";
20510
+ const match = /^Shell(?:\((.*)\))?$/su.exec(entry.trim());
20511
+ if (match)
20512
+ bucket.push(match[1] ?? "");
20513
+ }
20514
+ }
20515
+ }
20516
+ } catch {
20517
+ return "unreadable";
20518
+ }
20519
+ return rules;
20520
+ }
20521
+ var baseName = (word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "");
20522
+ var firstToken = (pattern) => pattern.trim().split(/[:\s]/u)[0] ?? "";
20523
+ function names(pattern, words) {
20524
+ const token = firstToken(pattern);
20525
+ if (!token || token === "*" || token.includes("*"))
20526
+ return true;
20527
+ const wanted = baseName(token);
20528
+ return words.some((word) => baseName(word) === wanted);
20529
+ }
20530
+ var literalPrefix = (pattern) => pattern.trim().replace(/(?::\*| \*|\*)$/u, "");
20531
+ var coversWrapper = (pattern) => {
20532
+ const token = firstToken(pattern);
20533
+ if (!token || token.includes("*"))
20534
+ return true;
20535
+ const words = literalPrefix(pattern).split(/\s+/u);
20536
+ if (baseName(words[0]) !== "workit")
20537
+ return false;
20538
+ return ["check", "--heavy"].slice(0, words.length - 1).every((word, at) => {
20539
+ const given = words[at + 1];
20540
+ return given === word || word.startsWith(given);
20541
+ });
20542
+ };
20543
+ var coversCommand = (pattern, command) => {
20544
+ const prefix = literalPrefix(pattern);
20545
+ if (!prefix)
20546
+ return true;
20547
+ return !prefix.includes("*") && command.trim().startsWith(prefix);
20548
+ };
20549
+ function wrapAllowed(read, words, command, options = {}) {
20550
+ if (read === "unreadable")
20551
+ return { ok: false, why: "named" };
20552
+ if (read.deny.some((pattern) => names(pattern, words)))
20553
+ return { ok: false, why: "named" };
20554
+ if (!options.bypass && read.allow.some(coversWrapper) && !read.allow.some((pattern) => coversCommand(pattern, command)))
20555
+ return { ok: false, why: "allowlisted" };
20556
+ return { ok: true };
20557
+ }
20558
+
20559
+ // packages/workit-core/src/core/heavy.ts
20560
+ import fs17 from "node:fs";
20561
+ import os7 from "node:os";
20562
+ import path22 from "node:path";
20563
+ function stateDir(env, platform = process.platform) {
20564
+ if (env.WORKFLOW_TOOLKIT_STATE)
20565
+ return env.WORKFLOW_TOOLKIT_STATE;
20566
+ if (env.XDG_STATE_HOME)
20567
+ return path22.join(env.XDG_STATE_HOME, "workit");
20568
+ const home = env.HOME || os7.homedir();
20569
+ if (platform === "darwin")
20570
+ return path22.join(home, "Library", "Application Support", "workit");
20571
+ if (platform === "win32")
20572
+ return path22.join(env.LOCALAPPDATA ?? path22.join(home, "AppData", "Local"), "workit");
20573
+ return path22.join(home, ".local", "state", "workit");
20574
+ }
20575
+ var HEAVY_COMMANDS_FILE = "heavy-commands.json";
20576
+ var heavyCommandsFile = (env) => path22.join(stateDir(env), HEAVY_COMMANDS_FILE);
20577
+ 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;
20578
+ var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
20579
+ var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
20580
+ var WATCH_SCRIPT = /(?:^|[:_.-])(?:watch|dev|serve|start|storybook)(?:$|[:_.-])/iu;
20581
+ var sub = (...names) => new Set(names);
20582
+ var TOOLS = {
20583
+ jest: "any",
20584
+ vitest: "any",
20585
+ mocha: "any",
20586
+ ava: "any",
20587
+ tap: "any",
20588
+ karma: "any",
20589
+ jasmine: "any",
20590
+ nyc: "any",
20591
+ c8: "any",
20592
+ tsc: "any",
20593
+ "vue-tsc": "any",
20594
+ "svelte-check": "any",
20595
+ eslint: "any",
20596
+ stylelint: "any",
20597
+ tslint: "any",
20598
+ webpack: "any",
20599
+ "webpack-cli": "any",
20600
+ rollup: "any",
20601
+ parcel: "any",
20602
+ tsup: "any",
20603
+ vite: sub("build", "optimize"),
20604
+ next: sub("build", "lint"),
20605
+ nuxt: sub("build", "generate"),
20606
+ nuxi: sub("build", "generate"),
20607
+ astro: sub("build", "check"),
20608
+ ng: sub("build", "b", "test", "t", "e2e", "lint"),
20609
+ "react-scripts": sub("build", "test"),
20610
+ playwright: sub("test", "install"),
20611
+ cypress: sub("run"),
20612
+ wdio: "any",
20613
+ testcafe: "any",
20614
+ turbo: "any",
20615
+ lerna: sub("run", "exec", "bootstrap"),
20616
+ "sonar-scanner": "any",
20617
+ sonarless: sub("scan"),
20618
+ "sonarless-mcp": sub("scan"),
20619
+ go: sub("test", "build", "vet", "install", "generate"),
20620
+ "golangci-lint": sub("run"),
20621
+ gotestsum: "any",
20622
+ staticcheck: "any",
20623
+ cargo: sub("build", "b", "test", "t", "check", "c", "clippy", "bench", "nextest", "doc", "install", "tarpaulin", "llvm-cov"),
20624
+ mvn: "any",
20625
+ mvnw: "any",
20626
+ gradle: "any",
20627
+ gradlew: "any",
20628
+ ant: "any",
20629
+ sbt: "any",
20630
+ bazel: sub("build", "test", "run", "coverage"),
20631
+ bazelisk: sub("build", "test", "run", "coverage"),
20632
+ buck2: sub("build", "test", "run"),
20633
+ dotnet: sub("build", "test", "publish", "restore", "pack", "msbuild"),
20634
+ msbuild: "any",
20635
+ pytest: "any",
20636
+ "py.test": "any",
20637
+ tox: "any",
20638
+ nox: "any",
20639
+ mypy: "any",
20640
+ pyright: "any",
20641
+ pylint: "any",
20642
+ coverage: sub("run"),
20643
+ maturin: sub("build", "develop"),
20644
+ pip: sub("install", "wheel"),
20645
+ pip3: sub("install", "wheel"),
20646
+ rspec: "any",
20647
+ rails: sub("test", "assets:precompile"),
20648
+ phpunit: "any",
20649
+ pest: "any",
20650
+ phpstan: "any",
20651
+ psalm: "any",
20652
+ composer: sub("install", "update", "require", "test"),
20653
+ mix: sub("test", "compile", "deps.get", "dialyzer", "credo"),
20654
+ swift: sub("build", "test"),
20655
+ xcodebuild: "any",
20656
+ flutter: sub("build", "test"),
20657
+ dart: sub("test", "compile"),
20658
+ pod: sub("install"),
20659
+ cmake: sub("--build"),
20660
+ ninja: "any",
20661
+ meson: sub("compile", "test"),
20662
+ ctest: "any",
20663
+ scons: "any",
20664
+ "docker-compose": sub("build"),
20665
+ buildah: sub("bud", "build"),
20666
+ earthly: "any"
20667
+ };
20668
+ var PM_VALUES = new Set([
20669
+ "--prefix",
20670
+ "-w",
20671
+ "--workspace",
20672
+ "-C",
20673
+ "--dir",
20674
+ "--filter",
20675
+ "-F",
20676
+ "--cwd",
20677
+ "-p",
20678
+ "--package",
20679
+ "--cache",
20680
+ "--registry"
20681
+ ]);
20682
+ function positional2(words, from, values = PM_VALUES) {
20683
+ let index = from;
20684
+ while (index < words.length && words[index].startsWith("-")) {
20685
+ if (words[index] === "--")
20686
+ return index + 1;
20687
+ index += values.has(words[index]) ? 2 : 1;
20688
+ }
20689
+ return index;
20690
+ }
20691
+ var baseName2 = (word) => (word?.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat|ps1)$/u, "");
20692
+ var WORKIT = /^(?:workit|@brainervirus\/workit-cli(?:@.*)?)$/u;
20693
+ var PYTHONS = /^(?:python[\d.]*|py)$/u;
20694
+ var NPM_INSTALL = new Set([
20695
+ "ci",
20696
+ "clean-install",
20697
+ "cit",
20698
+ "install-test",
20699
+ "it",
20700
+ "install",
20701
+ "i",
20702
+ "in",
20703
+ "add",
20704
+ "rebuild",
20705
+ "update",
20706
+ "up"
20707
+ ]);
20708
+ var NPM_RUN = new Set(["run", "run-script", "rum", "urn"]);
20709
+ var NPM_TEST = new Set(["test", "t", "tst"]);
20710
+ var EXEC = new Set(["exec", "x", "dlx"]);
20711
+ function builtIn(words, depth = 0) {
20712
+ if (depth > 6 || !words.length)
20713
+ return null;
20714
+ const name = baseName2(words[0]);
20715
+ const label = () => words.slice(0, 3).join(" ");
20716
+ const inner = (from) => builtIn(words.slice(from), depth + 1);
20717
+ if (WORKIT.test(name))
20718
+ return null;
20719
+ if (INFO_FLAGS.has(words[1] ?? "") && words.length === 2)
20720
+ return null;
20721
+ if (words.some((word) => word === "--watch" || word.startsWith("--watch=") || word === "--watchAll"))
20722
+ return null;
20723
+ if (name === "npx" || name === "bunx" || name === "pnpx")
20724
+ return inner(positional2(words, 1));
20725
+ if (name === "corepack")
20726
+ return inner(1);
20727
+ if (name === "npm" || name === "pnpm" || name === "yarn" || name === "bun" || name === "deno") {
20728
+ const at = positional2(words, 1);
20729
+ const command = words[at];
20730
+ if (command === undefined)
20731
+ return name === "yarn" ? "yarn (install)" : null;
20732
+ if (name === "deno") {
20733
+ if (["test", "bench", "check", "compile", "lint"].includes(command))
20734
+ return label();
20735
+ if (command === "task" && HEAVY_SCRIPT.test(words[at + 1] ?? ""))
20736
+ return label();
20737
+ return null;
20738
+ }
20739
+ if (NPM_INSTALL.has(command) || NPM_TEST.has(command))
20740
+ return label();
20741
+ if (name === "bun" && command === "build")
20742
+ return label();
20743
+ if (EXEC.has(command))
20744
+ return inner(positional2(words, at + 1));
20745
+ if (name === "yarn" && command === "workspace")
20746
+ return inner(at + 2);
20747
+ if (name === "yarn" && command === "workspaces")
20748
+ return words.slice(at + 1).some((word) => HEAVY_SCRIPT.test(word)) ? label() : null;
20749
+ if (NPM_RUN.has(command)) {
20750
+ const script = words[positional2(words, at + 1)];
20751
+ if (script === undefined || WATCH_SCRIPT.test(script))
20752
+ return null;
20753
+ if (HEAVY_SCRIPT.test(script))
20754
+ return label();
20755
+ return name === "bun" ? inner(positional2(words, at + 1)) : null;
20756
+ }
20757
+ if (name === "npm")
20758
+ return null;
20759
+ if (WATCH_SCRIPT.test(command))
20760
+ return null;
20761
+ if (HEAVY_SCRIPT.test(command) && !/\.[cm]?[jt]sx?$/u.test(command))
20762
+ return label();
20763
+ return inner(at);
20764
+ }
20765
+ if (PYTHONS.test(name)) {
20766
+ const at = words.indexOf("-m");
20767
+ return at > 0 && words[at + 1] ? inner(at + 1) : null;
20768
+ }
20769
+ if (["uv", "poetry", "pdm", "pipenv", "hatch", "rye"].includes(name)) {
20770
+ const at = positional2(words, 1);
20771
+ const command = words[at];
20772
+ if (command === "run")
20773
+ return inner(positional2(words, at + 1));
20774
+ if (command === "pip")
20775
+ return inner(at);
20776
+ return ["sync", "install", "build", "test", "add"].includes(command ?? "") ? label() : null;
20777
+ }
20778
+ if (name === "bundle") {
20779
+ const command = words[positional2(words, 1)];
20780
+ if (command === "exec")
20781
+ return inner(positional2(words, 2));
20782
+ return command === "install" || command === undefined ? label() : null;
20783
+ }
20784
+ if (name === "make" || name === "gmake") {
20785
+ const targets = words.slice(1).filter((word) => !word.startsWith("-") && !word.includes("="));
20786
+ if (targets.some((target) => WATCH_SCRIPT.test(target)))
20787
+ return null;
20788
+ return !targets.length || targets.some((target) => HEAVY_SCRIPT.test(target)) ? label() : null;
20789
+ }
20790
+ if (name === "rake") {
20791
+ const targets = words.slice(1).filter((word) => !word.startsWith("-") && !word.includes("="));
20792
+ return targets.some((target) => HEAVY_SCRIPT.test(target) || /^spec(?:$|:)/u.test(target)) ? label() : null;
20793
+ }
20794
+ if (name === "nx") {
20795
+ const at = positional2(words, 1);
20796
+ const command = words[at] ?? "";
20797
+ if (["run-many", "affected"].includes(command))
20798
+ return words.some((word) => /^(?:-t|--targets?)(?:=|$)/u.test(word)) && !words.some((word) => WATCH_SCRIPT.test(word.replace(/^--?targets?=/u, ""))) ? label() : null;
20799
+ if (command === "run") {
20800
+ const target = (words[at + 1] ?? "").split(":")[1] ?? "";
20801
+ return HEAVY_SCRIPT.test(target) && !WATCH_SCRIPT.test(target) ? label() : null;
20802
+ }
20803
+ return HEAVY_SCRIPT.test(command) && !WATCH_SCRIPT.test(command) ? label() : null;
20804
+ }
20805
+ if (name === "docker" || name === "podman" || name === "nerdctl") {
20806
+ const at = positional2(words, 1, new Set(["-H", "--host", "--context", "-c", "--config"]));
20807
+ const command = words[at];
20808
+ if (command === "build")
20809
+ return label();
20810
+ if (command === "buildx" || command === "image" || command === "builder")
20811
+ return words[positional2(words, at + 1)] === "build" ? label() : null;
20812
+ if (command === "compose") {
20813
+ const at2 = positional2(words, at + 1, new Set(["-f", "--file", "-p"]));
20814
+ const action = words[at2];
20815
+ if (action === "build")
20816
+ return label();
20817
+ return action === "up" && words.slice(at2 + 1).includes("--build") ? label() : null;
20818
+ }
20819
+ return null;
20820
+ }
20821
+ const rule = TOOLS[name];
20822
+ if (!rule)
20823
+ return null;
20824
+ if (words.slice(1).some((word) => WATCH_FLAG.test(word) || word === "watch" || word === "dev"))
20825
+ return null;
20826
+ if (rule === "any")
20827
+ return label();
20828
+ const command = words.slice(1).find((word) => !word.startsWith("-") && !word.startsWith("+") || rule.has(word));
20829
+ return command !== undefined && rule.has(command) ? label() : null;
20830
+ }
20831
+ var globWord = (word) => new RegExp(`^${word.replace(/[.+?^${}()|[\]\\]/gu, "\\$&").replaceAll("*", ".*")}$`, "u");
20832
+ function parsePattern(pattern) {
20833
+ const words = pattern.trim().split(/\s+/u).filter(Boolean);
20834
+ return words.length ? words.map(globWord) : null;
20835
+ }
20836
+ 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]));
20837
+ var readJson = (file) => {
20838
+ try {
20839
+ return JSON.parse(fs17.readFileSync(file, "utf8"));
20840
+ } catch {
20841
+ return;
20842
+ }
20843
+ };
20844
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
20845
+ var stringList = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
20846
+ function machinePatterns(env) {
20847
+ const value = readJson(heavyCommandsFile(env));
20848
+ return isRecord4(value) ? stringList(value.patterns) : [];
20849
+ }
20850
+ function repoPatterns(cwd) {
20851
+ return repoHeavyCommands(gitTop(cwd) ?? path22.resolve(cwd));
20852
+ }
20853
+ function heavyLines(lines, options) {
20854
+ const found = [];
20855
+ let patterns = null;
20856
+ for (const line of lines) {
20857
+ const reason = builtIn(line);
20858
+ if (reason) {
20859
+ found.push({ line, source: "catalog", reason });
20860
+ continue;
20861
+ }
20862
+ patterns ??= (options.patterns ?? [
20863
+ ...machinePatterns(options.env ?? process.env).map((pattern) => ({
20864
+ pattern,
20865
+ from: "machine"
20866
+ })),
20867
+ ...repoPatterns(options.cwd).map((pattern) => ({ pattern, from: "repo" }))
20868
+ ]).flatMap((entry) => {
20869
+ const matchers = parsePattern(entry.pattern);
20870
+ return matchers ? [{ ...entry, matchers }] : [];
20871
+ });
20872
+ const hit = patterns.find((entry) => matches(entry.matchers, line));
20873
+ if (hit)
20874
+ found.push({
20875
+ line,
20876
+ source: { pattern: hit.pattern, from: hit.from },
20877
+ reason: `${hit.from} pattern "${hit.pattern}"`
20878
+ });
20879
+ }
20880
+ return found;
20881
+ }
20882
+ var linesOf = (command, dialect) => {
20883
+ try {
20884
+ return commandLines(command, dialect);
20885
+ } catch {
20886
+ return null;
20887
+ }
20888
+ };
20889
+ var quoteWord = (word) => /^[\w@%+=:,./-]+$/u.test(word) ? word : `'${word.replaceAll("'", `'\\''`)}'`;
20890
+ var leaseCommand = (words) => `workit check --heavy -- ${words.map(quoteWord).join(" ")}`;
20891
+ function heavyPlan(command, options) {
20892
+ const dialect = options.dialect ?? "posix";
20893
+ if (dialect !== "posix" || !gitTop(options.cwd))
20894
+ return { kind: "none" };
20895
+ const lines = linesOf(command, dialect);
20896
+ if (!lines)
20897
+ return { kind: "none" };
20898
+ const heavy = heavyLines(lines, options);
20899
+ if (!heavy.length)
20900
+ return { kind: "none" };
20901
+ const simple = simpleCommand(command, dialect);
20902
+ const single = simple && !WORKIT.test(baseName2(simple[0])) && heavyLines([simple], options)[0];
20903
+ const judged = single ? [{ words: lines.flat(), text: command }] : heavy.map((match) => ({
20904
+ words: match.line,
20905
+ text: match.line.join(" ")
20906
+ }));
20907
+ if (options.rules !== null)
20908
+ for (const { words, text } of judged) {
20909
+ const verdict = wrapAllowed(options.rules, words, text, { bypass: options.bypass });
20910
+ if (!verdict.ok)
20911
+ return {
20912
+ kind: "passthrough",
20913
+ 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.`
20914
+ };
20915
+ }
20916
+ if (single) {
20917
+ const rewrite = leaseRewrite(command);
20918
+ return rewrite ? { kind: "rewrite", command: rewrite, reason: single.reason } : { kind: "none" };
20919
+ }
20920
+ const parts = heavy.map((match) => leaseCommand(match.line));
20921
+ return {
20922
+ kind: "split",
20923
+ 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.`,
20924
+ unblock: parts[0] ?? null
20925
+ };
20926
+ }
20927
+ function workitOnPath(env = process.env) {
20928
+ const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
20929
+ for (const dir of (env.PATH ?? "").split(path22.delimiter))
20930
+ for (const name of names) {
20931
+ if (!dir)
20932
+ continue;
20933
+ try {
20934
+ fs17.accessSync(path22.join(dir, name), fs17.constants.X_OK);
20935
+ return true;
20936
+ } catch {}
20937
+ }
20938
+ return false;
20939
+ }
20940
+ var ASSIGNMENTS = /^\s*((?:[A-Za-z_]\w*=(?:'[^']*'|"(?:[^"\\]|\\.)*"|[^\s'"])*\s+)*)/u;
20941
+ function leaseRewrite(command) {
20942
+ const text = command.trim();
20943
+ const prefix = ASSIGNMENTS.exec(text)?.[1] ?? "";
20944
+ const rest = text.slice(prefix.length).trim();
20945
+ if (!rest)
20946
+ return null;
20947
+ return `${prefix}workit check --heavy -- ${rest}`;
20948
+ }
20949
+
20950
+ // packages/workit-core/src/hooks/heavy-gate.ts
20951
+ var rulesFor = (host, cwd, env) => {
20952
+ switch (host) {
20953
+ case "claude_code":
20954
+ return claudeRules(cwd, env);
20955
+ case "opencode":
20956
+ return opencodeRules(cwd, env);
20957
+ case "codex_cli":
20958
+ case "codex_desktop":
20959
+ return codexRules(cwd, env);
20960
+ case "cursor":
20961
+ return cursorRules(cwd, env);
20962
+ case "pi":
20963
+ return null;
20964
+ }
20965
+ };
20966
+ function heavyGate(cwd, command, options) {
20967
+ const env = options.env ?? process.env;
20968
+ try {
20969
+ if (env.WORKIT_HEAVY_GATE === "0" || !maybeHeavy(command, cwd, env) || !workitOnPath(env))
20970
+ return { kind: "none" };
20971
+ return heavyPlan(command, {
20972
+ cwd,
20973
+ env,
20974
+ dialect: options.dialect ?? "posix",
20975
+ rules: rulesFor(options.host, cwd, env),
20976
+ bypass: options.permissionMode === "bypassPermissions"
20977
+ });
20978
+ } catch {
20979
+ return { kind: "none" };
20980
+ }
20981
+ }
20982
+ 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.`;
20983
+ var heavyDenial = (original, plan) => ({
20984
+ kind: "deny",
20985
+ 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}`,
20986
+ unblock: plan.command
20987
+ });
20988
+ var splitDenial = (plan) => ({
20989
+ kind: "deny",
20990
+ reason: plan.reason,
20991
+ unblock: plan.unblock
20992
+ });
20321
20993
  // packages/workit-core/src/hooks/write-gate.ts
20322
20994
  import { realpathSync as realpathSync5 } from "node:fs";
20323
- import os7 from "node:os";
20324
- import path23 from "node:path";
20995
+ import os9 from "node:os";
20996
+ import path24 from "node:path";
20325
20997
 
20326
20998
  // packages/workit-core/src/core/live-session.ts
20327
- import { spawnSync as spawnSync13 } from "node:child_process";
20999
+ import { spawnSync as spawnSync12 } from "node:child_process";
20328
21000
  import { createHash as createHash15 } from "node:crypto";
20329
- import fs17 from "node:fs";
20330
- import os6 from "node:os";
20331
- import path22 from "node:path";
21001
+ import fs18 from "node:fs";
21002
+ import os8 from "node:os";
21003
+ import path23 from "node:path";
20332
21004
  var LIVE_SESSION_TTL_MS = 15 * 60000;
20333
21005
  var HEARTBEAT_EVERY_MS = 30000;
20334
21006
  var liveProbe = {
20335
21007
  platform: process.platform,
20336
- run: (args) => spawnSync13("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
21008
+ run: (args) => spawnSync12("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
20337
21009
  start: (pid) => processStartOf(pid)
20338
21010
  };
20339
21011
  var leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session;
@@ -20349,9 +21021,9 @@ var processAlive = (pid) => {
20349
21021
  var processInfo = (pid) => {
20350
21022
  try {
20351
21023
  if (liveProbe.platform === "linux") {
20352
- const stat = fs17.readFileSync(`/proc/${pid}/stat`, "utf8");
21024
+ const stat = fs18.readFileSync(`/proc/${pid}/stat`, "utf8");
20353
21025
  const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
20354
- const command = fs17.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
21026
+ const command = fs18.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
20355
21027
  return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
20356
21028
  }
20357
21029
  if (liveProbe.platform === "win32")
@@ -20402,35 +21074,35 @@ var liveOwner = (input, transport, env = process.env) => {
20402
21074
  };
20403
21075
  };
20404
21076
  var checkoutOf = (target) => {
20405
- let dir = path22.resolve(target);
20406
- while (!fs17.existsSync(dir) && path22.dirname(dir) !== dir)
20407
- dir = path22.dirname(dir);
20408
- if (fs17.existsSync(dir) && !fs17.statSync(dir).isDirectory())
20409
- dir = path22.dirname(dir);
20410
- const parts = dir.split(path22.sep);
21077
+ let dir = path23.resolve(target);
21078
+ while (!fs18.existsSync(dir) && path23.dirname(dir) !== dir)
21079
+ dir = path23.dirname(dir);
21080
+ if (fs18.existsSync(dir) && !fs18.statSync(dir).isDirectory())
21081
+ dir = path23.dirname(dir);
21082
+ const parts = dir.split(path23.sep);
20411
21083
  const dotGit = parts.lastIndexOf(".git");
20412
21084
  if (dotGit > 0)
20413
- dir = parts.slice(0, dotGit).join(path22.sep) || path22.sep;
21085
+ dir = parts.slice(0, dotGit).join(path23.sep) || path23.sep;
20414
21086
  const location = resolveStore(dir);
20415
21087
  if (location instanceof Error || !location.shared || !location.top)
20416
21088
  return null;
20417
21089
  let top = location.top;
20418
21090
  try {
20419
- top = fs17.realpathSync.native(top);
21091
+ top = fs18.realpathSync.native(top);
20420
21092
  } catch {}
20421
21093
  return top;
20422
21094
  };
20423
- var markerFile2 = (top) => path22.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
21095
+ var markerFile2 = (top) => path23.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
20424
21096
  var checkoutKey = (top, platform = process.platform) => {
20425
- let key = path22.resolve(top);
21097
+ let key = path23.resolve(top);
20426
21098
  try {
20427
- key = fs17.realpathSync.native(key);
21099
+ key = fs18.realpathSync.native(key);
20428
21100
  } catch {}
20429
21101
  return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
20430
21102
  };
20431
21103
  var readMarker = (file) => {
20432
21104
  try {
20433
- const value = JSON.parse(fs17.readFileSync(file, "utf8"));
21105
+ const value = JSON.parse(fs18.readFileSync(file, "utf8"));
20434
21106
  if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
20435
21107
  return null;
20436
21108
  return {
@@ -20453,7 +21125,7 @@ var isLive = (marker, deps) => {
20453
21125
  const age = ageOf2(marker, deps.now ?? new Date);
20454
21126
  if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
20455
21127
  return false;
20456
- if (marker.machine !== (deps.machine ?? os6.hostname()) || marker.pid === null)
21128
+ if (marker.machine !== (deps.machine ?? os8.hostname()) || marker.pid === null)
20457
21129
  return true;
20458
21130
  if (!(deps.alive ?? processAlive)(marker.pid))
20459
21131
  return false;
@@ -20482,7 +21154,7 @@ var liveConflict = (target, owner, deps = {}) => {
20482
21154
  const marker = readMarker(file);
20483
21155
  if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
20484
21156
  return null;
20485
- if (sameOwner(marker, owner, deps.machine ?? os6.hostname()))
21157
+ if (sameOwner(marker, owner, deps.machine ?? os8.hostname()))
20486
21158
  return null;
20487
21159
  return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
20488
21160
  } catch {
@@ -20496,7 +21168,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
20496
21168
  return;
20497
21169
  const file = markerFile2(checkout);
20498
21170
  const now = deps.now ?? new Date;
20499
- const machine = deps.machine ?? os6.hostname();
21171
+ const machine = deps.machine ?? os8.hostname();
20500
21172
  const current = readMarker(file);
20501
21173
  if (current) {
20502
21174
  const age = ageOf2(current, now);
@@ -20517,10 +21189,10 @@ var claimLiveSession = (target, owner, deps = {}) => {
20517
21189
  checkout,
20518
21190
  heartbeatAt: now.toISOString()
20519
21191
  };
20520
- fs17.mkdirSync(path22.dirname(file), { recursive: true });
21192
+ fs18.mkdirSync(path23.dirname(file), { recursive: true });
20521
21193
  const temp = `${file}.${process.pid}.tmp`;
20522
- fs17.writeFileSync(temp, JSON.stringify(marker));
20523
- fs17.renameSync(temp, file);
21194
+ fs18.writeFileSync(temp, JSON.stringify(marker));
21195
+ fs18.renameSync(temp, file);
20524
21196
  } catch {}
20525
21197
  };
20526
21198
 
@@ -20529,27 +21201,42 @@ var NONE3 = { kind: "none" };
20529
21201
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
20530
21202
  var canonical2 = (file) => {
20531
21203
  let rest = "";
20532
- for (let dir = path23.resolve(file);; dir = path23.dirname(dir)) {
21204
+ for (let dir = path24.resolve(file);; dir = path24.dirname(dir)) {
20533
21205
  try {
20534
- return path23.join(realpathSync5(dir), rest);
21206
+ return path24.join(realpathSync5(dir), rest);
20535
21207
  } catch {
20536
- if (path23.dirname(dir) === dir)
20537
- return path23.resolve(file);
20538
- rest = rest ? path23.join(path23.basename(dir), rest) : path23.basename(dir);
21208
+ if (path24.dirname(dir) === dir)
21209
+ return path24.resolve(file);
21210
+ rest = rest ? path24.join(path24.basename(dir), rest) : path24.basename(dir);
20539
21211
  }
20540
21212
  }
20541
21213
  };
20542
21214
  var exempt = (cwd, root, file, plans) => {
20543
21215
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
20544
- const relative = path23.relative(fold(canonical2(root)), fold(canonical2(path23.resolve(cwd, file))));
20545
- if (relative.startsWith("..") || path23.isAbsolute(relative))
21216
+ const relative = path24.relative(fold(canonical2(root)), fold(canonical2(path24.resolve(cwd, file))));
21217
+ if (relative.startsWith("..") || path24.isAbsolute(relative))
20546
21218
  return true;
20547
- const posix = relative.split(path23.sep).join("/");
21219
+ const posix = relative.split(path24.sep).join("/");
20548
21220
  const segments = posix.split("/");
20549
21221
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
20550
21222
  };
20551
21223
  var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
20552
21224
  var operands = (words) => words.filter((item) => !item.startsWith("-"));
21225
+ var SCRIPT_SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
21226
+ var wrappedScript = (input) => {
21227
+ let words = input;
21228
+ while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
21229
+ words = words.slice(1);
21230
+ const tail = workitCheckTail(words);
21231
+ if (!tail)
21232
+ return null;
21233
+ if ("script" in tail)
21234
+ return tail.script;
21235
+ const [shell, flag, script] = tail.argv;
21236
+ if (SCRIPT_SHELLS.has((shell.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && /^-[a-z]*c$/.test(flag ?? ""))
21237
+ return script ?? null;
21238
+ return tail.argv.map(quoteWord2).join(" ");
21239
+ };
20553
21240
  var editsOf = (input) => {
20554
21241
  let words = input;
20555
21242
  while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
@@ -20626,10 +21313,18 @@ var editsOf = (input) => {
20626
21313
  return [];
20627
21314
  }
20628
21315
  };
20629
- var shellWrites = (command) => {
21316
+ var shellWrites = (command, depth = 0) => {
20630
21317
  const targets = [];
20631
21318
  let unknown = false;
20632
21319
  for (const segment of segmentsOf(command)) {
21320
+ const inner = depth < 4 ? wrappedScript(segment.words) : null;
21321
+ if (inner !== null) {
21322
+ const nested = shellWrites(inner, depth + 1);
21323
+ if (nested.writes && nested.targets === null)
21324
+ unknown = true;
21325
+ else if (nested.targets)
21326
+ targets.push(...nested.targets);
21327
+ }
20633
21328
  targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
20634
21329
  const edits = editsOf(segment.words);
20635
21330
  if (edits === null)
@@ -20642,7 +21337,7 @@ var shellWrites = (command) => {
20642
21337
  return { writes: true, targets: unknown ? null : targets };
20643
21338
  };
20644
21339
  var cache2 = new Map;
20645
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path23.resolve(root, file)) ?? "-").join("\x00");
21340
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path24.resolve(root, file)) ?? "-").join("\x00");
20646
21341
  var gateFor = (cwd) => {
20647
21342
  const store = new TaskStore(cwd);
20648
21343
  const listed = store.listTaskIndex();
@@ -20721,7 +21416,7 @@ var gitMutationDirs = (cwd, command, dialect) => {
20721
21416
  return [];
20722
21417
  if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
20723
21418
  return [];
20724
- return [path23.resolve(cwd, invocation.dir ?? ".")];
21419
+ return [path24.resolve(cwd, invocation.dir ?? ".")];
20725
21420
  });
20726
21421
  };
20727
21422
  var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
@@ -20731,15 +21426,15 @@ var cdInto = (dir, words) => {
20731
21426
  const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
20732
21427
  if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
20733
21428
  return;
20734
- return target === undefined ? os7.homedir() : path23.resolve(dir, target);
21429
+ return target === undefined ? os9.homedir() : path24.resolve(dir, target);
20735
21430
  };
20736
- var quoteWord = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
20737
- var segmentTargets = (cwd, command, dialect) => {
21431
+ var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
21432
+ var segmentTargets = (cwd, command, dialect, depth = 0) => {
20738
21433
  const targets = [];
20739
21434
  let dir = cwd;
20740
21435
  let stack = [];
20741
21436
  const groups = [];
20742
- const at = (file) => path23.isAbsolute(file) ? file : dir === undefined ? null : path23.resolve(dir, file);
21437
+ const at = (file) => path24.isAbsolute(file) ? file : dir === undefined ? null : path24.resolve(dir, file);
20743
21438
  for (const segment of segmentsOf(command, dialect)) {
20744
21439
  if (segment.group === "open") {
20745
21440
  groups.push({ dir, stack: [...stack] });
@@ -20767,6 +21462,9 @@ var segmentTargets = (cwd, command, dialect) => {
20767
21462
  dir = stack.length ? stack.pop() : undefined;
20768
21463
  continue;
20769
21464
  }
21465
+ const inner = depth < 4 ? wrappedScript(words) : null;
21466
+ if (inner !== null && dir !== undefined)
21467
+ targets.push(...segmentTargets(dir, inner, "posix", depth + 1));
20770
21468
  for (const file of segment.redirects)
20771
21469
  if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
20772
21470
  const resolved = at(file);
@@ -20776,15 +21474,15 @@ var segmentTargets = (cwd, command, dialect) => {
20776
21474
  const edits = editsOf(segment.words);
20777
21475
  if (edits === null) {
20778
21476
  if (dir !== undefined)
20779
- targets.push(path23.join(dir, DIR_TARGET));
21477
+ targets.push(path24.join(dir, DIR_TARGET));
20780
21478
  } else
20781
21479
  for (const file of edits) {
20782
21480
  const resolved = at(file);
20783
21481
  if (resolved)
20784
21482
  targets.push(resolved);
20785
21483
  }
20786
- 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), "")))
20787
- targets.push(path23.join(dir, DIR_TARGET));
21484
+ 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), "")))
21485
+ targets.push(path24.join(dir, DIR_TARGET));
20788
21486
  }
20789
21487
  return targets;
20790
21488
  };
@@ -20819,7 +21517,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
20819
21517
  var guarded = (targets) => {
20820
21518
  const seen = new Set;
20821
21519
  return targets.filter((target) => {
20822
- const dir = path23.dirname(target);
21520
+ const dir = path24.dirname(target);
20823
21521
  if (seen.has(dir))
20824
21522
  return false;
20825
21523
  seen.add(dir);
@@ -20840,7 +21538,7 @@ var claimAll = (owner, targets, deps) => {
20840
21538
  for (const target of guarded(targets))
20841
21539
  claimLiveSession(target, owner, deps);
20842
21540
  };
20843
- var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path23.resolve(cwd, file)) : [path23.join(cwd, DIR_TARGET)];
21541
+ var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path24.resolve(cwd, file)) : [path24.join(cwd, DIR_TARGET)];
20844
21542
  function writeGate(cwd, paths, owner, deps) {
20845
21543
  if (!owner)
20846
21544
  return beforeWriteGate(cwd, paths);
@@ -20860,7 +21558,7 @@ function shellGate(cwd, command, owner, dialect = "posix", deps) {
20860
21558
  try {
20861
21559
  targets = [
20862
21560
  ...segmentTargets(cwd, command, dialect),
20863
- ...gitMutationDirs(cwd, command, dialect).map((dir) => path23.join(dir, DIR_TARGET))
21561
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path24.join(dir, DIR_TARGET))
20864
21562
  ];
20865
21563
  } catch {
20866
21564
  targets = [];
@@ -20909,7 +21607,7 @@ function beforeWriteGate(cwd, paths) {
20909
21607
  }
20910
21608
  }
20911
21609
  // packages/workit-core/src/hooks/stop.ts
20912
- import { spawnSync as spawnSync14 } from "node:child_process";
21610
+ import { spawnSync as spawnSync13 } from "node:child_process";
20913
21611
  var NONE4 = { kind: "none" };
20914
21612
  var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
20915
21613
  var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
@@ -20930,7 +21628,7 @@ var asksUser = (message) => {
20930
21628
  };
20931
21629
  var short = (sha) => (sha ?? "?").slice(0, 12);
20932
21630
  var gitList = (cwd, args) => {
20933
- const run = spawnSync14("git", args, {
21631
+ const run = spawnSync13("git", args, {
20934
21632
  cwd,
20935
21633
  encoding: "utf8",
20936
21634
  timeout: 3000,
@@ -21294,12 +21992,12 @@ ${line}` };
21294
21992
  };
21295
21993
  // packages/workit-core/src/hooks/hosts/fields.ts
21296
21994
  import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
21297
- import path24 from "node:path";
21298
- var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
21995
+ import path25 from "node:path";
21996
+ var isRecord5 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
21299
21997
  var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
21300
21998
  var optionalText = (value) => typeof value === "string" ? value : null;
21301
21999
  var existingDirectory = (value) => {
21302
- if (!nonEmpty2(value) || !path24.isAbsolute(value) || !existsSync7(value))
22000
+ if (!nonEmpty2(value) || !path25.isAbsolute(value) || !existsSync7(value))
21303
22001
  return null;
21304
22002
  try {
21305
22003
  return statSync2(value).isDirectory() ? realpathSync6(value) : null;
@@ -21352,7 +22050,7 @@ var isWriteTool = (name) => typeof name === "string" && WRITE_TOOLS.has(name.toL
21352
22050
  var PATH_KEYS = ["file_path", "filePath", "notebook_path", "path", "target_file", "file"];
21353
22051
  var PATCH_HEADER = /^[ \t]*\*\*\* (?:(?:Add|Update|Delete) File|Move to): (.+)$/gm;
21354
22052
  var writePaths = (input) => {
21355
- if (!isRecord4(input))
22053
+ if (!isRecord5(input))
21356
22054
  return [];
21357
22055
  const paths = PATH_KEYS.map((key) => input[key]).filter(nonEmpty2);
21358
22056
  for (const value of Object.values(input))
@@ -21447,10 +22145,32 @@ function handleHook(input, deps) {
21447
22145
  const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE5;
21448
22146
  if (gate.kind === "deny")
21449
22147
  return gate;
22148
+ const plan = heavyGate(input.cwd, event.command, {
22149
+ host: input.host,
22150
+ dialect: event.dialect,
22151
+ permissionMode: input.permissionMode
22152
+ });
22153
+ if (plan.kind === "split" && canDeny)
22154
+ return splitDenial(plan);
22155
+ const rewrites = usable2(descriptor.shellPolicy.rewrite);
22156
+ if (plan.kind === "rewrite" && !rewrites && canDeny)
22157
+ return heavyDenial(event.command, plan);
22158
+ const lease = plan.kind === "rewrite" && rewrites ? plan : null;
22159
+ const aside = plan.kind === "passthrough" ? plan.note : null;
21450
22160
  noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
22161
+ const rewritten = (text) => lease ? {
22162
+ kind: "rewrite",
22163
+ command: lease.command,
22164
+ input: event.toolInput ? { ...event.toolInput, command: lease.command } : null,
22165
+ text
22166
+ } : NONE5;
21451
22167
  if (descriptor.transport !== "hook-process")
21452
- return NONE5;
21453
- return withContextLine(canDeny ? rawGitPre(input, event.command) : NONE5, shipNudge(input, event.command));
22168
+ return rewritten(null);
22169
+ const nudged = withContextLine(withContextLine(canDeny ? rawGitPre(input, event.command) : NONE5, aside), shipNudge(input, event.command));
22170
+ if (!lease)
22171
+ return nudged;
22172
+ return rewritten([rewriteNote(lease), nudged.kind === "context" ? nudged.text : null].filter(Boolean).join(`
22173
+ `));
21454
22174
  }
21455
22175
  case "shell.post":
21456
22176
  if (usable2(descriptor.events["shell.post"].support))
@@ -21528,7 +22248,12 @@ var CLAUDE_CODE_DESCRIPTOR = {
21528
22248
  "compact.pre": { support: "none", native: null },
21529
22249
  stop: { support: "native", native: "Stop" }
21530
22250
  },
21531
- shellPolicy: { deny: "native", channel: "permissionDecision", failClosed: false },
22251
+ shellPolicy: {
22252
+ deny: "native",
22253
+ channel: "permissionDecision",
22254
+ failClosed: false,
22255
+ rewrite: "native"
22256
+ },
21532
22257
  context: {
21533
22258
  sessionStart: "native",
21534
22259
  perTurn: "native",
@@ -21610,7 +22335,7 @@ var EVENTS = {
21610
22335
  var SOURCES = new Set(["startup", "resume", "clear", "compact", "fork"]);
21611
22336
  var SHELL_TOOLS = new Set(["Bash", "PowerShell"]);
21612
22337
  var dialectOf2 = (value) => value.tool_name === "PowerShell" ? "powershell" : "posix";
21613
- var toolCommand = (value) => isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null;
22338
+ var toolCommand = (value) => isRecord5(value.tool_input) ? commandText(value.tool_input.command) : null;
21614
22339
  var eventOf = (name, value) => {
21615
22340
  const toolUseId = optionalText(value.tool_use_id);
21616
22341
  switch (name) {
@@ -21638,19 +22363,28 @@ var eventOf = (name, value) => {
21638
22363
  kind: "tool.pre",
21639
22364
  tool: value.tool_name,
21640
22365
  toolUseId,
21641
- skill: isRecord4(value.tool_input) ? optionalText(value.tool_input.skill) : null
22366
+ skill: isRecord5(value.tool_input) ? optionalText(value.tool_input.skill) : null
21642
22367
  }
21643
22368
  };
21644
22369
  if (!SHELL_TOOLS.has(value.tool_name))
21645
22370
  return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
21646
22371
  const command = toolCommand(value);
21647
- 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}` };
22372
+ return command ? {
22373
+ ok: true,
22374
+ event: {
22375
+ kind: "shell.pre",
22376
+ command,
22377
+ toolUseId,
22378
+ dialect: dialectOf2(value),
22379
+ toolInput: value.tool_input
22380
+ }
22381
+ } : { ok: false, error: `tool_input.command is required for ${value.tool_name}` };
21648
22382
  }
21649
22383
  case "PostToolUse": {
21650
22384
  const command = toolCommand(value);
21651
22385
  if (!SHELL_TOOLS.has(String(value.tool_name)) || !command)
21652
22386
  return { ok: false, error: "only shell PostToolUse is mapped" };
21653
- const response = isRecord4(value.tool_response) ? value.tool_response : {};
22387
+ const response = isRecord5(value.tool_response) ? value.tool_response : {};
21654
22388
  return {
21655
22389
  ok: true,
21656
22390
  event: {
@@ -21708,6 +22442,17 @@ var CONTEXT_EVENTS = new Set([
21708
22442
  "PostToolUse"
21709
22443
  ]);
21710
22444
  var render = (decision, native) => {
22445
+ if (decision.kind === "rewrite" && native === "PreToolUse" && decision.input)
22446
+ return {
22447
+ json: {
22448
+ hookSpecificOutput: {
22449
+ hookEventName: "PreToolUse",
22450
+ updatedInput: decision.input,
22451
+ ...decision.text ? { additionalContext: decision.text } : {}
22452
+ }
22453
+ },
22454
+ exitCode: 0
22455
+ };
21711
22456
  if (decision.kind === "deny" && native === "PreToolUse")
21712
22457
  return {
21713
22458
  json: {
@@ -21733,14 +22478,14 @@ var render = (decision, native) => {
21733
22478
  var claudeCodeAdapter = {
21734
22479
  descriptor: CLAUDE_CODE_DESCRIPTOR,
21735
22480
  parse(raw) {
21736
- const native = isRecord4(raw) && Object.hasOwn(EVENTS, String(raw.hook_event_name)) ? raw.hook_event_name : null;
22481
+ const native = isRecord5(raw) && Object.hasOwn(EVENTS, String(raw.hook_event_name)) ? raw.hook_event_name : null;
21737
22482
  const fail = (error) => ({
21738
22483
  ok: false,
21739
22484
  error,
21740
22485
  native,
21741
22486
  event: native ? EVENTS[native] : null
21742
22487
  });
21743
- if (!isRecord4(raw) || !native)
22488
+ if (!isRecord5(raw) || !native)
21744
22489
  return fail("hook_event_name is required");
21745
22490
  if (!nonEmpty2(raw.session_id))
21746
22491
  return fail("session_id is required");
@@ -21794,7 +22539,12 @@ var CODEX_DESCRIPTOR = {
21794
22539
  "compact.pre": { support: "none", native: null },
21795
22540
  stop: { support: "native", native: "Stop" }
21796
22541
  },
21797
- shellPolicy: { deny: "native", channel: "permissionDecision", failClosed: false },
22542
+ shellPolicy: {
22543
+ deny: "native",
22544
+ channel: "permissionDecision",
22545
+ failClosed: false,
22546
+ rewrite: "none"
22547
+ },
21798
22548
  context: {
21799
22549
  sessionStart: "native",
21800
22550
  perTurn: "native",
@@ -21886,7 +22636,7 @@ var SHELL_TOOLS2 = new Set(["bash", "unified-exec"]);
21886
22636
  var isShellTool = (name) => SHELL_TOOLS2.has(String(name).toLowerCase());
21887
22637
  var SOURCES2 = new Set(["startup", "resume", "clear", "compact", "fork"]);
21888
22638
  var parseCodexHookInput = (value) => {
21889
- if (!isRecord4(value) || !Object.hasOwn(EVENTS2, String(value.hook_event_name)))
22639
+ if (!isRecord5(value) || !Object.hasOwn(EVENTS2, String(value.hook_event_name)))
21890
22640
  return { ok: false, error: "hook_event_name is required" };
21891
22641
  const event = value.hook_event_name;
21892
22642
  const cwd = existingDirectory(value.cwd);
@@ -21895,7 +22645,7 @@ var parseCodexHookInput = (value) => {
21895
22645
  const toolEvent = event === "PreToolUse" || event === "PostToolUse";
21896
22646
  if (toolEvent && !nonEmpty2(value.tool_name))
21897
22647
  return { ok: false, error: "tool_name is required" };
21898
- const command = isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null;
22648
+ const command = isRecord5(value.tool_input) ? commandText(value.tool_input.command) : null;
21899
22649
  if (toolEvent && isShellTool(value.tool_name) && !command)
21900
22650
  return { ok: false, error: "tool_input.command is required for shell tools" };
21901
22651
  if (event === "SubagentStart" && !nonEmpty2(value.agent_id))
@@ -22009,7 +22759,7 @@ var codexAdapter = {
22009
22759
  const host = detectCodexSurface(env);
22010
22760
  const parsed = parseCodexHookInput(raw);
22011
22761
  if (!parsed.ok) {
22012
- const native = isRecord4(raw) && Object.hasOwn(EVENTS2, String(raw.hook_event_name)) ? raw.hook_event_name : null;
22762
+ const native = isRecord5(raw) && Object.hasOwn(EVENTS2, String(raw.hook_event_name)) ? raw.hook_event_name : null;
22013
22763
  return { ok: false, error: parsed.error, native, event: native ? EVENTS2[native] : null };
22014
22764
  }
22015
22765
  const input = parsed.data;
@@ -22036,8 +22786,8 @@ var codexAdapter = {
22036
22786
  };
22037
22787
 
22038
22788
  // packages/workit-core/src/hooks/hosts/cursor.ts
22039
- import fs18 from "node:fs";
22040
- import path25 from "node:path";
22789
+ import fs19 from "node:fs";
22790
+ import path26 from "node:path";
22041
22791
  var undocumented = { support: "undocumented", native: null };
22042
22792
  var none = { support: "none", native: null };
22043
22793
  var CURSOR_DESCRIPTOR = {
@@ -22059,7 +22809,7 @@ var CURSOR_DESCRIPTOR = {
22059
22809
  "compact.pre": { support: "partial", native: "preCompact" },
22060
22810
  stop: { support: "native", native: "stop" }
22061
22811
  },
22062
- shellPolicy: { deny: "native", channel: "exit2+json", failClosed: false },
22812
+ shellPolicy: { deny: "native", channel: "exit2+json", failClosed: false, rewrite: "none" },
22063
22813
  context: {
22064
22814
  sessionStart: "native",
22065
22815
  perTurn: "none",
@@ -22152,7 +22902,7 @@ var EVENTS3 = {
22152
22902
  stop: "stop"
22153
22903
  };
22154
22904
  var parseCursorHookInput = (value) => {
22155
- if (!isRecord4(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
22905
+ if (!isRecord5(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
22156
22906
  return { ok: false, error: "hook_event_name is required" };
22157
22907
  const roots = value.workspace_roots;
22158
22908
  if (!Array.isArray(roots) || roots.length === 0 || !roots.every(nonEmpty2))
@@ -22234,17 +22984,17 @@ var protocolEvent2 = (input) => {
22234
22984
  };
22235
22985
  var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
22236
22986
  var lastAssistantText = (file) => {
22237
- if (!file || !path25.isAbsolute(file))
22987
+ if (!file || !path26.isAbsolute(file))
22238
22988
  return null;
22239
22989
  try {
22240
- const size = fs18.statSync(file).size;
22990
+ const size = fs19.statSync(file).size;
22241
22991
  const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
22242
22992
  const buffer = Buffer.alloc(size - start);
22243
- const fd = fs18.openSync(file, "r");
22993
+ const fd = fs19.openSync(file, "r");
22244
22994
  try {
22245
- fs18.readSync(fd, buffer, 0, buffer.length, start);
22995
+ fs19.readSync(fd, buffer, 0, buffer.length, start);
22246
22996
  } finally {
22247
- fs18.closeSync(fd);
22997
+ fs19.closeSync(fd);
22248
22998
  }
22249
22999
  const lines = buffer.toString("utf8").split(`
22250
23000
  `);
@@ -22255,14 +23005,14 @@ var lastAssistantText = (file) => {
22255
23005
  } catch {
22256
23006
  continue;
22257
23007
  }
22258
- if (!isRecord4(entry) || entry.role !== "assistant")
23008
+ if (!isRecord5(entry) || entry.role !== "assistant")
22259
23009
  continue;
22260
- const content = isRecord4(entry.message) ? entry.message.content : entry.content;
23010
+ const content = isRecord5(entry.message) ? entry.message.content : entry.content;
22261
23011
  if (typeof content === "string")
22262
23012
  return content;
22263
23013
  if (!Array.isArray(content))
22264
23014
  return null;
22265
- const text = content.filter((part) => isRecord4(part) && part.type === "text" && typeof part.text === "string").map((part) => part.text).join(`
23015
+ const text = content.filter((part) => isRecord5(part) && part.type === "text" && typeof part.text === "string").map((part) => part.text).join(`
22266
23016
  `);
22267
23017
  return text || null;
22268
23018
  }
@@ -22271,7 +23021,7 @@ var lastAssistantText = (file) => {
22271
23021
  return null;
22272
23022
  }
22273
23023
  };
22274
- var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path25.sep) ? root : `${root}${path25.sep}`);
23024
+ var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path26.sep) ? root : `${root}${path26.sep}`);
22275
23025
  var hookCwd = (input) => {
22276
23026
  const cwd = existingDirectory(input.cwd);
22277
23027
  if (cwd && input.hook_event_name === "beforeShellExecution")
@@ -22312,7 +23062,7 @@ var cursorAdapter = {
22312
23062
  parse(raw) {
22313
23063
  const parsed = parseCursorHookInput(raw);
22314
23064
  if (!parsed.ok) {
22315
- const native = isRecord4(raw) && Object.hasOwn(EVENTS3, String(raw.hook_event_name)) ? raw.hook_event_name : null;
23065
+ const native = isRecord5(raw) && Object.hasOwn(EVENTS3, String(raw.hook_event_name)) ? raw.hook_event_name : null;
22316
23066
  return { ok: false, error: parsed.error, native, event: native ? EVENTS3[native] : null };
22317
23067
  }
22318
23068
  const input = parsed.data;