@brainervirus/workit-opencode 8.13.0 → 8.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/plugin.js CHANGED
@@ -6,7 +6,7 @@ function define(plugin) {
6
6
  // packages/workit-core/src/core.ts
7
7
  import { spawnSync as spawnSync16 } from "node:child_process";
8
8
  import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
9
- import path22 from "node:path";
9
+ import path24 from "node:path";
10
10
 
11
11
  // packages/workit-core/src/core/task-contract.ts
12
12
  import { createHash, randomUUID } from "node:crypto";
@@ -8992,9 +8992,9 @@ function parseAdvertisedOperation(family, input, host) {
8992
8992
  });
8993
8993
  }
8994
8994
  // packages/workit-core/src/core/task-store.ts
8995
- import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
8996
- import * as fs7 from "node:fs";
8997
- import path6 from "node:path";
8995
+ import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
8996
+ import * as fs8 from "node:fs";
8997
+ import path8 from "node:path";
8998
8998
 
8999
8999
  // node_modules/@openclaw/fs-safe/dist/errors.js
9000
9000
  var OPERATIONAL_CODES = new Set([
@@ -9597,8 +9597,14 @@ function acquireFileLockSync(targetPath, options) {
9597
9597
  }
9598
9598
  }
9599
9599
  }
9600
+ // packages/workit-core/src/host-session.ts
9601
+ import { createHash as createHash3, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
9602
+ import fs5 from "node:fs";
9603
+ import path5 from "node:path";
9604
+
9600
9605
  // packages/workit-core/src/core/logger.ts
9601
9606
  import os from "node:os";
9607
+ import path3 from "node:path";
9602
9608
  var SENSITIVE_WORDS = new Set([
9603
9609
  "token",
9604
9610
  "secret",
@@ -9637,13 +9643,29 @@ var SENSITIVE_WORDS = new Set([
9637
9643
  ]);
9638
9644
  var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
9639
9645
  var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9646
+ var resolveStateDir = () => {
9647
+ const override = process.env.WORKFLOW_TOOLKIT_STATE;
9648
+ if (override)
9649
+ return override;
9650
+ const home = os.homedir();
9651
+ if (process.env.XDG_STATE_HOME)
9652
+ return path3.join(process.env.XDG_STATE_HOME, "workit");
9653
+ switch (os.platform()) {
9654
+ case "darwin":
9655
+ return path3.join(home, "Library", "Application Support", "workit");
9656
+ case "win32":
9657
+ return path3.join(process.env.LOCALAPPDATA ?? path3.join(home, "AppData", "Local"), "workit");
9658
+ default:
9659
+ return path3.join(process.env.HOME ?? home, ".local", "state", "workit");
9660
+ }
9661
+ };
9640
9662
  var homePattern = new RegExp(`${escapeRegExp(os.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
9641
9663
 
9642
9664
  // packages/workit-core/src/store/paths.ts
9643
9665
  import { spawnSync } from "node:child_process";
9644
9666
  import { createHash as createHash2 } from "node:crypto";
9645
9667
  import fs4 from "node:fs";
9646
- import path3 from "node:path";
9668
+ import path4 from "node:path";
9647
9669
  var NOT_A_REPO = /not a git repository \(or any (of the parent directories|parent up to)/u;
9648
9670
  var gitRun = (cwd, args) => {
9649
9671
  const run = spawnSync("git", args, {
@@ -9685,14 +9707,14 @@ var DISCOVERY_ENV = [
9685
9707
  ];
9686
9708
  var isGitDir = (dir) => {
9687
9709
  try {
9688
- return fs4.statSync(path3.join(dir, "HEAD")).isFile() && fs4.statSync(path3.join(dir, "objects")).isDirectory() && fs4.statSync(path3.join(dir, "refs")).isDirectory();
9710
+ return fs4.statSync(path4.join(dir, "HEAD")).isFile() && fs4.statSync(path4.join(dir, "objects")).isDirectory() && fs4.statSync(path4.join(dir, "refs")).isDirectory();
9689
9711
  } catch {
9690
9712
  return false;
9691
9713
  }
9692
9714
  };
9693
9715
  var reftableRefs = (common) => {
9694
9716
  try {
9695
- return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(path3.join(common, "config"), "utf8"));
9717
+ return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(path4.join(common, "config"), "utf8"));
9696
9718
  } catch {
9697
9719
  return false;
9698
9720
  }
@@ -9712,7 +9734,7 @@ function fastGit(root) {
9712
9734
  if (device !== null && stat.dev !== device)
9713
9735
  return { kind: "none" };
9714
9736
  device = stat.dev;
9715
- const dotGit = path3.join(dir, ".git");
9737
+ const dotGit = path4.join(dir, ".git");
9716
9738
  let entry = null;
9717
9739
  try {
9718
9740
  entry = fs4.statSync(dotGit);
@@ -9733,19 +9755,19 @@ function fastGit(root) {
9733
9755
  const match = /^gitdir: (.+)$/mu.exec(text);
9734
9756
  if (!match)
9735
9757
  return null;
9736
- gitDir = path3.resolve(dir, match[1].trim());
9758
+ gitDir = path4.resolve(dir, match[1].trim());
9737
9759
  } else
9738
9760
  return null;
9739
- if (!fs4.existsSync(path3.join(gitDir, "HEAD")))
9761
+ if (!fs4.existsSync(path4.join(gitDir, "HEAD")))
9740
9762
  return null;
9741
9763
  } else if (isGitDir(dir))
9742
9764
  gitDir = dir;
9743
9765
  if (gitDir) {
9744
9766
  let common = gitDir;
9745
9767
  try {
9746
- common = path3.resolve(gitDir, fs4.readFileSync(path3.join(gitDir, "commondir"), "utf8").trim());
9768
+ common = path4.resolve(gitDir, fs4.readFileSync(path4.join(gitDir, "commondir"), "utf8").trim());
9747
9769
  } catch {}
9748
- if (!fs4.existsSync(path3.join(common, "objects")))
9770
+ if (!fs4.existsSync(path4.join(common, "objects")))
9749
9771
  return null;
9750
9772
  return {
9751
9773
  kind: "repo",
@@ -9755,26 +9777,26 @@ function fastGit(root) {
9755
9777
  reftable: reftableRefs(common)
9756
9778
  };
9757
9779
  }
9758
- const parent = path3.dirname(dir);
9780
+ const parent = path4.dirname(dir);
9759
9781
  if (parent === dir)
9760
9782
  return { kind: "none" };
9761
9783
  dir = parent;
9762
9784
  }
9763
9785
  }
9764
9786
  function resolveStore(root) {
9765
- root = path3.resolve(root);
9787
+ root = path4.resolve(root);
9766
9788
  let directory = false;
9767
9789
  try {
9768
9790
  directory = fs4.statSync(root).isDirectory();
9769
9791
  } catch {}
9770
9792
  if (!directory)
9771
- return { dir: path3.join(root, ".workit"), shared: false, top: null };
9793
+ return { dir: path4.join(root, ".workit"), shared: false, top: null };
9772
9794
  const fast = fastGit(root);
9773
9795
  if (fast?.kind === "none")
9774
- return { dir: path3.join(root, ".workit"), shared: false, top: null };
9796
+ return { dir: path4.join(root, ".workit"), shared: false, top: null };
9775
9797
  if (fast?.kind === "repo")
9776
9798
  return {
9777
- dir: path3.join(fast.common, "workit"),
9799
+ dir: path4.join(fast.common, "workit"),
9778
9800
  shared: true,
9779
9801
  top: fast.top,
9780
9802
  git: { gitDir: fast.gitDir, top: fast.top, reftable: fast.reftable }
@@ -9784,20 +9806,20 @@ function resolveStore(root) {
9784
9806
  if (run.ok && common) {
9785
9807
  const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
9786
9808
  return {
9787
- dir: path3.join(real(path3.resolve(root, common)), "workit"),
9809
+ dir: path4.join(real(path4.resolve(root, common)), "workit"),
9788
9810
  shared: true,
9789
9811
  top: top.ok && top.stdout.trim() ? real(top.stdout.trim()) : null
9790
9812
  };
9791
9813
  }
9792
9814
  if (run.missing || !run.ok && NOT_A_REPO.test(run.stderr))
9793
- return { dir: path3.join(root, ".workit"), shared: false, top: null };
9815
+ return { dir: path4.join(root, ".workit"), shared: false, top: null };
9794
9816
  return new Error(`cannot locate the workit store: git rev-parse failed (${run.stderr.trim().split(`
9795
9817
  `)[0] || "no output"}); fix the repository (git status should work here)`);
9796
9818
  }
9797
9819
  var rebasingBranch = (gitDir) => {
9798
9820
  for (const state of ["rebase-merge", "rebase-apply"]) {
9799
9821
  try {
9800
- const ref = fs4.readFileSync(path3.join(gitDir, state, "head-name"), "utf8").trim();
9822
+ const ref = fs4.readFileSync(path4.join(gitDir, state, "head-name"), "utf8").trim();
9801
9823
  const match = /^refs\/heads\/(.+)$/u.exec(ref);
9802
9824
  if (match)
9803
9825
  return match[1];
@@ -9812,7 +9834,7 @@ function resolveTaskKey(root, location) {
9812
9834
  if (location.git && !location.git.reftable) {
9813
9835
  let head = "";
9814
9836
  try {
9815
- head = fs4.readFileSync(path3.join(location.git.gitDir, "HEAD"), "utf8").trim();
9837
+ head = fs4.readFileSync(path4.join(location.git.gitDir, "HEAD"), "utf8").trim();
9816
9838
  } catch {}
9817
9839
  const ref = /^ref: refs\/heads\/(.+)$/u.exec(head);
9818
9840
  if (ref && ref[1] !== ".invalid")
@@ -9843,7 +9865,7 @@ function resolveTaskKey(root, location) {
9843
9865
  return { key: `detached-${shortHash(real(worktree))}`, kind: "detached", branch: null };
9844
9866
  }
9845
9867
  function checkoutRootOf(root, location) {
9846
- const resolved = path3.resolve(root);
9868
+ const resolved = path4.resolve(root);
9847
9869
  let canonical = resolved;
9848
9870
  try {
9849
9871
  canonical = fs4.realpathSync(resolved);
@@ -9874,13 +9896,13 @@ function branchRenames(location) {
9874
9896
  }
9875
9897
  for (const entry of entries)
9876
9898
  if (entry.isDirectory())
9877
- walk(path3.join(dir, entry.name));
9899
+ walk(path4.join(dir, entry.name));
9878
9900
  else
9879
- files.push(path3.join(dir, entry.name));
9901
+ files.push(path4.join(dir, entry.name));
9880
9902
  };
9881
- walk(path3.join(path3.dirname(location.dir), "logs", "refs", "heads"));
9903
+ walk(path4.join(path4.dirname(location.dir), "logs", "refs", "heads"));
9882
9904
  if (location.git)
9883
- files.push(path3.join(location.git.gitDir, "logs", "HEAD"));
9905
+ files.push(path4.join(location.git.gitDir, "logs", "HEAD"));
9884
9906
  const seen = new Set;
9885
9907
  const renames = [];
9886
9908
  for (const file of files) {
@@ -9915,7 +9937,7 @@ var checkoutSlug = (root) => {
9915
9937
  let canonical = real(root);
9916
9938
  if (process.platform === "win32")
9917
9939
  canonical = canonical.toLowerCase();
9918
- const base = path3.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
9940
+ const base = path4.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
9919
9941
  return `${base}-${shortHash(canonical)}`;
9920
9942
  };
9921
9943
 
@@ -9947,6 +9969,89 @@ function hostSessionFromEnv(env) {
9947
9969
  }
9948
9970
  return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
9949
9971
  }
9972
+ var hash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 32);
9973
+ var userIssuedRoot = () => path5.join(resolveStateDir(), "issued");
9974
+ var userTokenRoot = () => path5.join(resolveStateDir(), "issued-tokens");
9975
+ function migrateRepoIssued(cwd) {
9976
+ if (cwd === undefined)
9977
+ return;
9978
+ let old;
9979
+ try {
9980
+ const location = resolveStore(cwd);
9981
+ if (location instanceof Error)
9982
+ return;
9983
+ old = path5.join(location.dir, "hooks", "issued");
9984
+ if (!fs5.existsSync(old))
9985
+ return;
9986
+ } catch {
9987
+ return;
9988
+ }
9989
+ for (const lead of safeList(old)) {
9990
+ try {
9991
+ const from = path5.join(old, lead);
9992
+ if (!fs5.statSync(from).isDirectory())
9993
+ continue;
9994
+ const to = path5.join(userIssuedRoot(), lead);
9995
+ fs5.mkdirSync(to, { recursive: true, mode: 448 });
9996
+ for (const id of fs5.readdirSync(from)) {
9997
+ try {
9998
+ fs5.copyFileSync(path5.join(from, id), path5.join(to, id), fs5.constants.COPYFILE_EXCL);
9999
+ } catch (error) {
10000
+ if (error.code !== "EEXIST")
10001
+ throw error;
10002
+ }
10003
+ fs5.rmSync(path5.join(from, id), { force: true });
10004
+ }
10005
+ removeIfEmpty(from);
10006
+ } catch {}
10007
+ }
10008
+ removeIfEmpty(old);
10009
+ }
10010
+ var safeList = (dir) => {
10011
+ try {
10012
+ return fs5.readdirSync(dir);
10013
+ } catch {
10014
+ return [];
10015
+ }
10016
+ };
10017
+ var removeIfEmpty = (dir) => {
10018
+ try {
10019
+ fs5.rmdirSync(dir);
10020
+ } catch {}
10021
+ };
10022
+ var issuedRecord = (cwd, claimed) => {
10023
+ const cut = claimed.lastIndexOf(":");
10024
+ if (cut <= 0)
10025
+ return null;
10026
+ migrateRepoIssued(cwd);
10027
+ try {
10028
+ return fs5.readFileSync(path5.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
10029
+ } catch {
10030
+ return null;
10031
+ }
10032
+ };
10033
+ var TOKEN_HASH = /^([0-9a-f]{64})$/mu;
10034
+ var issuedToken = (claimed) => {
10035
+ const cut = claimed.lastIndexOf(":");
10036
+ if (cut <= 0)
10037
+ return { stored: null, leadBound: false };
10038
+ const dir = path5.join(userTokenRoot(), hash(claimed.slice(0, cut)));
10039
+ try {
10040
+ const text = fs5.readFileSync(path5.join(dir, hash(claimed)), "utf8");
10041
+ return { stored: TOKEN_HASH.exec(text)?.[1] ?? null, leadBound: true };
10042
+ } catch {
10043
+ return { stored: null, leadBound: safeList(dir).length > 0 };
10044
+ }
10045
+ };
10046
+ function issuedBinding(cwd, claimed) {
10047
+ const record = issuedRecord(cwd, claimed);
10048
+ const { stored, leadBound } = issuedToken(claimed);
10049
+ if (stored)
10050
+ return "token";
10051
+ if (record === null)
10052
+ return null;
10053
+ return leadBound ? "token" : "legacy";
10054
+ }
9950
10055
  var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
9951
10056
  var SESSION_UNSAFE = /[^A-Za-z0-9_.:@/+-]/g;
9952
10057
  function childSessionId(lead, child) {
@@ -9957,26 +10062,26 @@ function childSessionId(lead, child) {
9957
10062
 
9958
10063
  // packages/workit-core/src/core/package-root.ts
9959
10064
  import { existsSync } from "node:fs";
9960
- import path4 from "node:path";
10065
+ import path6 from "node:path";
9961
10066
  import { fileURLToPath } from "node:url";
9962
10067
  var packageRoot = () => {
9963
- let dir = path4.dirname(fileURLToPath(import.meta.url));
10068
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
9964
10069
  while (true) {
9965
- const parent = path4.dirname(dir);
9966
- if (existsSync(path4.join(dir, "package.json")) || parent === dir)
10070
+ const parent = path6.dirname(dir);
10071
+ if (existsSync(path6.join(dir, "package.json")) || parent === dir)
9967
10072
  return dir;
9968
10073
  dir = parent;
9969
10074
  }
9970
10075
  };
9971
10076
  var assetRoot = () => {
9972
10077
  const root = packageRoot();
9973
- const assets = path4.join(root, "assets");
10078
+ const assets = path6.join(root, "assets");
9974
10079
  return existsSync(assets) ? assets : root;
9975
10080
  };
9976
10081
 
9977
10082
  // packages/workit-core/src/core/store-lock.ts
9978
10083
  import { spawnSync as spawnSync2 } from "node:child_process";
9979
- import fs5 from "node:fs";
10084
+ import fs6 from "node:fs";
9980
10085
  import { hostname } from "node:os";
9981
10086
  var metadataLockSchema = object({
9982
10087
  pid: number2().int().nonnegative().safe(),
@@ -9988,8 +10093,10 @@ var metadataLockSchema = object({
9988
10093
  var FOREIGN_LOCK_TTL_MS = 10 * 60000;
9989
10094
  var UNREADABLE_LOCK_TTL_MS = 30000;
9990
10095
  var RECLAIM_GUARD_TTL_MS = 30000;
9991
- var defaultLockTimeoutMs = 250;
10096
+ var IN_PROCESS_LOCK_TIMEOUT_MS = 250;
10097
+ var defaultLockTimeoutMs = IN_PROCESS_LOCK_TIMEOUT_MS;
9992
10098
  var defaultLockTimeout = () => defaultLockTimeoutMs;
10099
+ var hostAwareLockTimeout = (cliMs) => defaultLockTimeoutMs > IN_PROCESS_LOCK_TIMEOUT_MS ? cliMs : IN_PROCESS_LOCK_TIMEOUT_MS;
9993
10100
  var parseMetadataLock = (raw) => {
9994
10101
  let value;
9995
10102
  try {
@@ -10018,7 +10125,7 @@ var parseProcStatStart = (stat) => {
10018
10125
  var processStartOf = (pid) => {
10019
10126
  if (process.platform === "linux") {
10020
10127
  try {
10021
- return parseProcStatStart(fs5.readFileSync(`/proc/${pid}/stat`, "utf8"));
10128
+ return parseProcStatStart(fs6.readFileSync(`/proc/${pid}/stat`, "utf8"));
10022
10129
  } catch {
10023
10130
  return null;
10024
10131
  }
@@ -10039,7 +10146,7 @@ var processStartOf = (pid) => {
10039
10146
  };
10040
10147
  var readTrimmed = (file) => {
10041
10148
  try {
10042
- return fs5.readFileSync(file, "utf8").trim() || null;
10149
+ return fs6.readFileSync(file, "utf8").trim() || null;
10043
10150
  } catch {
10044
10151
  return null;
10045
10152
  }
@@ -10050,7 +10157,7 @@ var localLockHost = () => {
10050
10157
  return cachedLockHost;
10051
10158
  let pidns = null;
10052
10159
  try {
10053
- pidns = /\[(\d+)\]/.exec(fs5.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
10160
+ pidns = /\[(\d+)\]/.exec(fs6.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
10054
10161
  } catch {}
10055
10162
  const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
10056
10163
  cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
@@ -10096,7 +10203,7 @@ var classifyLockOwner = (payload, ageMs, localHost = localLockHost(), options =
10096
10203
  };
10097
10204
  var ageOf = (file, nowMs) => {
10098
10205
  try {
10099
- return nowMs - fs5.lstatSync(file).mtimeMs;
10206
+ return nowMs - fs6.lstatSync(file).mtimeMs;
10100
10207
  } catch {
10101
10208
  return null;
10102
10209
  }
@@ -10107,7 +10214,7 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
10107
10214
  if (age === null || age <= RECLAIM_GUARD_TTL_MS)
10108
10215
  return false;
10109
10216
  try {
10110
- fs5.rmdirSync(guard);
10217
+ fs6.rmdirSync(guard);
10111
10218
  return true;
10112
10219
  } catch {
10113
10220
  return false;
@@ -10116,8 +10223,8 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
10116
10223
 
10117
10224
  // packages/workit-core/src/store/event-log.ts
10118
10225
  import { randomUUID as randomUUID2 } from "node:crypto";
10119
- import fs6 from "node:fs";
10120
- import path5 from "node:path";
10226
+ import fs7 from "node:fs";
10227
+ import path7 from "node:path";
10121
10228
  var EVENT_VERSION = 1;
10122
10229
 
10123
10230
  class LogDamage extends Error {
@@ -10146,7 +10253,7 @@ var parseEvent = (line, file, offset) => {
10146
10253
  function readLog(file, from = 0, expectSeq = null) {
10147
10254
  let fd;
10148
10255
  try {
10149
- fd = fs6.openSync(file, "r");
10256
+ fd = fs7.openSync(file, "r");
10150
10257
  } catch (error) {
10151
10258
  if (error.code === "ENOENT")
10152
10259
  return null;
@@ -10155,13 +10262,13 @@ function readLog(file, from = 0, expectSeq = null) {
10155
10262
  let bytes;
10156
10263
  let size;
10157
10264
  try {
10158
- size = fs6.fstatSync(fd).size;
10265
+ size = fs7.fstatSync(fd).size;
10159
10266
  if (from > size)
10160
10267
  throw new LogDamage(`${file}: shorter than its snapshot`);
10161
10268
  bytes = Buffer.alloc(size - from);
10162
10269
  let read = 0;
10163
10270
  while (read < bytes.length) {
10164
- const got = fs6.readSync(fd, bytes, read, bytes.length - read, from + read);
10271
+ const got = fs7.readSync(fd, bytes, read, bytes.length - read, from + read);
10165
10272
  if (got === 0)
10166
10273
  break;
10167
10274
  read += got;
@@ -10169,7 +10276,7 @@ function readLog(file, from = 0, expectSeq = null) {
10169
10276
  bytes = bytes.subarray(0, read);
10170
10277
  size = from + read;
10171
10278
  } finally {
10172
- fs6.closeSync(fd);
10279
+ fs7.closeSync(fd);
10173
10280
  }
10174
10281
  const events = [];
10175
10282
  let start = 0;
@@ -10194,11 +10301,11 @@ function readLog(file, from = 0, expectSeq = null) {
10194
10301
  }
10195
10302
  var fsyncDirectory = (dir) => {
10196
10303
  try {
10197
- const fd = fs6.openSync(dir, "r");
10304
+ const fd = fs7.openSync(dir, "r");
10198
10305
  try {
10199
- fs6.fsyncSync(fd);
10306
+ fs7.fsyncSync(fd);
10200
10307
  } finally {
10201
- fs6.closeSync(fd);
10308
+ fs7.closeSync(fd);
10202
10309
  }
10203
10310
  } catch (error) {
10204
10311
  if (process.platform !== "win32")
@@ -10208,46 +10315,46 @@ var fsyncDirectory = (dir) => {
10208
10315
  function appendEvent(file, event, validEnd) {
10209
10316
  const line = Buffer.from(`${JSON.stringify(event)}
10210
10317
  `, "utf8");
10211
- const created = !fs6.existsSync(file);
10212
- if (!created && fs6.statSync(file).size !== validEnd) {
10213
- const cut = fs6.openSync(file, "r+");
10318
+ const created = !fs7.existsSync(file);
10319
+ if (!created && fs7.statSync(file).size !== validEnd) {
10320
+ const cut = fs7.openSync(file, "r+");
10214
10321
  try {
10215
- fs6.ftruncateSync(cut, validEnd);
10216
- fs6.fsyncSync(cut);
10322
+ fs7.ftruncateSync(cut, validEnd);
10323
+ fs7.fsyncSync(cut);
10217
10324
  } finally {
10218
- fs6.closeSync(cut);
10325
+ fs7.closeSync(cut);
10219
10326
  }
10220
10327
  }
10221
- const fd = fs6.openSync(file, "a", 384);
10328
+ const fd = fs7.openSync(file, "a", 384);
10222
10329
  try {
10223
10330
  let written = 0;
10224
10331
  while (written < line.length)
10225
- written += fs6.writeSync(fd, line, written, line.length - written);
10226
- fs6.fsyncSync(fd);
10332
+ written += fs7.writeSync(fd, line, written, line.length - written);
10333
+ fs7.fsyncSync(fd);
10227
10334
  } finally {
10228
- fs6.closeSync(fd);
10335
+ fs7.closeSync(fd);
10229
10336
  }
10230
10337
  if (created)
10231
- fsyncDirectory(path5.dirname(file));
10338
+ fsyncDirectory(path7.dirname(file));
10232
10339
  return validEnd;
10233
10340
  }
10234
10341
  function replaceFile(file, bytes, durable) {
10235
10342
  const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
10236
10343
  try {
10237
- const fd = fs6.openSync(temporary, "wx", 384);
10344
+ const fd = fs7.openSync(temporary, "wx", 384);
10238
10345
  try {
10239
- fs6.writeSync(fd, bytes);
10346
+ fs7.writeSync(fd, bytes);
10240
10347
  if (durable)
10241
- fs6.fsyncSync(fd);
10348
+ fs7.fsyncSync(fd);
10242
10349
  } finally {
10243
- fs6.closeSync(fd);
10350
+ fs7.closeSync(fd);
10244
10351
  }
10245
10352
  renameWithRetry(temporary, file);
10246
10353
  if (durable)
10247
- fsyncDirectory(path5.dirname(file));
10354
+ fsyncDirectory(path7.dirname(file));
10248
10355
  } catch (error) {
10249
10356
  try {
10250
- fs6.unlinkSync(temporary);
10357
+ fs7.unlinkSync(temporary);
10251
10358
  } catch {}
10252
10359
  throw error;
10253
10360
  }
@@ -10272,7 +10379,7 @@ var retryTransient = (run) => {
10272
10379
  }
10273
10380
  }
10274
10381
  };
10275
- var renameWithRetry = (from, to) => retryTransient(() => fs6.renameSync(from, to));
10382
+ var renameWithRetry = (from, to) => retryTransient(() => fs7.renameSync(from, to));
10276
10383
 
10277
10384
  // packages/workit-core/src/store/patch.ts
10278
10385
  var isPlainObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -10385,7 +10492,7 @@ var isPatch = (value) => Array.isArray(value) && value.every((op) => {
10385
10492
  });
10386
10493
 
10387
10494
  // packages/workit-core/src/store/reduce.ts
10388
- import { createHash as createHash3 } from "node:crypto";
10495
+ import { createHash as createHash4 } from "node:crypto";
10389
10496
  var OPENING_TYPES = new Set([
10390
10497
  "task.opened",
10391
10498
  "task.imported",
@@ -10414,7 +10521,7 @@ var isObject4 = (value) => typeof value === "object" && value !== null && !Array
10414
10521
  var isKey = (value) => isObject4(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string");
10415
10522
  var BLOB_REF = "$blob";
10416
10523
  var blobRef = (value) => isObject4(value) && Object.keys(value).length === 1 && typeof value[BLOB_REF] === "string" && /^[0-9a-f]{64}$/.test(value[BLOB_REF]) ? value[BLOB_REF] : null;
10417
- var blobDigest = (value) => createHash3("sha256").update(JSON.stringify(value)).digest("hex");
10524
+ var blobDigest = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
10418
10525
  var toBlob = (value, blobs) => {
10419
10526
  if (!isObject4(value))
10420
10527
  return value;
@@ -10569,7 +10676,7 @@ var indexEntry = (task, key, legacy, file) => {
10569
10676
  };
10570
10677
  var fileSignature = (file) => {
10571
10678
  try {
10572
- const stat = fs7.statSync(file, { bigint: true });
10679
+ const stat = fs8.statSync(file, { bigint: true });
10573
10680
  return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
10574
10681
  } catch {
10575
10682
  return null;
@@ -10587,7 +10694,7 @@ var cachedRuntimeVersion = null;
10587
10694
  var runtimeVersion = () => {
10588
10695
  if (cachedRuntimeVersion === null) {
10589
10696
  try {
10590
- const pkg = JSON.parse(fs7.readFileSync(path6.join(packageRoot(), "package.json"), "utf8"));
10697
+ const pkg = JSON.parse(fs8.readFileSync(path8.join(packageRoot(), "package.json"), "utf8"));
10591
10698
  cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
10592
10699
  } catch {
10593
10700
  cachedRuntimeVersion = "0.0.0";
@@ -10613,14 +10720,14 @@ var isNewerVersion = (candidate, current) => {
10613
10720
  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);
10614
10721
  var readText = (file) => {
10615
10722
  try {
10616
- return fs7.readFileSync(file, "utf8");
10723
+ return fs8.readFileSync(file, "utf8");
10617
10724
  } catch {
10618
10725
  return "";
10619
10726
  }
10620
10727
  };
10621
10728
  var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
10622
10729
  var branchStart = (location, branch, aliases) => {
10623
- const first = readText(path6.join(path6.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
10730
+ const first = readText(path8.join(path8.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
10624
10731
  `, 1)[0];
10625
10732
  const created = REFLOG_LINE.exec(first);
10626
10733
  const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
@@ -10630,7 +10737,7 @@ var branchStart = (location, branch, aliases) => {
10630
10737
  return start;
10631
10738
  if (!location.git)
10632
10739
  return null;
10633
- for (const line of readText(path6.join(location.git.gitDir, "logs", "HEAD")).split(`
10740
+ for (const line of readText(path8.join(location.git.gitDir, "logs", "HEAD")).split(`
10634
10741
  `)) {
10635
10742
  const entry = REFLOG_LINE.exec(line);
10636
10743
  const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
@@ -10640,23 +10747,23 @@ var branchStart = (location, branch, aliases) => {
10640
10747
  }
10641
10748
  return null;
10642
10749
  };
10643
- var refExists = (common, ref) => fs7.existsSync(path6.join(common, ref)) || readText(path6.join(common, "packed-refs")).includes(` ${ref}
10750
+ var refExists = (common, ref) => fs8.existsSync(path8.join(common, ref)) || readText(path8.join(common, "packed-refs")).includes(` ${ref}
10644
10751
  `);
10645
10752
  var origins = new Map;
10646
10753
  var sameDirectoryIdentity = (left, right) => {
10647
- if (!path6.isAbsolute(left) || !path6.isAbsolute(right))
10754
+ if (!path8.isAbsolute(left) || !path8.isAbsolute(right))
10648
10755
  return false;
10649
- const normalizedLeft = path6.resolve(left);
10650
- const normalizedRight = path6.resolve(right);
10756
+ const normalizedLeft = path8.resolve(left);
10757
+ const normalizedRight = path8.resolve(right);
10651
10758
  try {
10652
- const a = fs7.statSync(normalizedLeft, { bigint: true });
10653
- const b = fs7.statSync(normalizedRight, { bigint: true });
10759
+ const a = fs8.statSync(normalizedLeft, { bigint: true });
10760
+ const b = fs8.statSync(normalizedRight, { bigint: true });
10654
10761
  if (!a.isDirectory() || !b.isDirectory())
10655
10762
  return false;
10656
10763
  if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
10657
10764
  return true;
10658
- const canonicalLeft = fs7.realpathSync(normalizedLeft);
10659
- const canonicalRight = fs7.realpathSync(normalizedRight);
10765
+ const canonicalLeft = fs8.realpathSync(normalizedLeft);
10766
+ const canonicalRight = fs8.realpathSync(normalizedRight);
10660
10767
  if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
10661
10768
  return true;
10662
10769
  return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
@@ -10693,8 +10800,8 @@ class TaskStore {
10693
10800
  migrationChecked = false;
10694
10801
  compactDue = new Set;
10695
10802
  constructor(root, options = {}) {
10696
- const resolved = path6.resolve(root);
10697
- this.origin = fs7.existsSync(resolved) ? fs7.realpathSync(resolved) : resolved;
10803
+ const resolved = path8.resolve(root);
10804
+ this.origin = fs8.existsSync(resolved) ? fs8.realpathSync(resolved) : resolved;
10698
10805
  this.located = resolveStore(this.origin);
10699
10806
  this.root = checkoutRootOf(this.origin, this.located);
10700
10807
  this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
@@ -10702,7 +10809,7 @@ class TaskStore {
10702
10809
  this.migrateOnRead = options.migrateOnRead;
10703
10810
  }
10704
10811
  location() {
10705
- if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs7.existsSync(path6.join(this.root, ".git"))) {
10812
+ if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs8.existsSync(path8.join(this.root, ".git"))) {
10706
10813
  this.located = undefined;
10707
10814
  this.migrationChecked = false;
10708
10815
  }
@@ -10766,7 +10873,7 @@ class TaskStore {
10766
10873
  const tasksDir = this.paths().tasks;
10767
10874
  let names;
10768
10875
  try {
10769
- names = fs7.existsSync(tasksDir) ? fs7.readdirSync(tasksDir).filter((name) => validId(name)) : [];
10876
+ names = fs8.existsSync(tasksDir) ? fs8.readdirSync(tasksDir).filter((name) => validId(name)) : [];
10770
10877
  } catch (error) {
10771
10878
  return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
10772
10879
  path: tasksDir
@@ -10807,7 +10914,7 @@ class TaskStore {
10807
10914
  const file = this.paths().workspace;
10808
10915
  let bytes;
10809
10916
  try {
10810
- bytes = retryTransient(() => fs7.readFileSync(file, "utf8"));
10917
+ bytes = retryTransient(() => fs8.readFileSync(file, "utf8"));
10811
10918
  } catch (error) {
10812
10919
  if (error.code === "ENOENT")
10813
10920
  return success(null, null, null);
@@ -10858,7 +10965,7 @@ class TaskStore {
10858
10965
  const moved = this.bindLocked(again.data.id, key, input.now);
10859
10966
  return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
10860
10967
  }
10861
- const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path6.basename(this.root) || this.root;
10968
+ const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path8.basename(this.root) || this.root;
10862
10969
  const intent = {
10863
10970
  objective: input.objective?.trim() || `Work on ${label}`,
10864
10971
  scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
@@ -10918,14 +11025,14 @@ class TaskStore {
10918
11025
  const branch = key.ok ? key.data.branch : null;
10919
11026
  if (!branch || !location.ok)
10920
11027
  return success(null, null, null);
10921
- const common = path6.dirname(location.data.dir);
10922
- const heads = path6.join(common, "logs", "refs", "heads");
10923
- const headLog = location.data.git ? path6.join(location.data.git.gitDir, "logs", "HEAD") : "";
11028
+ const common = path8.dirname(location.data.dir);
11029
+ const heads = path8.join(common, "logs", "refs", "heads");
11030
+ const headLog = location.data.git ? path8.join(location.data.git.gitDir, "logs", "HEAD") : "";
10924
11031
  const signatures = [
10925
- path6.join(heads, branch),
11032
+ path8.join(heads, branch),
10926
11033
  headLog,
10927
11034
  heads,
10928
- path6.join(common, "packed-refs")
11035
+ path8.join(common, "packed-refs")
10929
11036
  ].map((file) => fileSignature(file) ?? "-");
10930
11037
  const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
10931
11038
  let resolved = origins.get(slot);
@@ -10937,7 +11044,7 @@ class TaskStore {
10937
11044
  aliases.push(rename.from);
10938
11045
  const start = branchStart(location.data, branch, aliases);
10939
11046
  const slash = start?.indexOf("/") ?? -1;
10940
- const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path6.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
11047
+ const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path8.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
10941
11048
  const name = start && (remote ? start.slice(slash + 1) : start);
10942
11049
  resolved = {
10943
11050
  signature: signatures.join("|"),
@@ -11175,13 +11282,13 @@ class TaskStore {
11175
11282
  if (!ready.ok)
11176
11283
  return ready;
11177
11284
  const dirs = this.paths();
11178
- const recovery = path6.join(this.root, ".workit", "recovery");
11179
- if (fs7.existsSync(recovery)) {
11285
+ const recovery = path8.join(this.root, ".workit", "recovery");
11286
+ if (fs8.existsSync(recovery)) {
11180
11287
  let files = 0;
11181
11288
  let bytes = 0;
11182
11289
  try {
11183
- for (const name of fs7.readdirSync(recovery)) {
11184
- const stat = fs7.lstatSync(path6.join(recovery, name));
11290
+ for (const name of fs8.readdirSync(recovery)) {
11291
+ const stat = fs8.lstatSync(path8.join(recovery, name));
11185
11292
  if (!stat.isFile())
11186
11293
  continue;
11187
11294
  files += 1;
@@ -11190,17 +11297,17 @@ class TaskStore {
11190
11297
  } catch {}
11191
11298
  const remove = options.pruneRecovery === true && !dryRun;
11192
11299
  if (remove)
11193
- fs7.rmSync(recovery, { recursive: true, force: true });
11300
+ fs8.rmSync(recovery, { recursive: true, force: true });
11194
11301
  report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
11195
11302
  }
11196
- if (!fs7.existsSync(dirs.tasks))
11303
+ if (!fs8.existsSync(dirs.tasks))
11197
11304
  return success(null, null, report);
11198
- const names = fs7.readdirSync(dirs.tasks).filter((name) => validId(name));
11305
+ const names = fs8.readdirSync(dirs.tasks).filter((name) => validId(name));
11199
11306
  for (const name of names) {
11200
11307
  const file = this.eventsPath(name);
11201
11308
  let size = 0;
11202
11309
  try {
11203
- size = fs7.statSync(file).size;
11310
+ size = fs8.statSync(file).size;
11204
11311
  } catch {
11205
11312
  continue;
11206
11313
  }
@@ -11228,16 +11335,16 @@ class TaskStore {
11228
11335
  const references = new Set;
11229
11336
  for (const name of names)
11230
11337
  try {
11231
- for (const match of fs7.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
11338
+ for (const match of fs8.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
11232
11339
  references.add(match[1]);
11233
11340
  } catch {}
11234
- if (fs7.existsSync(dirs.blobs))
11235
- for (const name of fs7.readdirSync(dirs.blobs)) {
11341
+ if (fs8.existsSync(dirs.blobs))
11342
+ for (const name of fs8.readdirSync(dirs.blobs)) {
11236
11343
  const digest = name.replace(/\.json$/, "");
11237
- const file = path6.join(dirs.blobs, name);
11344
+ const file = path8.join(dirs.blobs, name);
11238
11345
  let stat;
11239
11346
  try {
11240
- stat = fs7.lstatSync(file);
11347
+ stat = fs8.lstatSync(file);
11241
11348
  } catch {
11242
11349
  continue;
11243
11350
  }
@@ -11248,11 +11355,11 @@ class TaskStore {
11248
11355
  report.blobs.removed += 1;
11249
11356
  report.blobs.removedBytes += stat.size;
11250
11357
  if (!dryRun)
11251
- fs7.rmSync(file, { force: true });
11358
+ fs8.rmSync(file, { force: true });
11252
11359
  }
11253
11360
  return success(null, null, null);
11254
11361
  };
11255
- const swept = dryRun ? sweep() : this.withLock(path6.join(dirs.dir, "gc.lock"), sweep);
11362
+ const swept = dryRun ? sweep() : this.withLock(path8.join(dirs.dir, "gc.lock"), sweep);
11256
11363
  if (!swept.ok)
11257
11364
  return swept;
11258
11365
  const temporaryDirs = [
@@ -11260,17 +11367,17 @@ class TaskStore {
11260
11367
  dirs.tasks,
11261
11368
  dirs.blobs,
11262
11369
  dirs.checkout,
11263
- ...names.map((name) => path6.join(dirs.tasks, name))
11370
+ ...names.map((name) => path8.join(dirs.tasks, name))
11264
11371
  ];
11265
11372
  for (const directory of temporaryDirs) {
11266
- if (!fs7.existsSync(directory))
11373
+ if (!fs8.existsSync(directory))
11267
11374
  continue;
11268
- for (const name of fs7.readdirSync(directory)) {
11375
+ for (const name of fs8.readdirSync(directory)) {
11269
11376
  if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
11270
11377
  continue;
11271
- const file = path6.join(directory, name);
11378
+ const file = path8.join(directory, name);
11272
11379
  try {
11273
- const stat = fs7.lstatSync(file);
11380
+ const stat = fs8.lstatSync(file);
11274
11381
  if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
11275
11382
  continue;
11276
11383
  } catch {
@@ -11278,37 +11385,37 @@ class TaskStore {
11278
11385
  }
11279
11386
  report.temporary.removed += 1;
11280
11387
  if (!dryRun)
11281
- fs7.rmSync(file, { force: true });
11388
+ fs8.rmSync(file, { force: true });
11282
11389
  }
11283
11390
  }
11284
11391
  return success(null, null, report);
11285
11392
  }
11286
11393
  paths() {
11287
11394
  const location = this.location();
11288
- const dir = location.ok ? location.data.dir : path6.join(this.root, ".workit");
11289
- const checkout = path6.join(dir, "checkouts", checkoutSlug(this.root));
11395
+ const dir = location.ok ? location.data.dir : path8.join(this.root, ".workit");
11396
+ const checkout = path8.join(dir, "checkouts", checkoutSlug(this.root));
11290
11397
  return {
11291
11398
  dir,
11292
11399
  shared: location.ok ? location.data.shared : false,
11293
- marker: path6.join(dir, "store.json"),
11400
+ marker: path8.join(dir, "store.json"),
11294
11401
  checkout,
11295
- workspace: path6.join(checkout, "workspace.json"),
11296
- checkoutLock: path6.join(checkout, "metadata.lock"),
11297
- tasks: path6.join(dir, "tasks"),
11298
- index: path6.join(dir, "task-index.json"),
11299
- blobs: path6.join(dir, "blobs", "candidates"),
11300
- legacy: path6.join(dir, "legacy", checkoutSlug(this.root), "v2"),
11301
- v2: path6.join(this.root, ".workit")
11402
+ workspace: path8.join(checkout, "workspace.json"),
11403
+ checkoutLock: path8.join(checkout, "metadata.lock"),
11404
+ tasks: path8.join(dir, "tasks"),
11405
+ index: path8.join(dir, "task-index.json"),
11406
+ blobs: path8.join(dir, "blobs", "candidates"),
11407
+ legacy: path8.join(dir, "legacy", checkoutSlug(this.root), "v2"),
11408
+ v2: path8.join(this.root, ".workit")
11302
11409
  };
11303
11410
  }
11304
11411
  eventsPath(taskId) {
11305
- return path6.join(this.paths().tasks, taskId, "events.jsonl");
11412
+ return path8.join(this.paths().tasks, taskId, "events.jsonl");
11306
11413
  }
11307
11414
  snapshotPath(taskId) {
11308
- return path6.join(this.paths().tasks, taskId, "snapshot.json");
11415
+ return path8.join(this.paths().tasks, taskId, "snapshot.json");
11309
11416
  }
11310
11417
  taskLockPath(taskId) {
11311
- return path6.join(this.paths().tasks, taskId, "lock");
11418
+ return path8.join(this.paths().tasks, taskId, "lock");
11312
11419
  }
11313
11420
  ready(write = false) {
11314
11421
  const location = this.location();
@@ -11318,7 +11425,7 @@ class TaskStore {
11318
11425
  return success(null, null, null);
11319
11426
  const dirs = this.paths();
11320
11427
  try {
11321
- const marker = JSON.parse(fs7.readFileSync(dirs.marker, "utf8"));
11428
+ const marker = JSON.parse(fs8.readFileSync(dirs.marker, "utf8"));
11322
11429
  if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
11323
11430
  return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
11324
11431
  } catch {}
@@ -11347,21 +11454,21 @@ class TaskStore {
11347
11454
  return {
11348
11455
  read: (digest) => {
11349
11456
  try {
11350
- return JSON.parse(fs7.readFileSync(path6.join(dir, `${digest}.json`), "utf8"));
11457
+ return JSON.parse(fs8.readFileSync(path8.join(dir, `${digest}.json`), "utf8"));
11351
11458
  } catch (error) {
11352
11459
  throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
11353
11460
  }
11354
11461
  },
11355
11462
  write: (digest, value) => {
11356
- const file = path6.join(dir, `${digest}.json`);
11357
- if (fs7.existsSync(file)) {
11463
+ const file = path8.join(dir, `${digest}.json`);
11464
+ if (fs8.existsSync(file)) {
11358
11465
  const stamp = new Date;
11359
11466
  try {
11360
- fs7.utimesSync(file, stamp, stamp);
11467
+ fs8.utimesSync(file, stamp, stamp);
11361
11468
  return;
11362
11469
  } catch {}
11363
11470
  }
11364
- fs7.mkdirSync(dir, { recursive: true });
11471
+ fs8.mkdirSync(dir, { recursive: true });
11365
11472
  replaceFile(file, JSON.stringify(value), true);
11366
11473
  }
11367
11474
  };
@@ -11443,7 +11550,7 @@ class TaskStore {
11443
11550
  }
11444
11551
  readSnapshot(taskId) {
11445
11552
  try {
11446
- const value = JSON.parse(fs7.readFileSync(this.snapshotPath(taskId), "utf8"));
11553
+ const value = JSON.parse(fs8.readFileSync(this.snapshotPath(taskId), "utf8"));
11447
11554
  if (!isObject5(value) || value.v !== 1 || value.task !== taskId || typeof value.seq !== "number" || typeof value.lastId !== "string" || typeof value.lastStart !== "number" || typeof value.end !== "number" || !isObject5(value.record))
11448
11555
  return null;
11449
11556
  return value;
@@ -11545,8 +11652,8 @@ class TaskStore {
11545
11652
  }
11546
11653
  }
11547
11654
  openTask(record, type, key, legacy = null) {
11548
- const dir = path6.join(this.paths().tasks, record.id);
11549
- fs7.mkdirSync(dir, { recursive: true });
11655
+ const dir = path8.join(this.paths().tasks, record.id);
11656
+ fs8.mkdirSync(dir, { recursive: true });
11550
11657
  const blobs = this.blobStore();
11551
11658
  const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
11552
11659
  const event = this.event(record.id, 1, type, {
@@ -11598,11 +11705,11 @@ class TaskStore {
11598
11705
  } catch {}
11599
11706
  if (!latest || latest.events[0]?.id !== last.id)
11600
11707
  return failure2("busy", "the task log changed during compaction; retry", { taskId });
11601
- const appended = fs7.readFileSync(file).subarray(log.end, latest.end);
11708
+ const appended = fs8.readFileSync(file).subarray(log.end, latest.end);
11602
11709
  const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
11603
11710
  replaceFile(file, bytes.toString("utf8"), true);
11604
11711
  try {
11605
- fs7.rmSync(this.snapshotPath(taskId), { force: true });
11712
+ fs8.rmSync(this.snapshotPath(taskId), { force: true });
11606
11713
  } catch {}
11607
11714
  const reloaded = this.load(taskId);
11608
11715
  if (reloaded.ok && reloaded.data)
@@ -11615,7 +11722,7 @@ class TaskStore {
11615
11722
  }
11616
11723
  countEvents(file) {
11617
11724
  try {
11618
- const bytes = fs7.readFileSync(file);
11725
+ const bytes = fs8.readFileSync(file);
11619
11726
  let count = 0;
11620
11727
  for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
11621
11728
  count += 1;
@@ -11637,8 +11744,8 @@ class TaskStore {
11637
11744
  return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
11638
11745
  }
11639
11746
  withKeyLock(key, operation) {
11640
- const digest = createHash4("sha256").update(key).digest("hex").slice(0, 32);
11641
- return this.withLock(path6.join(this.paths().dir, "keys", `${digest}.lock`), operation);
11747
+ const digest = createHash5("sha256").update(key).digest("hex").slice(0, 32);
11748
+ return this.withLock(path8.join(this.paths().dir, "keys", `${digest}.lock`), operation);
11642
11749
  }
11643
11750
  bindLocked(taskId, key, timestamp) {
11644
11751
  const current = this.readWorkspace();
@@ -11779,7 +11886,7 @@ class TaskStore {
11779
11886
  writeWorkspace(record) {
11780
11887
  const file = this.paths().workspace;
11781
11888
  try {
11782
- fs7.mkdirSync(path6.dirname(file), { recursive: true });
11889
+ fs8.mkdirSync(path8.dirname(file), { recursive: true });
11783
11890
  replaceFile(file, `${canonicalJson(record)}
11784
11891
  `, true);
11785
11892
  return success(null, null, null);
@@ -11789,7 +11896,7 @@ class TaskStore {
11789
11896
  }
11790
11897
  readIndex() {
11791
11898
  try {
11792
- const value = JSON.parse(fs7.readFileSync(this.paths().index, "utf8"));
11899
+ const value = JSON.parse(fs8.readFileSync(this.paths().index, "utf8"));
11793
11900
  if (!isObject5(value) || value.version !== INDEX_VERSION || !isObject5(value.tasks))
11794
11901
  return {};
11795
11902
  const tasks = {};
@@ -11803,7 +11910,7 @@ class TaskStore {
11803
11910
  }
11804
11911
  writeIndex(entries) {
11805
11912
  try {
11806
- fs7.mkdirSync(this.paths().tasks, { recursive: true });
11913
+ fs8.mkdirSync(this.paths().tasks, { recursive: true });
11807
11914
  replaceFile(this.paths().index, JSON.stringify({
11808
11915
  version: INDEX_VERSION,
11809
11916
  tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
@@ -11857,23 +11964,23 @@ class TaskStore {
11857
11964
  return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
11858
11965
  }
11859
11966
  v2Dirs() {
11860
- return [...new Set([this.root, this.origin])].map((dir) => path6.join(dir, ".workit"));
11967
+ return [...new Set([this.root, this.origin])].map((dir) => path8.join(dir, ".workit"));
11861
11968
  }
11862
11969
  pendingMigrations() {
11863
11970
  return this.v2Dirs().filter((dir) => {
11864
- const raw = this.readV2Workspace(path6.join(dir, "workspace.json"));
11971
+ const raw = this.readV2Workspace(path8.join(dir, "workspace.json"));
11865
11972
  return raw !== null && raw !== TOMBSTONE;
11866
11973
  });
11867
11974
  }
11868
11975
  migrateV2(v2) {
11869
11976
  const dirs = this.paths();
11870
- const v2Workspace = path6.join(v2, "workspace.json");
11871
- const v2Tasks = path6.join(v2, "tasks");
11872
- const legacy = path6.join(dirs.dir, "legacy", checkoutSlug(path6.dirname(v2)), "v2");
11977
+ const v2Workspace = path8.join(v2, "workspace.json");
11978
+ const v2Tasks = path8.join(v2, "tasks");
11979
+ const legacy = path8.join(dirs.dir, "legacy", checkoutSlug(path8.dirname(v2)), "v2");
11873
11980
  const raw = this.readV2Workspace(v2Workspace);
11874
11981
  if (raw === null || raw === TOMBSTONE)
11875
11982
  return success(null, null, null);
11876
- return this.withLock(dirs.checkoutLock, () => this.withLock(path6.join(v2, "metadata.lock"), () => {
11983
+ return this.withLock(dirs.checkoutLock, () => this.withLock(path8.join(v2, "metadata.lock"), () => {
11877
11984
  const workspace = this.readV2Workspace(v2Workspace);
11878
11985
  if (workspace === null || workspace === TOMBSTONE)
11879
11986
  return success(null, null, null);
@@ -11882,7 +11989,7 @@ class TaskStore {
11882
11989
  return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
11883
11990
  let files = [];
11884
11991
  try {
11885
- files = fs7.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path6.join(v2Tasks, name)));
11992
+ files = fs8.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path8.join(v2Tasks, name)));
11886
11993
  } catch {}
11887
11994
  const current = this.readWorkspace();
11888
11995
  if (!current.ok)
@@ -11900,22 +12007,22 @@ class TaskStore {
11900
12007
  let skipped = 0;
11901
12008
  const done = new Map;
11902
12009
  try {
11903
- fs7.mkdirSync(path6.join(legacy, "tasks"), { recursive: true });
12010
+ fs8.mkdirSync(path8.join(legacy, "tasks"), { recursive: true });
11904
12011
  for (const name of ["workspace.json", "index.json"])
11905
- if (fs7.existsSync(path6.join(v2, name)))
11906
- fs7.copyFileSync(path6.join(v2, name), path6.join(legacy, name));
12012
+ if (fs8.existsSync(path8.join(v2, name)))
12013
+ fs8.copyFileSync(path8.join(v2, name), path8.join(legacy, name));
11907
12014
  for (const name of files) {
11908
12015
  const id = name.slice(0, -5);
11909
- const file = path6.join(v2Tasks, name);
11910
- const bytes = fs7.readFileSync(file, "utf8");
12016
+ const file = path8.join(v2Tasks, name);
12017
+ const bytes = fs8.readFileSync(file, "utf8");
11911
12018
  const digest = sha256(bytes);
11912
- fs7.writeFileSync(path6.join(legacy, "tasks", name), bytes);
12019
+ fs8.writeFileSync(path8.join(legacy, "tasks", name), bytes);
11913
12020
  const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
11914
12021
  if (!parsed.ok || parsed.data.id !== id)
11915
12022
  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 });
11916
12023
  const record = { ...parsed.data, workspaceId: target.id };
11917
- const origin = { path: path6.join(legacy, "tasks", name), digest };
11918
- if (!fs7.existsSync(this.eventsPath(id))) {
12024
+ const origin = { path: path8.join(legacy, "tasks", name), digest };
12025
+ if (!fs8.existsSync(this.eventsPath(id))) {
11919
12026
  this.openTask(record, "migrated.from_v2", null, origin);
11920
12027
  migrated += 1;
11921
12028
  } else {
@@ -11950,8 +12057,8 @@ class TaskStore {
11950
12057
  try {
11951
12058
  let complete = true;
11952
12059
  for (const [name, digest] of done) {
11953
- const file = path6.join(v2Tasks, name);
11954
- if (sha256(fs7.readFileSync(file, "utf8")) !== digest) {
12060
+ const file = path8.join(v2Tasks, name);
12061
+ if (sha256(fs8.readFileSync(file, "utf8")) !== digest) {
11955
12062
  complete = false;
11956
12063
  continue;
11957
12064
  }
@@ -11961,7 +12068,7 @@ class TaskStore {
11961
12068
  return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
11962
12069
  path: v2
11963
12070
  });
11964
- fs7.rmSync(path6.join(v2, "index.json"), { force: true });
12071
+ fs8.rmSync(path8.join(v2, "index.json"), { force: true });
11965
12072
  this.writeTombstone(v2, target.id);
11966
12073
  } catch (error) {
11967
12074
  return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
@@ -11981,45 +12088,45 @@ class TaskStore {
11981
12088
  }
11982
12089
  adoptLocalStore() {
11983
12090
  const dirs = this.paths();
11984
- const marker = path6.join(dirs.v2, "store.json");
11985
- if (!dirs.shared || !fs7.existsSync(marker))
12091
+ const marker = path8.join(dirs.v2, "store.json");
12092
+ if (!dirs.shared || !fs8.existsSync(marker))
11986
12093
  return success(null, null, null);
11987
12094
  return this.withLock(dirs.checkoutLock, () => {
11988
- if (!fs7.existsSync(marker))
12095
+ if (!fs8.existsSync(marker))
11989
12096
  return success(null, null, null);
11990
12097
  let tasks = 0;
11991
12098
  try {
11992
12099
  const merge = (from, to) => {
11993
- if (!fs7.existsSync(to)) {
11994
- fs7.mkdirSync(path6.dirname(to), { recursive: true });
12100
+ if (!fs8.existsSync(to)) {
12101
+ fs8.mkdirSync(path8.dirname(to), { recursive: true });
11995
12102
  try {
11996
- fs7.renameSync(from, to);
12103
+ fs8.renameSync(from, to);
11997
12104
  } catch {
11998
- fs7.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
11999
- fs7.rmSync(from, { recursive: true, force: true });
12105
+ fs8.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
12106
+ fs8.rmSync(from, { recursive: true, force: true });
12000
12107
  }
12001
12108
  return;
12002
12109
  }
12003
- if (fs7.lstatSync(from).isDirectory() && fs7.lstatSync(to).isDirectory())
12004
- for (const name of fs7.readdirSync(from))
12005
- merge(path6.join(from, name), path6.join(to, name));
12110
+ if (fs8.lstatSync(from).isDirectory() && fs8.lstatSync(to).isDirectory())
12111
+ for (const name of fs8.readdirSync(from))
12112
+ merge(path8.join(from, name), path8.join(to, name));
12006
12113
  };
12007
12114
  for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
12008
- const from = path6.join(dirs.v2, sub);
12009
- if (!fs7.existsSync(from))
12115
+ const from = path8.join(dirs.v2, sub);
12116
+ if (!fs8.existsSync(from))
12010
12117
  continue;
12011
12118
  if (sub === "tasks") {
12012
- for (const name of fs7.readdirSync(from))
12013
- if (validId(name) && !fs7.existsSync(path6.join(dirs.dir, sub, name)))
12119
+ for (const name of fs8.readdirSync(from))
12120
+ if (validId(name) && !fs8.existsSync(path8.join(dirs.dir, sub, name)))
12014
12121
  tasks += 1;
12015
12122
  }
12016
- merge(from, path6.join(dirs.dir, sub));
12123
+ merge(from, path8.join(dirs.dir, sub));
12017
12124
  }
12018
- const ledger = path6.join(dirs.v2, "ledger");
12019
- if (fs7.existsSync(ledger) && !fs7.existsSync(path6.join(dirs.dir, "ledger")))
12020
- merge(ledger, path6.join(dirs.dir, "ledger"));
12021
- fs7.rmSync(path6.join(dirs.v2, "task-index.json"), { force: true });
12022
- fs7.renameSync(marker, path6.join(dirs.v2, "store.moved.json"));
12125
+ const ledger = path8.join(dirs.v2, "ledger");
12126
+ if (fs8.existsSync(ledger) && !fs8.existsSync(path8.join(dirs.dir, "ledger")))
12127
+ merge(ledger, path8.join(dirs.dir, "ledger"));
12128
+ fs8.rmSync(path8.join(dirs.v2, "task-index.json"), { force: true });
12129
+ fs8.renameSync(marker, path8.join(dirs.v2, "store.moved.json"));
12023
12130
  } catch (error) {
12024
12131
  return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
12025
12132
  path: dirs.v2
@@ -12039,7 +12146,7 @@ class TaskStore {
12039
12146
  readV2Workspace(file) {
12040
12147
  let bytes;
12041
12148
  try {
12042
- bytes = fs7.readFileSync(file, "utf8");
12149
+ bytes = fs8.readFileSync(file, "utf8");
12043
12150
  } catch {
12044
12151
  return null;
12045
12152
  }
@@ -12057,7 +12164,7 @@ class TaskStore {
12057
12164
  schemaVersion: SCHEMA_VERSION,
12058
12165
  id: workspaceId ?? newId(),
12059
12166
  revision: newRevision(),
12060
- root: path6.dirname(v2),
12167
+ root: path8.dirname(v2),
12061
12168
  runtime: { createdWith: version, updatedWith: version },
12062
12169
  writer: null,
12063
12170
  store: {
@@ -12068,12 +12175,12 @@ class TaskStore {
12068
12175
  },
12069
12176
  critical: ["store"]
12070
12177
  };
12071
- fs7.mkdirSync(v2, { recursive: true });
12072
- const ignore = path6.join(v2, ".gitignore");
12073
- if (!fs7.existsSync(ignore))
12074
- fs7.writeFileSync(ignore, `*
12178
+ fs8.mkdirSync(v2, { recursive: true });
12179
+ const ignore = path8.join(v2, ".gitignore");
12180
+ if (!fs8.existsSync(ignore))
12181
+ fs8.writeFileSync(ignore, `*
12075
12182
  `);
12076
- replaceFile(path6.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
12183
+ replaceFile(path8.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
12077
12184
  `, true);
12078
12185
  }
12079
12186
  stubFor(taskId) {
@@ -12089,7 +12196,7 @@ class TaskStore {
12089
12196
  }
12090
12197
  isStub(file) {
12091
12198
  try {
12092
- const value = JSON.parse(fs7.readFileSync(file, "utf8"));
12199
+ const value = JSON.parse(fs8.readFileSync(file, "utf8"));
12093
12200
  return isObject5(value) && isObject5(value.store) && value.store.format === "workit-store";
12094
12201
  } catch {
12095
12202
  return false;
@@ -12097,32 +12204,32 @@ class TaskStore {
12097
12204
  }
12098
12205
  writeTombstoneIfMissing() {
12099
12206
  try {
12100
- const v2 = path6.join(this.root, ".workit");
12101
- if (fs7.existsSync(path6.join(v2, "workspace.json")))
12207
+ const v2 = path8.join(this.root, ".workit");
12208
+ if (fs8.existsSync(path8.join(v2, "workspace.json")))
12102
12209
  return;
12103
12210
  let id = null;
12104
12211
  try {
12105
- id = JSON.parse(fs7.readFileSync(this.paths().workspace, "utf8")).id ?? null;
12212
+ id = JSON.parse(fs8.readFileSync(this.paths().workspace, "utf8")).id ?? null;
12106
12213
  } catch {}
12107
12214
  this.writeTombstone(v2, id);
12108
12215
  } catch {}
12109
12216
  }
12110
12217
  initializeStorage() {
12111
12218
  const dirs = this.paths();
12112
- retryTransient(() => fs7.mkdirSync(dirs.checkout, { recursive: true }));
12113
- retryTransient(() => fs7.mkdirSync(dirs.tasks, { recursive: true }));
12114
- if (!fs7.existsSync(dirs.marker))
12219
+ retryTransient(() => fs8.mkdirSync(dirs.checkout, { recursive: true }));
12220
+ retryTransient(() => fs8.mkdirSync(dirs.tasks, { recursive: true }));
12221
+ if (!fs8.existsSync(dirs.marker))
12115
12222
  replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
12116
12223
  `, false);
12117
12224
  if (!dirs.shared) {
12118
- const ignore = path6.join(dirs.dir, ".gitignore");
12225
+ const ignore = path8.join(dirs.dir, ".gitignore");
12119
12226
  let current = null;
12120
12227
  try {
12121
- current = fs7.readFileSync(ignore, "utf8");
12228
+ current = fs8.readFileSync(ignore, "utf8");
12122
12229
  } catch {}
12123
12230
  if (current !== `*
12124
12231
  `)
12125
- retryTransient(() => fs7.writeFileSync(ignore, `*
12232
+ retryTransient(() => fs8.writeFileSync(ignore, `*
12126
12233
  `));
12127
12234
  }
12128
12235
  this.writeTombstoneIfMissing();
@@ -12138,7 +12245,7 @@ class TaskStore {
12138
12245
  shouldReclaim: ({ payload, nowMs }) => {
12139
12246
  let ageMs = null;
12140
12247
  try {
12141
- ageMs = nowMs - fs7.lstatSync(lockPath).mtimeMs;
12248
+ ageMs = nowMs - fs8.lstatSync(lockPath).mtimeMs;
12142
12249
  } catch {}
12143
12250
  return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
12144
12251
  },
@@ -12175,7 +12282,7 @@ class TaskStore {
12175
12282
  withLock(lockPath, operation) {
12176
12283
  try {
12177
12284
  this.initializeStorage();
12178
- fs7.mkdirSync(path6.dirname(lockPath), { recursive: true });
12285
+ fs8.mkdirSync(path8.dirname(lockPath), { recursive: true });
12179
12286
  } catch (error) {
12180
12287
  return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
12181
12288
  path: this.paths().dir
@@ -12215,18 +12322,18 @@ class TaskStore {
12215
12322
  lockDirWritable(lockPath) {
12216
12323
  const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
12217
12324
  try {
12218
- fs7.closeSync(fs7.openSync(probe, "wx", 384));
12325
+ fs8.closeSync(fs8.openSync(probe, "wx", 384));
12219
12326
  } catch {
12220
12327
  return false;
12221
12328
  }
12222
12329
  try {
12223
- fs7.rmSync(probe, { force: true });
12330
+ fs8.rmSync(probe, { force: true });
12224
12331
  } catch {}
12225
12332
  return true;
12226
12333
  }
12227
12334
  lockHolderPresent(lockPath) {
12228
12335
  try {
12229
- fs7.lstatSync(lockPath);
12336
+ fs8.lstatSync(lockPath);
12230
12337
  return true;
12231
12338
  } catch (error) {
12232
12339
  return error?.code !== "ENOENT";
@@ -12238,12 +12345,12 @@ class TaskStore {
12238
12345
  if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
12239
12346
  return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
12240
12347
  path: lockPath,
12241
- guidance: `Check permissions and the read-only attribute on ${path6.dirname(lockPath)}.`
12348
+ guidance: `Check permissions and the read-only attribute on ${path8.dirname(lockPath)}.`
12242
12349
  });
12243
12350
  if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
12244
12351
  let lock = null;
12245
12352
  try {
12246
- lock = parseMetadataLockOrNull(fs7.readFileSync(lockPath, "utf8"));
12353
+ lock = parseMetadataLockOrNull(fs8.readFileSync(lockPath, "utf8"));
12247
12354
  } catch {}
12248
12355
  return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
12249
12356
  outcome: "not_started",
@@ -12261,15 +12368,15 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
12261
12368
  var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
12262
12369
 
12263
12370
  // packages/workit-core/src/core/task-evaluation.ts
12264
- import { createHash as createHash9 } from "node:crypto";
12265
- import * as fs12 from "node:fs";
12371
+ import { createHash as createHash10 } from "node:crypto";
12372
+ import * as fs13 from "node:fs";
12266
12373
  import { spawnSync as spawnSync10 } from "node:child_process";
12267
- import path14 from "node:path";
12374
+ import path16 from "node:path";
12268
12375
 
12269
12376
  // packages/workit-core/src/check-config.ts
12270
12377
  import { spawnSync as spawnSync3 } from "node:child_process";
12271
- import fs8 from "node:fs";
12272
- import path7 from "node:path";
12378
+ import fs9 from "node:fs";
12379
+ import path9 from "node:path";
12273
12380
  var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
12274
12381
  var CHECKS_FILE = "workit.checks.json";
12275
12382
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -12322,10 +12429,10 @@ var git = (cwd, args) => {
12322
12429
  return value || null;
12323
12430
  };
12324
12431
  function checkRoot(cwd) {
12325
- return path7.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
12432
+ return path9.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
12326
12433
  }
12327
12434
  var packageManager = (root) => {
12328
- const has = (file) => fs8.existsSync(path7.join(root, file));
12435
+ const has = (file) => fs9.existsSync(path9.join(root, file));
12329
12436
  if (has("bun.lock") || has("bun.lockb"))
12330
12437
  return "bun";
12331
12438
  if (has("pnpm-lock.yaml"))
@@ -12345,7 +12452,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
12345
12452
  function detectedChecks(root) {
12346
12453
  const read = (file) => {
12347
12454
  try {
12348
- return fs8.readFileSync(path7.join(root, file), "utf8");
12455
+ return fs9.readFileSync(path9.join(root, file), "utf8");
12349
12456
  } catch {
12350
12457
  return null;
12351
12458
  }
@@ -12384,7 +12491,7 @@ function detectedChecks(root) {
12384
12491
  var GATES = ["testing", "verification"];
12385
12492
  function loadCheckConfig(cwd) {
12386
12493
  const root = checkRoot(cwd);
12387
- const file = path7.join(root, CHECKS_FILE);
12494
+ const file = path9.join(root, CHECKS_FILE);
12388
12495
  const none = (error = null) => ({
12389
12496
  source: error ? CHECKS_FILE : "none",
12390
12497
  root,
@@ -12394,7 +12501,7 @@ function loadCheckConfig(cwd) {
12394
12501
  });
12395
12502
  let text = null;
12396
12503
  try {
12397
- text = fs8.readFileSync(file, "utf8");
12504
+ text = fs9.readFileSync(file, "utf8");
12398
12505
  } catch (error) {
12399
12506
  if (error.code !== "ENOENT")
12400
12507
  return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
@@ -12457,10 +12564,10 @@ function workitAvailable(env = process.env) {
12457
12564
  return workitOnPath;
12458
12565
  const windows = process.platform === "win32";
12459
12566
  const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
12460
- const dirs = (env.PATH ?? env.Path ?? "").split(path7.delimiter).filter(Boolean);
12567
+ const dirs = (env.PATH ?? env.Path ?? "").split(path9.delimiter).filter(Boolean);
12461
12568
  workitOnPath = dirs.some((dir) => exts.some((ext) => {
12462
12569
  try {
12463
- return fs8.statSync(path7.join(dir, `workit${ext}`)).isFile();
12570
+ return fs9.statSync(path9.join(dir, `workit${ext}`)).isFile();
12464
12571
  } catch {
12465
12572
  return false;
12466
12573
  }
@@ -12473,10 +12580,10 @@ function checkCommand(args, version, onPath = workitAvailable()) {
12473
12580
 
12474
12581
  // packages/workit-core/src/git/rev.ts
12475
12582
  import { spawnSync as spawnSync4 } from "node:child_process";
12476
- import { createHash as createHash5 } from "node:crypto";
12477
- import fs9 from "node:fs";
12583
+ import { createHash as createHash6 } from "node:crypto";
12584
+ import fs10 from "node:fs";
12478
12585
  import os2 from "node:os";
12479
- import path8 from "node:path";
12586
+ import path10 from "node:path";
12480
12587
  var MAX_BUFFER = 256 * 1024 * 1024;
12481
12588
  var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
12482
12589
  var git2 = (cwd, args, options = {}) => {
@@ -12511,10 +12618,10 @@ function headSha(cwd) {
12511
12618
  var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
12512
12619
  var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
12513
12620
  var seedIndex = (from, to) => {
12514
- fs9.copyFileSync(from, to);
12515
- const { atimeNs, mtimeNs } = fs9.statSync(from, { bigint: true });
12621
+ fs10.copyFileSync(from, to);
12622
+ const { atimeNs, mtimeNs } = fs10.statSync(from, { bigint: true });
12516
12623
  const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
12517
- fs9.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
12624
+ fs10.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
12518
12625
  };
12519
12626
  function worktreeTree(cwd, options = {}) {
12520
12627
  const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
@@ -12523,10 +12630,10 @@ function worktreeTree(cwd, options = {}) {
12523
12630
  return null;
12524
12631
  const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
12525
12632
  const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
12526
- const scratch = fs9.mkdtempSync(path8.join(os2.tmpdir(), "workit-tree-"));
12633
+ const scratch = fs10.mkdtempSync(path10.join(os2.tmpdir(), "workit-tree-"));
12527
12634
  try {
12528
- const tempIndex = path8.join(scratch, "index");
12529
- if (fs9.existsSync(indexPath))
12635
+ const tempIndex = path10.join(scratch, "index");
12636
+ if (fs10.existsSync(indexPath))
12530
12637
  seedIndex(indexPath, tempIndex);
12531
12638
  const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
12532
12639
  const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
@@ -12538,7 +12645,7 @@ function worktreeTree(cwd, options = {}) {
12538
12645
  const skipped = [];
12539
12646
  for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
12540
12647
  try {
12541
- const stat = fs9.statSync(path8.join(top, file));
12648
+ const stat = fs10.statSync(path10.join(top, file));
12542
12649
  if (stat.isFile() && stat.size > cap)
12543
12650
  skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
12544
12651
  } catch {}
@@ -12555,10 +12662,10 @@ function worktreeTree(cwd, options = {}) {
12555
12662
  if (!tree)
12556
12663
  return null;
12557
12664
  const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
12558
- const key = skipped.length ? `sha256:${createHash5("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
12665
+ const key = skipped.length ? `sha256:${createHash6("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
12559
12666
  return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
12560
12667
  } finally {
12561
- fs9.rmSync(scratch, { recursive: true, force: true });
12668
+ fs10.rmSync(scratch, { recursive: true, force: true });
12562
12669
  }
12563
12670
  }
12564
12671
  function worktreeSignal(cwd, options = {}) {
@@ -12578,16 +12685,16 @@ function worktreeSignal(cwd, options = {}) {
12578
12685
  });
12579
12686
  if (!status.ok)
12580
12687
  return null;
12581
- const hash = createHash5("sha256");
12688
+ const hash = createHash6("sha256");
12582
12689
  hash.update(`${headSha(top) ?? "unborn"}\x00`);
12583
12690
  for (const entry of status.stdout.split("\x00")) {
12584
12691
  if (entry.length < 4)
12585
12692
  continue;
12586
12693
  const file = entry.slice(3);
12587
- const absolute = path8.join(top, file);
12694
+ const absolute = path10.join(top, file);
12588
12695
  let stat = "-";
12589
12696
  try {
12590
- const info = fs9.lstatSync(absolute, { bigint: true });
12697
+ const info = fs10.lstatSync(absolute, { bigint: true });
12591
12698
  stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
12592
12699
  if (info.isDirectory())
12593
12700
  stat += `:${headSha(absolute) ?? "-"}`;
@@ -12654,17 +12761,17 @@ function pushRemoteName(cwd, branch) {
12654
12761
 
12655
12762
  // packages/workit-core/src/core/policy/derive.ts
12656
12763
  import { existsSync as existsSync4 } from "node:fs";
12657
- import path13 from "node:path";
12764
+ import path15 from "node:path";
12658
12765
 
12659
12766
  // packages/workit-core/src/ledger.ts
12660
12767
  import { spawnSync as spawnSync9 } from "node:child_process";
12661
- import { createHash as createHash8, randomBytes as randomBytes2 } from "node:crypto";
12662
- import fs11 from "node:fs";
12663
- import path12 from "node:path";
12768
+ import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
12769
+ import fs12 from "node:fs";
12770
+ import path14 from "node:path";
12664
12771
 
12665
12772
  // packages/workit-core/src/core/vcs-config.ts
12666
- import fs10 from "node:fs";
12667
- import path11 from "node:path";
12773
+ import fs11 from "node:fs";
12774
+ import path13 from "node:path";
12668
12775
  import { spawnSync as spawnSync8 } from "node:child_process";
12669
12776
 
12670
12777
  // packages/workit-core/src/core/config.ts
@@ -12678,7 +12785,7 @@ import {
12678
12785
  writeFileSync as writeFileSync2
12679
12786
  } from "node:fs";
12680
12787
  import os3 from "node:os";
12681
- import path9 from "node:path";
12788
+ import path11 from "node:path";
12682
12789
 
12683
12790
  // packages/workit-core/src/core/boundary.ts
12684
12791
  import { statSync as statSync2 } from "node:fs";
@@ -12737,7 +12844,7 @@ var mergePreset = (preset, input = {}, current = {
12737
12844
  protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
12738
12845
  };
12739
12846
  };
12740
- var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path9.join(process.env.XDG_CONFIG_HOME || path9.join(os3.homedir(), ".config"), "workit");
12847
+ 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");
12741
12848
  var migratedDir = null;
12742
12849
  var migrationFailed = false;
12743
12850
  var ensureConfigDir = (dir = resolveConfigDir()) => {
@@ -12747,7 +12854,7 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
12747
12854
  migratedDir = dir;
12748
12855
  return dir;
12749
12856
  }
12750
- const legacy = path9.join(process.env.XDG_CONFIG_HOME || path9.join(os3.homedir(), ".config"), "workflow-toolkit");
12857
+ const legacy = path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workflow-toolkit");
12751
12858
  if (!existsSync3(legacy)) {
12752
12859
  migratedDir = dir;
12753
12860
  return dir;
@@ -12760,8 +12867,8 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
12760
12867
  mkdirSync2(dir, { recursive: true });
12761
12868
  diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
12762
12869
  for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
12763
- const src = path9.join(legacy, entry.name);
12764
- const dest = path9.join(dir, entry.name);
12870
+ const src = path11.join(legacy, entry.name);
12871
+ const dest = path11.join(dir, entry.name);
12765
12872
  if (existsSync3(dest))
12766
12873
  continue;
12767
12874
  try {
@@ -12833,7 +12940,7 @@ var parseConfigResult = (raw, file) => {
12833
12940
  return { status: "valid", path: file, config };
12834
12941
  };
12835
12942
  var readConfigTyped = (dir) => {
12836
- const file = path9.join(dir ?? configDir(), "config.json");
12943
+ const file = path11.join(dir ?? configDir(), "config.json");
12837
12944
  return parseConfigResult(readSafe(file), file);
12838
12945
  };
12839
12946
  var readConfig = () => {
@@ -12851,10 +12958,10 @@ var mergeConfigValues = (input, current) => ({
12851
12958
  var writeConfig = (config) => {
12852
12959
  const dir = configDir();
12853
12960
  mkdirSync2(dir, { recursive: true });
12854
- writeFileSync2(path9.join(dir, "config.json"), JSON.stringify(config, null, 2) + `
12961
+ writeFileSync2(path11.join(dir, "config.json"), JSON.stringify(config, null, 2) + `
12855
12962
  `, "utf8");
12856
12963
  };
12857
- var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path9.join(configDir(), "vcs.json");
12964
+ var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path11.join(configDir(), "vcs.json");
12858
12965
  var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
12859
12966
  var resolveBranchPolicy = (config, workspace, repoPreset) => {
12860
12967
  const wp = workspace?.branchPolicy ?? {};
@@ -12896,9 +13003,9 @@ var resolveCommitPolicy = (config, workspace) => {
12896
13003
 
12897
13004
  // packages/workit-core/src/core/workspaces.ts
12898
13005
  import { spawnSync as spawnSync6 } from "node:child_process";
12899
- import { createHash as createHash6 } from "node:crypto";
13006
+ import { createHash as createHash7 } from "node:crypto";
12900
13007
  import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
12901
- import path10 from "node:path";
13008
+ import path12 from "node:path";
12902
13009
 
12903
13010
  // packages/workit-core/src/core/branch-policy.ts
12904
13011
  import { spawnSync as spawnSync5 } from "node:child_process";
@@ -13033,7 +13140,7 @@ var releaseTrackSchema = object({
13033
13140
  context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
13034
13141
  }
13035
13142
  }
13036
- if (track.versionSource?.kind === "package-json" && (path10.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
13143
+ 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(".."))) {
13037
13144
  context.addIssue({
13038
13145
  code: "custom",
13039
13146
  path: ["versionSource", "path"],
@@ -13094,7 +13201,7 @@ var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).opt
13094
13201
  names.add(workspace.name);
13095
13202
  }
13096
13203
  });
13097
- var workspacesRevision = (contents) => createHash6("sha256").update(contents ?? "<missing>").digest("hex");
13204
+ var workspacesRevision = (contents) => createHash7("sha256").update(contents ?? "<missing>").digest("hex");
13098
13205
  var validateWorkspacesDocument = (parsed, file) => {
13099
13206
  if (!isConfigObject(parsed)) {
13100
13207
  return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
@@ -13132,7 +13239,7 @@ var validateWorkspacesDocument = (parsed, file) => {
13132
13239
  };
13133
13240
  };
13134
13241
  var readWorkspacesResult = (dir = configDir()) => {
13135
- const file = path10.join(dir, "workspaces.json");
13242
+ const file = path12.join(dir, "workspaces.json");
13136
13243
  let raw;
13137
13244
  try {
13138
13245
  raw = readFileSync3(file, "utf8");
@@ -13243,9 +13350,9 @@ var mainCheckoutOf = (cwd) => {
13243
13350
  let main = null;
13244
13351
  const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
13245
13352
  const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
13246
- if (gitDir && common && path10.resolve(gitDir) !== path10.resolve(common)) {
13353
+ if (gitDir && common && path12.resolve(gitDir) !== path12.resolve(common)) {
13247
13354
  const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
13248
- main = listed?.trim() || (path10.basename(common) === ".git" ? path10.dirname(common) : common);
13355
+ main = listed?.trim() || (path12.basename(common) === ".git" ? path12.dirname(common) : common);
13249
13356
  }
13250
13357
  mainCheckouts.set(cwd, main);
13251
13358
  return main;
@@ -13336,7 +13443,7 @@ var runtimeWorkspaceIndexSchema = object({
13336
13443
  workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
13337
13444
  }).passthrough();
13338
13445
  var matchRuntimeWorkspace = (cwd, dir = configDir()) => {
13339
- const file = path10.join(dir, "workspaces.json");
13446
+ const file = path12.join(dir, "workspaces.json");
13340
13447
  let raw;
13341
13448
  try {
13342
13449
  raw = readFileSync3(file, "utf8");
@@ -13411,7 +13518,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
13411
13518
  }
13412
13519
 
13413
13520
  // packages/workit-core/src/core/release-tracks.ts
13414
- import { createHash as createHash7 } from "node:crypto";
13521
+ import { createHash as createHash8 } from "node:crypto";
13415
13522
  import { spawnSync as spawnSync7 } from "node:child_process";
13416
13523
  class ReleaseTrackError extends Error {
13417
13524
  }
@@ -13794,7 +13901,7 @@ function resolveTrackFor(cwd, options) {
13794
13901
  ]) ?? "",
13795
13902
  git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
13796
13903
  ] : [];
13797
- key = createHash7("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
13904
+ key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
13798
13905
  const hit = memo2.get(key);
13799
13906
  if (hit)
13800
13907
  return hit;
@@ -13809,7 +13916,7 @@ function resolveTrackFor(cwd, options) {
13809
13916
  }
13810
13917
 
13811
13918
  // packages/workit-core/src/core/vcs-config.ts
13812
- var workspacesPath = () => path11.join(configDir(), "workspaces.json");
13919
+ var workspacesPath = () => path13.join(configDir(), "workspaces.json");
13813
13920
  var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
13814
13921
  var remoteProvider = (cwd) => {
13815
13922
  const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
@@ -13899,7 +14006,7 @@ var readVcsConfig = () => {
13899
14006
  const file = vcsConfigPath();
13900
14007
  let raw;
13901
14008
  try {
13902
- raw = fs10.readFileSync(file, "utf8");
14009
+ raw = fs11.readFileSync(file, "utf8");
13903
14010
  } catch {
13904
14011
  return { status: "missing", path: file, config: {} };
13905
14012
  }
@@ -13984,7 +14091,7 @@ function vcsConfig(mode, cwd, options = {}) {
13984
14091
  const prov = cfg[provider] ?? {};
13985
14092
  const out = {
13986
14093
  ok: true,
13987
- configPath: path11.resolve(cfgPath),
14094
+ configPath: path13.resolve(cfgPath),
13988
14095
  provider,
13989
14096
  defaultTargetBranch: workspaceDefault,
13990
14097
  pr: cfg.pr ?? {},
@@ -14195,7 +14302,7 @@ function diffHash(cwd, base, head) {
14195
14302
  ], GIT_TIMEOUTS.worktree);
14196
14303
  if (!diff.ok || !diff.stdout)
14197
14304
  return null;
14198
- const hash = createHash8("sha256");
14305
+ const hash = createHash9("sha256");
14199
14306
  for (const line of diff.stdout.split(`
14200
14307
  `)) {
14201
14308
  if (line.startsWith("index "))
@@ -14207,16 +14314,16 @@ function diffHash(cwd, base, head) {
14207
14314
  return `sha256:${hash.digest("hex")}`;
14208
14315
  }
14209
14316
  function storeRoot(cwd) {
14210
- const location = resolveStore(path12.resolve(cwd));
14317
+ const location = resolveStore(path14.resolve(cwd));
14211
14318
  if (location instanceof Error)
14212
14319
  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");
14213
14320
  return { ok: true, value: { root: location.dir, shared: location.shared } };
14214
14321
  }
14215
14322
  function ledgerPath(cwd) {
14216
14323
  const root = storeRoot(cwd);
14217
- return root.ok ? { ok: true, value: path12.join(root.value.root, "ledger", "ledger.jsonl") } : root;
14324
+ return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
14218
14325
  }
14219
- var newId2 = () => `${Date.now().toString(36)}-${randomBytes2(6).toString("hex")}`;
14326
+ var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
14220
14327
  var NETWORK_FS_TYPES = new Set([
14221
14328
  26985,
14222
14329
  20859,
@@ -14234,7 +14341,7 @@ var onNetworkFs = (dir) => {
14234
14341
  if (process.platform !== "linux")
14235
14342
  return false;
14236
14343
  try {
14237
- return isNetworkFsType(fs11.statfsSync(dir).type);
14344
+ return isNetworkFsType(fs12.statfsSync(dir).type);
14238
14345
  } catch {
14239
14346
  return false;
14240
14347
  }
@@ -14244,15 +14351,15 @@ var LOCK_STALE_MS = 30000;
14244
14351
  var LOCK_OWNER = "owner";
14245
14352
  var lockToken = (lock) => {
14246
14353
  try {
14247
- return fs11.readFileSync(path12.join(lock, LOCK_OWNER), "utf8");
14354
+ return fs12.readFileSync(path14.join(lock, LOCK_OWNER), "utf8");
14248
14355
  } catch {
14249
14356
  return null;
14250
14357
  }
14251
14358
  };
14252
14359
  var lockAgeMs = (lock) => {
14253
- for (const target of [path12.join(lock, LOCK_OWNER), lock]) {
14360
+ for (const target of [path14.join(lock, LOCK_OWNER), lock]) {
14254
14361
  try {
14255
- return Date.now() - fs11.statSync(target).mtimeMs;
14362
+ return Date.now() - fs12.statSync(target).mtimeMs;
14256
14363
  } catch {}
14257
14364
  }
14258
14365
  return null;
@@ -14260,55 +14367,55 @@ var lockAgeMs = (lock) => {
14260
14367
  var ledgerLock = {
14261
14368
  acquire(lock) {
14262
14369
  try {
14263
- fs11.mkdirSync(lock);
14370
+ fs12.mkdirSync(lock);
14264
14371
  } catch (error) {
14265
14372
  if (error.code === "EEXIST")
14266
14373
  return null;
14267
14374
  throw error;
14268
14375
  }
14269
- const token = `${process.pid}-${randomBytes2(8).toString("hex")}`;
14270
- fs11.writeFileSync(path12.join(lock, LOCK_OWNER), token);
14376
+ const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
14377
+ fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
14271
14378
  return token;
14272
14379
  },
14273
14380
  release(lock, token) {
14274
14381
  if (lockToken(lock) !== token)
14275
14382
  return false;
14276
- const tomb = `${lock}.release-${randomBytes2(6).toString("hex")}`;
14383
+ const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
14277
14384
  try {
14278
- fs11.renameSync(lock, tomb);
14385
+ fs12.renameSync(lock, tomb);
14279
14386
  } catch {
14280
14387
  return false;
14281
14388
  }
14282
14389
  if (lockToken(tomb) !== token) {
14283
14390
  try {
14284
- fs11.renameSync(tomb, lock);
14391
+ fs12.renameSync(tomb, lock);
14285
14392
  } catch {}
14286
14393
  return false;
14287
14394
  }
14288
- fs11.rmSync(tomb, { recursive: true, force: true });
14395
+ fs12.rmSync(tomb, { recursive: true, force: true });
14289
14396
  return true;
14290
14397
  },
14291
14398
  token: lockToken,
14292
14399
  reap(lock, observed) {
14293
- const tomb = `${lock}.reap-${randomBytes2(6).toString("hex")}`;
14400
+ const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
14294
14401
  try {
14295
- fs11.renameSync(lock, tomb);
14402
+ fs12.renameSync(lock, tomb);
14296
14403
  } catch {
14297
14404
  return false;
14298
14405
  }
14299
14406
  if (lockToken(tomb) === observed) {
14300
- fs11.rmSync(tomb, { recursive: true, force: true });
14407
+ fs12.rmSync(tomb, { recursive: true, force: true });
14301
14408
  return true;
14302
14409
  }
14303
14410
  try {
14304
- fs11.renameSync(tomb, lock);
14411
+ fs12.renameSync(tomb, lock);
14305
14412
  } catch {
14306
- fs11.rmSync(tomb, { recursive: true, force: true });
14413
+ fs12.rmSync(tomb, { recursive: true, force: true });
14307
14414
  }
14308
14415
  return false;
14309
14416
  }
14310
14417
  };
14311
- function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
14418
+ function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
14312
14419
  const lock = `${file}.lock`;
14313
14420
  const deadline = Date.now() + waitMs;
14314
14421
  let token = null;
@@ -14330,14 +14437,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
14330
14437
  ledgerLock.release(lock, token);
14331
14438
  }
14332
14439
  }
14333
- var sha2562 = (text) => createHash8("sha256").update(text).digest("hex");
14440
+ var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
14334
14441
  var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
14335
14442
  ${body}`);
14336
14443
  var TAIL_BYTES = 64 * 1024;
14337
14444
  var lastLine = (fd, size) => {
14338
14445
  for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
14339
14446
  const buffer = Buffer.alloc(span);
14340
- fs11.readSync(fd, buffer, 0, span, size - span);
14447
+ fs12.readSync(fd, buffer, 0, span, size - span);
14341
14448
  const end = buffer.lastIndexOf(10);
14342
14449
  if (end < 0) {
14343
14450
  if (span === size)
@@ -14366,15 +14473,23 @@ var lastLine = (fd, size) => {
14366
14473
  }
14367
14474
  return null;
14368
14475
  };
14476
+ var tighten = (fd) => {
14477
+ try {
14478
+ if ((fs12.fstatSync(fd).mode & 63) !== 0)
14479
+ fs12.fchmodSync(fd, 384);
14480
+ } catch {}
14481
+ };
14369
14482
  function appendLine(file, build, options) {
14370
14483
  const write = () => {
14371
- const fd = fs11.openSync(file, "a+");
14484
+ const fd = fs12.openSync(file, "a+", 384);
14372
14485
  try {
14373
- const size = fs11.fstatSync(fd).size;
14486
+ if (process.platform !== "win32")
14487
+ tighten(fd);
14488
+ const size = fs12.fstatSync(fd).size;
14374
14489
  let fence = "";
14375
14490
  if (size > 0) {
14376
14491
  const last = Buffer.alloc(1);
14377
- fs11.readSync(fd, last, 0, 1, size - 1);
14492
+ fs12.readSync(fd, last, 0, 1, size - 1);
14378
14493
  if (last[0] !== 10)
14379
14494
  fence = `
14380
14495
  `;
@@ -14383,17 +14498,17 @@ function appendLine(file, build, options) {
14383
14498
  if (!line.ok)
14384
14499
  return line;
14385
14500
  const bytes = Buffer.from(fence + line.value);
14386
- const written = fs11.writeSync(fd, bytes);
14501
+ const written = fs12.writeSync(fd, bytes);
14387
14502
  if (written !== bytes.length)
14388
14503
  return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
14389
14504
  return { ok: true, value: undefined };
14390
14505
  } finally {
14391
- fs11.closeSync(fd);
14506
+ fs12.closeSync(fd);
14392
14507
  }
14393
14508
  };
14394
14509
  try {
14395
- fs11.mkdirSync(path12.dirname(file), { recursive: true });
14396
- if (!options.forceLock && !onNetworkFs(path12.dirname(file)))
14510
+ fs12.mkdirSync(path14.dirname(file), { recursive: true });
14511
+ if (!options.forceLock && !onNetworkFs(path14.dirname(file)))
14397
14512
  return write();
14398
14513
  const locked = withLedgerLock(file, write);
14399
14514
  return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
@@ -14464,8 +14579,10 @@ function readLedger(cwd, options = {}) {
14464
14579
  if (!file.ok)
14465
14580
  return file;
14466
14581
  let text = "";
14582
+ let stat = null;
14467
14583
  try {
14468
- text = fs11.readFileSync(file.value, "utf8");
14584
+ stat = fs12.statSync(file.value);
14585
+ text = fs12.readFileSync(file.value, "utf8");
14469
14586
  } catch (error) {
14470
14587
  if (error.code === "ENOENT")
14471
14588
  return {
@@ -14484,6 +14601,7 @@ function readLedger(cwd, options = {}) {
14484
14601
  const integrity = { chained: 0, legacy: 0, unverified: [] };
14485
14602
  const earlier = new Set([null]);
14486
14603
  let chainStarted = false;
14604
+ const ids = new Set;
14487
14605
  for (const raw of text.split(`
14488
14606
  `)) {
14489
14607
  const trimmed = raw.trim();
@@ -14517,6 +14635,10 @@ function readLedger(cwd, options = {}) {
14517
14635
  reason = "unsigned";
14518
14636
  else
14519
14637
  rowIntegrity = "legacy";
14638
+ if (options.integrity && !reason && ids.has(parsed.id))
14639
+ reason = "duplicate_id";
14640
+ if (options.integrity)
14641
+ ids.add(parsed.id);
14520
14642
  if (reason)
14521
14643
  rowIntegrity = "unverified";
14522
14644
  const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
@@ -14540,7 +14662,8 @@ function readLedger(cwd, options = {}) {
14540
14662
  seq: rows.length + 1,
14541
14663
  superseded: false,
14542
14664
  supersedeIgnored: false,
14543
- ...rowIntegrity ? { integrity: rowIntegrity } : {}
14665
+ ...rowIntegrity ? { integrity: rowIntegrity } : {},
14666
+ ...reason ? { integrityReason: reason } : {}
14544
14667
  };
14545
14668
  rows.push(row);
14546
14669
  if (!options.integrity)
@@ -14568,18 +14691,48 @@ function readLedger(cwd, options = {}) {
14568
14691
  const byId = new Map;
14569
14692
  for (const row of rows) {
14570
14693
  const target = row.supersedes ? byId.get(row.supersedes) : undefined;
14571
- if (target && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
14694
+ if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
14572
14695
  target.superseded = true;
14573
14696
  else if (row.supersedes)
14574
14697
  row.supersedeIgnored = true;
14575
14698
  if (!byId.has(row.id))
14576
14699
  byId.set(row.id, row);
14577
14700
  }
14701
+ if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
14702
+ tamperCache.set(file.value, {
14703
+ size: stat.size,
14704
+ stamp: fileStamp(stat),
14705
+ breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
14706
+ });
14578
14707
  return {
14579
14708
  ok: true,
14580
14709
  value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
14581
14710
  };
14582
14711
  }
14712
+ var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
14713
+ var tamperCache = new Map;
14714
+ var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
14715
+ function ledgerTampering(cwd) {
14716
+ const file = ledgerPath(cwd);
14717
+ if (!file.ok)
14718
+ return [];
14719
+ try {
14720
+ const stat = fs12.statSync(file.value);
14721
+ const cached = tamperCache.get(file.value);
14722
+ if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
14723
+ return cached.breaks;
14724
+ } catch {
14725
+ return [];
14726
+ }
14727
+ const read = readLedger(cwd, { integrity: true });
14728
+ return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
14729
+ }
14730
+ function tamperedUnblock(cwd) {
14731
+ const file = ledgerPath(cwd);
14732
+ const where = file.ok ? file.value : "the ledger file";
14733
+ return `workit ledger verify-integrity # lists the edited or missing rows; if the edit is understood, move ${where} aside (mv ${where} ${where}.tampered-<date>) and record the verdicts again`;
14734
+ }
14735
+ var tamperedWarning = (cwd, breaks) => `the ledger was edited, or a row was removed or moved (${breaks.slice(0, 3).map((row) => `${row.reason} at row ${row.seq}`).join(", ")}${breaks.length > 3 ? ", …" : ""}): no verdict is accepted until it is repaired. ${tamperedUnblock(cwd)}`;
14583
14736
  function verdictBasis(verdict, current) {
14584
14737
  if (verdict.head && verdict.head === current.head)
14585
14738
  return "fresh";
@@ -14587,7 +14740,7 @@ function verdictBasis(verdict, current) {
14587
14740
  return "carried";
14588
14741
  return "stale";
14589
14742
  }
14590
- var isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.superseded;
14743
+ var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
14591
14744
  function trailerSessions(cwd, base, head) {
14592
14745
  if (!base || !head || !safeRef(base) || !safeRef(head))
14593
14746
  return [];
@@ -14668,6 +14821,56 @@ var authoredBy = (authors, session) => {
14668
14821
  return true;
14669
14822
  return false;
14670
14823
  };
14824
+ var TRUST_REASONS = new Set([
14825
+ "unsigned",
14826
+ "hash_mismatch",
14827
+ "broken_link",
14828
+ "duplicate_id",
14829
+ "unissued_session",
14830
+ "unbound_session"
14831
+ ]);
14832
+ var SHELL_VOUCHED_HOSTS = new Set([
14833
+ "cli",
14834
+ "opencode",
14835
+ "pi",
14836
+ "codex_cli",
14837
+ "codex_desktop"
14838
+ ]);
14839
+ function sessionTrust(cwd, row) {
14840
+ const session = row.actor.session;
14841
+ if (!session || row.self === true)
14842
+ return null;
14843
+ const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
14844
+ const host = row.actor.host;
14845
+ const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
14846
+ if (!issuedOnly)
14847
+ return null;
14848
+ const binding = issuedBinding(cwd, session);
14849
+ if (binding === null)
14850
+ return "unissued_session";
14851
+ return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
14852
+ }
14853
+ function trustReasons(cwd, row) {
14854
+ if (row.integrity === "unverified")
14855
+ return [row.integrityReason ?? "unsigned"];
14856
+ if (row.integrity !== "verified")
14857
+ return ["unsigned"];
14858
+ const session = sessionTrust(cwd, row);
14859
+ return session ? [session] : [];
14860
+ }
14861
+ function withIntegrity(cwd, rows) {
14862
+ if (rows.every((row) => row.integrity !== undefined))
14863
+ return rows;
14864
+ const read = readLedger(cwd, { integrity: true });
14865
+ const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
14866
+ return rows.map((row) => {
14867
+ if (row.integrity !== undefined)
14868
+ return row;
14869
+ const fresh = bySeq.get(row.seq);
14870
+ return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
14871
+ });
14872
+ }
14873
+ var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
14671
14874
  var independenceReasons = (row, authors) => {
14672
14875
  const reasons = [];
14673
14876
  if (!row.actor.session)
@@ -14678,15 +14881,17 @@ var independenceReasons = (row, authors) => {
14678
14881
  reasons.push("self");
14679
14882
  return reasons;
14680
14883
  };
14681
- function effectiveOf(rows, authors, sameCode, isCurrent) {
14884
+ function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
14682
14885
  let effective = null;
14683
14886
  for (const row of rows) {
14684
14887
  if (!effective) {
14685
14888
  effective = row;
14686
14889
  continue;
14687
14890
  }
14688
- const rowIndependent = independenceReasons(row, authors).length === 0;
14689
- const effIndependent = independenceReasons(effective, authors).length === 0;
14891
+ if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
14892
+ continue;
14893
+ const rowIndependent = reasonsOf(row).length === 0;
14894
+ const effIndependent = reasonsOf(effective).length === 0;
14690
14895
  if (effIndependent && !rowIndependent && isCurrent(effective))
14691
14896
  continue;
14692
14897
  if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
@@ -14700,7 +14905,7 @@ function restackCarries(rows, branch, from, to) {
14700
14905
  return false;
14701
14906
  const edges = new Map;
14702
14907
  for (const row of rows)
14703
- if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
14908
+ if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
14704
14909
  edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
14705
14910
  const seen = new Set([from]);
14706
14911
  const queue = [from];
@@ -14716,7 +14921,8 @@ function restackCarries(rows, branch, from, to) {
14716
14921
  }
14717
14922
  return false;
14718
14923
  }
14719
- function checkVerdicts(cwd, branch, rows) {
14924
+ function checkVerdicts(cwd, branch, ledgerRows) {
14925
+ const rows = withIntegrity(cwd, ledgerRows);
14720
14926
  const head = branchHead(cwd, branch);
14721
14927
  const fallbackBase = defaultBase(cwd);
14722
14928
  const scope = branchScope(cwd, branch);
@@ -14746,21 +14952,47 @@ function checkVerdicts(cwd, branch, rows) {
14746
14952
  return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
14747
14953
  };
14748
14954
  const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
14955
+ const failureAuthorCache = new Map;
14956
+ const failureAuthors = (row) => {
14957
+ let set = failureAuthorCache.get(row.seq);
14958
+ if (!set) {
14959
+ set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
14960
+ failureAuthorCache.set(row.seq, set);
14961
+ }
14962
+ return set;
14963
+ };
14964
+ const reasonCache = new Map;
14965
+ const reasonsOf = (row) => {
14966
+ let reasons = reasonCache.get(row);
14967
+ if (!reasons) {
14968
+ const failing = FAILING.has(String(row.result));
14969
+ reasons = [
14970
+ ...trustReasons(cwd, row),
14971
+ ...independenceReasons(row, failing ? failureAuthors(row) : authors)
14972
+ ];
14973
+ reasonCache.set(row, reasons);
14974
+ }
14975
+ return reasons;
14976
+ };
14977
+ const retired = new Set;
14978
+ for (const row of rows)
14979
+ if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
14980
+ retired.add(row.supersedes);
14749
14981
  const byKind = new Map;
14750
14982
  for (const row of rows)
14751
- if (isLiveVerdict(row) && scope(row)) {
14983
+ if (isLiveVerdict(row, retired) && scope(row)) {
14752
14984
  const kind = typeof row.kind === "string" ? row.kind : "review";
14753
14985
  byKind.set(kind, [...byKind.get(kind) ?? [], row]);
14754
14986
  }
14755
14987
  const verdicts = [];
14756
14988
  const warnings = [];
14757
14989
  for (const [kind, list] of byKind) {
14758
- const row = effectiveOf(list, authors, sameCode, (candidate) => basisOf(candidate) !== "stale");
14990
+ const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
14759
14991
  if (!row)
14760
14992
  continue;
14761
14993
  const basis = basisOf(row);
14762
14994
  const current = basis !== "stale";
14763
- const independence = independenceReasons(row, authors);
14995
+ const independence = reasonsOf(row);
14764
14996
  const reasons = [
14765
14997
  ...current ? [] : ["stale"],
14766
14998
  ...PASSING.has(String(row.result)) ? [] : ["not_passing"],
@@ -14777,23 +15009,31 @@ function checkVerdicts(cwd, branch, rows) {
14777
15009
  });
14778
15010
  if (reasons.length === 0) {
14779
15011
  for (const later of list)
14780
- if (later.seq > row.seq && independenceReasons(later, authors).length > 0 && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
15012
+ if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
14781
15013
  warnings.push(`the author's own (self) ${String(later.result)} ${kind} verdict ${later.id} by ${later.actor.session ?? "no session"} on the current code is newer than the accepted pass ${row.id}: read it before merging`);
14782
15014
  }
14783
15015
  }
14784
15016
  for (const row of rows)
14785
15017
  if (row.integrity === "unverified" && scope(row))
14786
15018
  warnings.push(`ledger row ${row.id} (${row.type}) on ${row.branch ?? branch} is unverified: the workit CLI did not write it or it changed afterwards (workit ledger verify-integrity)`);
15019
+ const tampered = ledgerTampering(cwd);
15020
+ if (tampered.length) {
15021
+ warnings.push(tamperedWarning(cwd, tampered));
15022
+ for (const entry of verdicts) {
15023
+ entry.accepted = false;
15024
+ entry.reasons = [...entry.reasons, "ledger_tampered"];
15025
+ }
15026
+ }
14787
15027
  verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
14788
15028
  const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
14789
15029
  const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
14790
15030
  const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
14791
- const accepted = newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
15031
+ const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
14792
15032
  accepted: false,
14793
15033
  verdict: failing?.verdict ?? null,
14794
15034
  reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
14795
15035
  };
14796
- const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
15036
+ const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
14797
15037
  return {
14798
15038
  branch,
14799
15039
  head,
@@ -14956,7 +15196,7 @@ var productDecision = (task, ledger) => {
14956
15196
  };
14957
15197
  };
14958
15198
  var planRecorded = (task, root) => {
14959
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path13.resolve(root, ref.path)));
15199
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path15.resolve(root, ref.path)));
14960
15200
  if (cited)
14961
15201
  return {
14962
15202
  met: true,
@@ -15015,15 +15255,15 @@ function writeBlockers(task, root) {
15015
15255
  }
15016
15256
 
15017
15257
  // packages/workit-core/src/core/task-evaluation.ts
15018
- var digestBytes = (value) => createHash9("sha256").update(value).digest("hex");
15019
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path14.sep}`);
15020
- var relative = (root, candidate) => path14.relative(root, candidate).split(path14.sep).join("/") || ".";
15258
+ var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
15259
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
15260
+ var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
15021
15261
  var canonicalPath = (value) => {
15022
15262
  if (typeof value !== "string" || !value)
15023
15263
  return null;
15024
15264
  if (value.includes("\\") || value.split("/").includes(".."))
15025
15265
  return null;
15026
- const normalized = path14.posix.normalize(value);
15266
+ const normalized = path16.posix.normalize(value);
15027
15267
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
15028
15268
  return null;
15029
15269
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -15071,21 +15311,21 @@ var gitPaths = (root) => {
15071
15311
  git: !/not a git repository/i.test(stderr)
15072
15312
  };
15073
15313
  }
15074
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path14.sep).join("/")) : [];
15314
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
15075
15315
  const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
15076
15316
  if (stagedDeleted.status !== 0) {
15077
15317
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
15078
15318
  if (!/not a git repository/i.test(stderr))
15079
15319
  return { paths, uncertain: true, git: true };
15080
15320
  } else if (stagedDeleted.stdout) {
15081
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path14.sep).join("/")));
15321
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
15082
15322
  }
15083
15323
  return { paths: [...new Set(paths)], uncertain: false, git: true };
15084
15324
  };
15085
15325
  var walk = (root, directory, output) => {
15086
15326
  let names;
15087
15327
  try {
15088
- names = fs12.readdirSync(directory);
15328
+ names = fs13.readdirSync(directory);
15089
15329
  } catch {
15090
15330
  return true;
15091
15331
  }
@@ -15093,10 +15333,10 @@ var walk = (root, directory, output) => {
15093
15333
  for (const name of names) {
15094
15334
  if (name === ".git" || name === ".workit")
15095
15335
  continue;
15096
- const target = path14.join(directory, name);
15336
+ const target = path16.join(directory, name);
15097
15337
  let stat;
15098
15338
  try {
15099
- stat = fs12.lstatSync(target);
15339
+ stat = fs13.lstatSync(target);
15100
15340
  } catch {
15101
15341
  uncertain = true;
15102
15342
  continue;
@@ -15111,14 +15351,14 @@ var walk = (root, directory, output) => {
15111
15351
  var scopeRoots = (root, scope) => {
15112
15352
  const roots = [];
15113
15353
  for (const item of scope.paths.length ? scope.paths : ["."]) {
15114
- const target = path14.resolve(root, item);
15354
+ const target = path16.resolve(root, item);
15115
15355
  if (!inside(root, target))
15116
15356
  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` }] });
15117
15357
  let ancestor = target;
15118
- while (!fs12.existsSync(ancestor) && ancestor !== root)
15119
- ancestor = path14.dirname(ancestor);
15358
+ while (!fs13.existsSync(ancestor) && ancestor !== root)
15359
+ ancestor = path16.dirname(ancestor);
15120
15360
  try {
15121
- if (!inside(root, fs12.realpathSync(ancestor)))
15361
+ if (!inside(root, fs13.realpathSync(ancestor)))
15122
15362
  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` }] });
15123
15363
  } catch {
15124
15364
  return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
@@ -15130,7 +15370,7 @@ var scopeRoots = (root, scope) => {
15130
15370
  function captureCandidate(root, scope, environment = []) {
15131
15371
  let checkout;
15132
15372
  try {
15133
- checkout = fs12.realpathSync(root);
15373
+ checkout = fs13.realpathSync(root);
15134
15374
  } catch {
15135
15375
  return failure2("invalid_input", "candidate root does not exist");
15136
15376
  }
@@ -15148,11 +15388,11 @@ function captureCandidate(root, scope, environment = []) {
15148
15388
  if (item === ".git" || item === ".workit")
15149
15389
  continue;
15150
15390
  names.add(item);
15151
- if (!fs12.existsSync(target))
15391
+ if (!fs13.existsSync(target))
15152
15392
  continue;
15153
15393
  let stat;
15154
15394
  try {
15155
- stat = fs12.lstatSync(target);
15395
+ stat = fs13.lstatSync(target);
15156
15396
  } catch {
15157
15397
  uncertain = true;
15158
15398
  continue;
@@ -15168,17 +15408,17 @@ function captureCandidate(root, scope, environment = []) {
15168
15408
  for (const item of [...names].toSorted()) {
15169
15409
  if (!scopeMatches(item, normalizedScope))
15170
15410
  continue;
15171
- const target = path14.join(checkout, item);
15411
+ const target = path16.join(checkout, item);
15172
15412
  try {
15173
- const stat = fs12.lstatSync(target);
15413
+ const stat = fs13.lstatSync(target);
15174
15414
  if (stat.isSymbolicLink()) {
15175
- const link = fs12.readlinkSync(target);
15415
+ const link = fs13.readlinkSync(target);
15176
15416
  files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
15177
15417
  } else if (stat.isFile()) {
15178
15418
  files.push({
15179
15419
  path: item,
15180
15420
  kind: "file",
15181
- digest: digestBytes(fs12.readFileSync(target)),
15421
+ digest: digestBytes(fs13.readFileSync(target)),
15182
15422
  executable: (stat.mode & 73) !== 0
15183
15423
  });
15184
15424
  } else if (!stat.isDirectory()) {
@@ -15343,7 +15583,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
15343
15583
  var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15344
15584
  let root;
15345
15585
  try {
15346
- root = fs12.realpathSync(checkoutRoot);
15586
+ root = fs13.realpathSync(checkoutRoot);
15347
15587
  } catch {
15348
15588
  return failure2("permission_denied", "checkout root is unavailable");
15349
15589
  }
@@ -15352,23 +15592,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15352
15592
  continue;
15353
15593
  if (!reference.digest)
15354
15594
  return failure2("invalid_input", "document references require a byte digest");
15355
- const target = path14.resolve(root, reference.path);
15356
- if (target !== root && !target.startsWith(`${root}${path14.sep}`))
15595
+ const target = path16.resolve(root, reference.path);
15596
+ if (target !== root && !target.startsWith(`${root}${path16.sep}`))
15357
15597
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
15358
15598
  fields: [
15359
15599
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
15360
15600
  ]
15361
15601
  });
15362
15602
  try {
15363
- const below = path14.relative(root, target);
15603
+ const below = path16.relative(root, target);
15364
15604
  let current = root;
15365
- for (const segment of below.split(path14.sep)) {
15605
+ for (const segment of below.split(path16.sep)) {
15366
15606
  if (!segment)
15367
15607
  continue;
15368
- current = path14.join(current, segment);
15369
- const stat = fs12.lstatSync(current);
15370
- const real = fs12.realpathSync(current);
15371
- if (real !== root && !real.startsWith(`${root}${path14.sep}`))
15608
+ current = path16.join(current, segment);
15609
+ const stat = fs13.lstatSync(current);
15610
+ const real = fs13.realpathSync(current);
15611
+ if (real !== root && !real.startsWith(`${root}${path16.sep}`))
15372
15612
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
15373
15613
  fields: [
15374
15614
  {
@@ -15384,7 +15624,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15384
15624
  if (current !== target && !stat.isDirectory())
15385
15625
  return failure2("invalid_input", "document reference ancestor is not a directory");
15386
15626
  }
15387
- if (digestBytes(fs12.readFileSync(target)) !== reference.digest)
15627
+ if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
15388
15628
  return failure2("permission_denied", "decision document bytes have changed");
15389
15629
  } catch {
15390
15630
  return failure2("invalid_input", "decision document is unavailable");
@@ -15916,17 +16156,17 @@ var exportDigest = (bundle) => sha256({
15916
16156
  // packages/workit-core/src/core/route-intent.ts
15917
16157
  import { spawnSync as spawnSync12 } from "node:child_process";
15918
16158
  import { existsSync as existsSync6 } from "node:fs";
15919
- import path17 from "node:path";
16159
+ import path19 from "node:path";
15920
16160
 
15921
16161
  // packages/workit-core/src/hooks/raw-git.ts
15922
16162
  import { spawnSync as spawnSync11 } from "node:child_process";
15923
- import { createHash as createHash12 } from "node:crypto";
15924
- import fs14 from "node:fs";
16163
+ import { createHash as createHash13 } from "node:crypto";
16164
+ import fs15 from "node:fs";
15925
16165
  import os4 from "node:os";
15926
- import path16 from "node:path";
16166
+ import path18 from "node:path";
15927
16167
 
15928
16168
  // packages/workit-core/src/core/branch.ts
15929
- import { createHash as createHash10 } from "node:crypto";
16169
+ import { createHash as createHash11 } from "node:crypto";
15930
16170
 
15931
16171
  // packages/workit-core/src/core/git.ts
15932
16172
  import { execFileSync } from "node:child_process";
@@ -16120,7 +16360,7 @@ var branchPolicySnapshotFor = (workspaceRoot) => {
16120
16360
  const source = resolved.provenance.branchPolicy;
16121
16361
  const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
16122
16362
  const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
16123
- const fingerprint = createHash10("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
16363
+ const fingerprint = createHash11("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
16124
16364
  return {
16125
16365
  branchPolicy,
16126
16366
  namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
@@ -16174,9 +16414,9 @@ var releaseBranchesOf = (cwd) => {
16174
16414
  var releaseBranch = (cwd, branch) => releaseBranchesOf(cwd).branches.some((name) => name.toLowerCase() === branch.toLowerCase());
16175
16415
 
16176
16416
  // packages/workit-core/src/hooks/session-marker.ts
16177
- import { createHash as createHash11 } from "node:crypto";
16178
- import fs13 from "node:fs";
16179
- import path15 from "node:path";
16417
+ import { createHash as createHash12 } from "node:crypto";
16418
+ import fs14 from "node:fs";
16419
+ import path17 from "node:path";
16180
16420
  var sessionMarkerFile = (input, session, store) => {
16181
16421
  let dir = store;
16182
16422
  if (dir === undefined) {
@@ -16185,13 +16425,13 @@ var sessionMarkerFile = (input, session, store) => {
16185
16425
  return null;
16186
16426
  dir = location.dir;
16187
16427
  }
16188
- const name = createHash11("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
16189
- return path15.join(dir, "hooks", `skills-${name}.json`);
16428
+ const name = createHash12("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
16429
+ return path17.join(dir, "hooks", `skills-${name}.json`);
16190
16430
  };
16191
16431
  var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
16192
16432
  var readSessionMarker = (file) => {
16193
16433
  try {
16194
- const value = JSON.parse(fs13.readFileSync(file, "utf8"));
16434
+ const value = JSON.parse(fs14.readFileSync(file, "utf8"));
16195
16435
  return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
16196
16436
  } catch {
16197
16437
  return { nudged: [], loaded: [], raw: [] };
@@ -16199,8 +16439,8 @@ var readSessionMarker = (file) => {
16199
16439
  };
16200
16440
  var writeSessionMarker = (file, marker) => {
16201
16441
  try {
16202
- fs13.mkdirSync(path15.dirname(file), { recursive: true });
16203
- fs13.writeFileSync(file, JSON.stringify(marker));
16442
+ fs14.mkdirSync(path17.dirname(file), { recursive: true });
16443
+ fs14.writeFileSync(file, JSON.stringify(marker));
16204
16444
  } catch {}
16205
16445
  };
16206
16446
 
@@ -16697,14 +16937,14 @@ var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
16697
16937
  var ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
16698
16938
  var MAX_DEPTH = 4;
16699
16939
  var MAX_EXPANSIONS = 16;
16700
- var isAbsolute = (value) => path16.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
16940
+ var isAbsolute = (value) => path18.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
16701
16941
  var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
16702
16942
  var joinDir = (base, raw) => {
16703
16943
  const next = raw.replace(HERE, ".");
16704
16944
  if (base === undefined || /[$`*?]/.test(next) || next === "-")
16705
16945
  return;
16706
- const expanded = next === "~" || next.startsWith("~/") ? path16.join(os4.homedir(), next.slice(1)) : next;
16707
- return base === null || isAbsolute(expanded) ? expanded : path16.join(base, expanded);
16946
+ const expanded = next === "~" || next.startsWith("~/") ? path18.join(os4.homedir(), next.slice(1)) : next;
16947
+ return base === null || isAbsolute(expanded) ? expanded : path18.join(base, expanded);
16708
16948
  };
16709
16949
  var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
16710
16950
  var cdTarget = (words) => {
@@ -17455,7 +17695,7 @@ var repoAt = (dir) => {
17455
17695
  return { location, dir, branch: resolveTaskKey(dir, location).branch };
17456
17696
  };
17457
17697
  var isWorkitWorkspace = (repo) => {
17458
- if (fs14.existsSync(repo.location.dir))
17698
+ if (fs15.existsSync(repo.location.dir))
17459
17699
  return true;
17460
17700
  try {
17461
17701
  return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
@@ -17476,17 +17716,17 @@ var headSha2 = (repo) => {
17476
17716
  if (!git || git.reftable)
17477
17717
  return null;
17478
17718
  try {
17479
- const head = fs14.readFileSync(path16.join(git.gitDir, "HEAD"), "utf8").trim();
17719
+ const head = fs15.readFileSync(path18.join(git.gitDir, "HEAD"), "utf8").trim();
17480
17720
  if (/^[0-9a-f]{40,64}$/.test(head))
17481
17721
  return head;
17482
17722
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
17483
17723
  if (!ref)
17484
17724
  return null;
17485
- const common = path16.dirname(repo.location.dir);
17725
+ const common = path18.dirname(repo.location.dir);
17486
17726
  try {
17487
- return fs14.readFileSync(path16.join(common, ref), "utf8").trim() || null;
17727
+ return fs15.readFileSync(path18.join(common, ref), "utf8").trim() || null;
17488
17728
  } catch {}
17489
- const packed = fs14.readFileSync(path16.join(common, "packed-refs"), "utf8");
17729
+ const packed = fs15.readFileSync(path18.join(common, "packed-refs"), "utf8");
17490
17730
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
17491
17731
  } catch {
17492
17732
  return null;
@@ -17574,8 +17814,8 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
17574
17814
  const sub = invocation.args[0];
17575
17815
  if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
17576
17816
  return [invocation];
17577
- const dir = path16.resolve(root, invocation.dir ?? ".");
17578
- if (!fs14.existsSync(dir))
17817
+ const dir = path18.resolve(root, invocation.dir ?? ".");
17818
+ if (!fs15.existsSync(dir))
17579
17819
  return [invocation];
17580
17820
  const config = gitConfig(dir, invocation, cache);
17581
17821
  const alias = configValue(config, `alias.${sub.toLowerCase()}`);
@@ -17597,11 +17837,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
17597
17837
  var normalizedUrl = (url, dir) => {
17598
17838
  const trimmed = url.trim();
17599
17839
  if (/^file:\/\//i.test(trimmed))
17600
- return path16.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
17840
+ return path18.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
17601
17841
  const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
17602
17842
  const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
17603
17843
  if (!scheme && !scp)
17604
- return path16.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
17844
+ return path18.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
17605
17845
  const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
17606
17846
  return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
17607
17847
  };
@@ -17825,7 +18065,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
17825
18065
  const out = [];
17826
18066
  const repos = new Map;
17827
18067
  const repoFor = (relative) => {
17828
- const dir = path16.resolve(cwd, relative ?? ".");
18068
+ const dir = path18.resolve(cwd, relative ?? ".");
17829
18069
  if (!repos.has(dir)) {
17830
18070
  const repo = repoAt(dir);
17831
18071
  repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
@@ -17910,8 +18150,8 @@ var analysisDenial = (error) => ({
17910
18150
  unblock: SPLIT_IT
17911
18151
  });
17912
18152
  var MARKER_TTL_SECONDS = 3600;
17913
- var markerFile = (repo, session, key) => path16.join(repo.location.dir, "hooks", `commit-${createHash12("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
17914
- var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash12("sha256").update(command).digest("hex")}`;
18153
+ var markerFile = (repo, session, key) => path18.join(repo.location.dir, "hooks", `commit-${createHash13("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
18154
+ var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash13("sha256").update(command).digest("hex")}`;
17915
18155
  function noteRawCommit(input, command, key) {
17916
18156
  try {
17917
18157
  if (!input.session.id || !/\bcommit\b/.test(command))
@@ -17921,8 +18161,8 @@ function noteRawCommit(input, command, key) {
17921
18161
  if (action.kind !== "commit")
17922
18162
  continue;
17923
18163
  const file = markerFile(repo, input.session.id, key);
17924
- fs14.mkdirSync(path16.dirname(file), { recursive: true });
17925
- fs14.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
18164
+ fs15.mkdirSync(path18.dirname(file), { recursive: true });
18165
+ fs15.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
17926
18166
  `);
17927
18167
  }
17928
18168
  } catch {}
@@ -17945,11 +18185,11 @@ var headCommit = (dir) => {
17945
18185
  var settle2 = (input, repo, file) => {
17946
18186
  let marker;
17947
18187
  try {
17948
- marker = JSON.parse(fs14.readFileSync(file, "utf8"));
18188
+ marker = JSON.parse(fs15.readFileSync(file, "utf8"));
17949
18189
  } catch {
17950
18190
  return;
17951
18191
  }
17952
- fs14.rmSync(file, { force: true });
18192
+ fs15.rmSync(file, { force: true });
17953
18193
  const session = input.session.id;
17954
18194
  const now = Date.now() / 1000;
17955
18195
  if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
@@ -18140,7 +18380,7 @@ var judgedDir = (root, dir) => {
18140
18380
  return null;
18141
18381
  if (dir === null)
18142
18382
  return root;
18143
- const resolved = path17.resolve(root, dir);
18383
+ const resolved = path19.resolve(root, dir);
18144
18384
  return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
18145
18385
  };
18146
18386
  var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
@@ -18376,7 +18616,7 @@ var judgeInputSummary = (raw) => {
18376
18616
 
18377
18617
  // packages/workit-core/src/autonomy.ts
18378
18618
  import os5 from "node:os";
18379
- import path18 from "node:path";
18619
+ import path20 from "node:path";
18380
18620
 
18381
18621
  // packages/workit-core/src/core/safe-write.ts
18382
18622
  import {
@@ -18410,10 +18650,10 @@ var accountHome = () => {
18410
18650
  }
18411
18651
  };
18412
18652
  var grantsHome = { resolve: accountHome };
18413
- var grantsDir = () => path18.join(grantsHome.resolve(), ".config", "workit");
18653
+ var grantsDir = () => path20.join(grantsHome.resolve(), ".config", "workit");
18414
18654
  var grantsOverride = () => {
18415
- const configured = path18.resolve(resolveConfigDir());
18416
- return configured === path18.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 ${path18.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
18655
+ const configured = path20.resolve(resolveConfigDir());
18656
+ return configured === path20.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 ${path20.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
18417
18657
  };
18418
18658
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
18419
18659
  var DEFAULT_GRANTS = Object.freeze({
@@ -20185,7 +20425,7 @@ class WorkitCore {
20185
20425
  // packages/workit-core/src/core/repo-context.ts
20186
20426
  import { spawnSync as spawnSync13 } from "node:child_process";
20187
20427
  import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
20188
- import path19 from "node:path";
20428
+ import path21 from "node:path";
20189
20429
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
20190
20430
  try {
20191
20431
  gitRevisionParts(value);
@@ -20319,7 +20559,7 @@ var findPrTemplate = (cwd) => {
20319
20559
  ]) {
20320
20560
  const parts = rel.split("/");
20321
20561
  const base = parts[parts.length - 1];
20322
- const dir = parts.length > 1 ? path19.join(cwd, ...parts.slice(0, -1)) : cwd;
20562
+ const dir = parts.length > 1 ? path21.join(cwd, ...parts.slice(0, -1)) : cwd;
20323
20563
  const actual = probeCaseInsensitive(dir, base);
20324
20564
  if (actual)
20325
20565
  return [...parts.slice(0, -1), actual].join("/");
@@ -20350,7 +20590,7 @@ var diffStatForRange = (cwd, range) => {
20350
20590
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
20351
20591
  };
20352
20592
  var packageScripts = (cwd, keys) => {
20353
- const pkgPath = path19.join(cwd, "package.json");
20593
+ const pkgPath = path21.join(cwd, "package.json");
20354
20594
  if (!existsSync7(pkgPath))
20355
20595
  return [];
20356
20596
  try {
@@ -20376,9 +20616,9 @@ var documentationFiles = (cwd) => {
20376
20616
  if (entry.isDirectory()) {
20377
20617
  if (excluded.has(entry.name))
20378
20618
  continue;
20379
- walk(path19.join(dir, entry.name), depth + 1);
20619
+ walk(path21.join(dir, entry.name), depth + 1);
20380
20620
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
20381
- matches.push("./" + path19.relative(cwd, path19.join(dir, entry.name)).replaceAll("\\", "/"));
20621
+ matches.push("./" + path21.relative(cwd, path21.join(dir, entry.name)).replaceAll("\\", "/"));
20382
20622
  }
20383
20623
  }
20384
20624
  };
@@ -20394,7 +20634,7 @@ var readTrimmed2 = (file, maxLines) => {
20394
20634
  `);
20395
20635
  };
20396
20636
  function prReadyContext(root, range) {
20397
- const cwd = path19.resolve(repoRoot(root));
20637
+ const cwd = path21.resolve(repoRoot(root));
20398
20638
  if (range !== undefined && !isResolvableContextRange(cwd, range))
20399
20639
  return invalidRange(cwd);
20400
20640
  let stdout = "";
@@ -20452,7 +20692,7 @@ ${status.stderr}` : "");
20452
20692
  stdout += `template_path: ${template}
20453
20693
 
20454
20694
  `;
20455
- stdout += readTrimmed2(path19.join(cwd, template), 220);
20695
+ stdout += readTrimmed2(path21.join(cwd, template), 220);
20456
20696
  } else {
20457
20697
  stdout += `template_path: none
20458
20698
 
@@ -20462,7 +20702,7 @@ ${status.stderr}` : "");
20462
20702
  stdout += `
20463
20703
  `;
20464
20704
  stdout += printSection("Recent Validation Signals");
20465
- if (existsSync7(path19.join(cwd, "package.json"))) {
20705
+ if (existsSync7(path21.join(cwd, "package.json"))) {
20466
20706
  stdout += `package.json detected. Common scripts:
20467
20707
  `;
20468
20708
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -20471,7 +20711,7 @@ ${status.stderr}` : "");
20471
20711
  `) + `
20472
20712
  `;
20473
20713
  }
20474
- if (existsSync7(path19.join(cwd, "Cargo.toml")) || existsSync7(path19.join(cwd, "src-tauri/Cargo.toml"))) {
20714
+ if (existsSync7(path21.join(cwd, "Cargo.toml")) || existsSync7(path21.join(cwd, "src-tauri/Cargo.toml"))) {
20475
20715
  stdout += `Rust project detected.
20476
20716
  `;
20477
20717
  }
@@ -20504,7 +20744,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
20504
20744
  - Apply with the host-native editor, preserving the repository changelog conventions.
20505
20745
  - If Unreleased already has duplicate category headings, normalize_only first.`;
20506
20746
  function changelogContext(root, range) {
20507
- const cwd = path19.resolve(repoRoot(root));
20747
+ const cwd = path21.resolve(repoRoot(root));
20508
20748
  if (range !== undefined && !isResolvableContextRange(cwd, range))
20509
20749
  return invalidRange(cwd);
20510
20750
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -20522,7 +20762,7 @@ function changelogContext(root, range) {
20522
20762
  stdout += CHANGELOG_RULES + `
20523
20763
  `;
20524
20764
  stdout += printSection("Existing CHANGELOG.md");
20525
- const changelogPath = path19.join(cwd, "CHANGELOG.md");
20765
+ const changelogPath = path21.join(cwd, "CHANGELOG.md");
20526
20766
  if (existsSync7(changelogPath)) {
20527
20767
  stdout += readTrimmed2(changelogPath, 260) + `
20528
20768
  `;
@@ -20542,7 +20782,7 @@ function changelogContext(root, range) {
20542
20782
  return { stdout, stderr: "", exitCode: 0, cwd };
20543
20783
  }
20544
20784
  function docsRefreshContext(root, range) {
20545
- const cwd = path19.resolve(repoRoot(root));
20785
+ const cwd = path21.resolve(repoRoot(root));
20546
20786
  if (range !== undefined && !isResolvableContextRange(cwd, range))
20547
20787
  return invalidRange(cwd);
20548
20788
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -20564,7 +20804,7 @@ function docsRefreshContext(root, range) {
20564
20804
  `) + `
20565
20805
  `;
20566
20806
  stdout += printSection("README Preview");
20567
- const readme = path19.join(cwd, "README.md");
20807
+ const readme = path21.join(cwd, "README.md");
20568
20808
  if (existsSync7(readme)) {
20569
20809
  stdout += readTrimmed2(readme, 220) + `
20570
20810
  `;
@@ -20573,7 +20813,7 @@ function docsRefreshContext(root, range) {
20573
20813
  `;
20574
20814
  }
20575
20815
  stdout += printSection("Package Scripts");
20576
- const pkgPath = path19.join(cwd, "package.json");
20816
+ const pkgPath = path21.join(cwd, "package.json");
20577
20817
  if (existsSync7(pkgPath)) {
20578
20818
  try {
20579
20819
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
@@ -20587,7 +20827,7 @@ function docsRefreshContext(root, range) {
20587
20827
  return { stdout, stderr: "", exitCode: 0, cwd };
20588
20828
  }
20589
20829
  function releaseNotesContext(root, rangeOrTag) {
20590
- const cwd = path19.resolve(repoRoot(root));
20830
+ const cwd = path21.resolve(repoRoot(root));
20591
20831
  if (!rangeOrTag) {
20592
20832
  return { stdout: "", stderr: `ERROR: release tag or range required
20593
20833
  `, exitCode: 1, cwd };
@@ -20625,7 +20865,7 @@ function releaseNotesContext(root, rangeOrTag) {
20625
20865
  `;
20626
20866
  stdout += printSection("Existing Release Files");
20627
20867
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
20628
- if (existsSync7(path19.join(cwd, rel)))
20868
+ if (existsSync7(path21.join(cwd, rel)))
20629
20869
  stdout += rel + `
20630
20870
  `;
20631
20871
  }
@@ -20656,8 +20896,8 @@ function releaseNotesContext(root, rangeOrTag) {
20656
20896
  import { spawnSync as spawnSync15 } from "node:child_process";
20657
20897
 
20658
20898
  // packages/workit-core/src/core/pr-create.ts
20659
- import fs15 from "node:fs";
20660
- import path20 from "node:path";
20899
+ import fs16 from "node:fs";
20900
+ import path22 from "node:path";
20661
20901
  import { spawnSync as spawnSync14 } from "node:child_process";
20662
20902
  // packages/workit-core/src/forge/pr-body.ts
20663
20903
  function parseGhIssue(value) {
@@ -20668,13 +20908,13 @@ function parseGhIssue(value) {
20668
20908
  // packages/workit-core/src/core/pr-create.ts
20669
20909
  function whichOnPath(tool) {
20670
20910
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
20671
- for (const dir of (process.env.PATH ?? "").split(path20.delimiter)) {
20911
+ for (const dir of (process.env.PATH ?? "").split(path22.delimiter)) {
20672
20912
  if (!dir)
20673
20913
  continue;
20674
20914
  for (const name of names) {
20675
- const candidate = path20.join(dir, name);
20915
+ const candidate = path22.join(dir, name);
20676
20916
  try {
20677
- fs15.accessSync(candidate, fs15.constants.X_OK);
20917
+ fs16.accessSync(candidate, fs16.constants.X_OK);
20678
20918
  return candidate;
20679
20919
  } catch {}
20680
20920
  }
@@ -20792,8 +21032,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
20792
21032
  };
20793
21033
 
20794
21034
  // packages/workit-core/src/core/youtrack.ts
20795
- import fs16 from "node:fs";
20796
- import path21 from "node:path";
21035
+ import fs17 from "node:fs";
21036
+ import path23 from "node:path";
20797
21037
 
20798
21038
  // packages/workit-core/src/core/templates.ts
20799
21039
  var repoRoot2 = assetRoot();
@@ -20806,22 +21046,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
20806
21046
  // packages/workit-core/src/core/youtrack.ts
20807
21047
  var ISSUE_RE = /^[A-Z]+-\d+$/;
20808
21048
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
20809
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path21.join(configDir(), "youtrack.json");
20810
- var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path21.join(resolveConfigDir(), "youtrack.json");
21049
+ var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path23.join(configDir(), "youtrack.json");
21050
+ var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path23.join(resolveConfigDir(), "youtrack.json");
20811
21051
  var youTrackTokenModeOk = (p) => {
20812
21052
  if (process.platform === "win32")
20813
21053
  return true;
20814
- const mode = fs16.statSync(p).mode & 511;
21054
+ const mode = fs17.statSync(p).mode & 511;
20815
21055
  return mode === 384;
20816
21056
  };
20817
21057
  function readYouTrackConfig(required) {
20818
21058
  const cfgPath = youTrackConfigPath();
20819
- if (!fs16.existsSync(cfgPath)) {
21059
+ if (!fs17.existsSync(cfgPath)) {
20820
21060
  return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
20821
21061
  }
20822
21062
  let parsed;
20823
21063
  try {
20824
- parsed = JSON.parse(fs16.readFileSync(cfgPath, "utf8"));
21064
+ parsed = JSON.parse(fs17.readFileSync(cfgPath, "utf8"));
20825
21065
  } catch {
20826
21066
  return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
20827
21067
  }
@@ -20832,11 +21072,11 @@ function readYouTrackConfig(required) {
20832
21072
  }
20833
21073
  var readYouTrackContextConfig = () => {
20834
21074
  const cfgPath = youTrackReadOnlyConfigPath();
20835
- if (!fs16.existsSync(cfgPath))
21075
+ if (!fs17.existsSync(cfgPath))
20836
21076
  return { data: {} };
20837
21077
  let parsed;
20838
21078
  try {
20839
- parsed = JSON.parse(fs16.readFileSync(cfgPath, "utf8"));
21079
+ parsed = JSON.parse(fs17.readFileSync(cfgPath, "utf8"));
20840
21080
  } catch {
20841
21081
  return { error: "YouTrack configuration is unavailable" };
20842
21082
  }
@@ -20875,12 +21115,12 @@ var youTrackToken = () => {
20875
21115
  if ("error" in loaded)
20876
21116
  return loaded;
20877
21117
  const tokenFile = String(loaded.config.tokenFile ?? "");
20878
- const tokenPath = tokenFile ? path21.isAbsolute(tokenFile) ? path21.resolve(tokenFile) : path21.resolve(process.cwd(), tokenFile) : "";
20879
- if (!tokenPath || !fs16.existsSync(tokenPath))
21118
+ const tokenPath = tokenFile ? path23.isAbsolute(tokenFile) ? path23.resolve(tokenFile) : path23.resolve(process.cwd(), tokenFile) : "";
21119
+ if (!tokenPath || !fs17.existsSync(tokenPath))
20880
21120
  return { error: "missing youtrack.token" };
20881
21121
  if (!youTrackTokenModeOk(tokenPath))
20882
21122
  return { error: "youtrack.token mode must be 0600" };
20883
- const token = fs16.readFileSync(tokenPath, "utf8").trim();
21123
+ const token = fs17.readFileSync(tokenPath, "utf8").trim();
20884
21124
  if (!token)
20885
21125
  return { error: "empty token file" };
20886
21126
  if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
@@ -20926,7 +21166,7 @@ function youTrackTokenCreateUrl() {
20926
21166
  const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
20927
21167
  const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
20928
21168
  const base = String(config.baseUrl ?? "").replace(/\/+$/, "");
20929
- const tokenFile = String(config.tokenFile ?? path21.join(path21.dirname(loaded.path), "youtrack.token"));
21169
+ const tokenFile = String(config.tokenFile ?? path23.join(path23.dirname(loaded.path), "youtrack.token"));
20930
21170
  const tab = String(defaults.profileTab ?? "account-security");
20931
21171
  const createUrl = base ? `${base}/users/me?${new URLSearchParams({ tab })}` : null;
20932
21172
  const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
@@ -20935,7 +21175,7 @@ function youTrackTokenCreateUrl() {
20935
21175
  tokenName: name,
20936
21176
  tokenDescription: desc,
20937
21177
  scopes,
20938
- tokenFile: path21.resolve(tokenFile),
21178
+ tokenFile: path23.resolve(tokenFile),
20939
21179
  createUrl,
20940
21180
  docsUrl,
20941
21181
  prefillSupported: false,
@@ -20981,9 +21221,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
20981
21221
  } catch {
20982
21222
  continue;
20983
21223
  }
20984
- if (!fs16.existsSync(full))
21224
+ if (!fs17.existsSync(full))
20985
21225
  continue;
20986
- const text = fs16.readFileSync(full, "utf8");
21226
+ const text = fs17.readFileSync(full, "utf8");
20987
21227
  const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
20988
21228
  if (m)
20989
21229
  return m[1];
@@ -21275,15 +21515,15 @@ function resolveGitRevision(root, value) {
21275
21515
  }
21276
21516
  function resolveInside(root, candidate) {
21277
21517
  const base = realpathSync5(root);
21278
- const target = path22.resolve(base, candidate);
21279
- if (target !== base && !target.startsWith(base + path22.sep)) {
21518
+ const target = path24.resolve(base, candidate);
21519
+ if (target !== base && !target.startsWith(base + path24.sep)) {
21280
21520
  throw new Error("path must stay inside repository root");
21281
21521
  }
21282
21522
  let ancestor = target;
21283
21523
  while (!existsSync8(ancestor))
21284
- ancestor = path22.dirname(ancestor);
21524
+ ancestor = path24.dirname(ancestor);
21285
21525
  const canonicalAncestor = realpathSync5(ancestor);
21286
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path22.sep)) {
21526
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path24.sep)) {
21287
21527
  throw new Error("path must stay inside repository root");
21288
21528
  }
21289
21529
  return target;
@@ -21327,7 +21567,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
21327
21567
  };
21328
21568
  });
21329
21569
  // packages/workit-core/src/hooks/context.ts
21330
- import path23 from "node:path";
21570
+ import path25 from "node:path";
21331
21571
  var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
21332
21572
  var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
21333
21573
  var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
@@ -21412,7 +21652,7 @@ function entryCompactContext(store, entry, session, context) {
21412
21652
  return null;
21413
21653
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
21414
21654
  const observed = task?.data.evidence.some(isObservedCheck) ?? false;
21415
- const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path23.resolve(store.root, ref.path)] : []));
21655
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path25.resolve(store.root, ref.path)] : []));
21416
21656
  const observedFiles = filesKey(files);
21417
21657
  const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
21418
21658
  if (!compact.ok || !compact.data)
@@ -21444,27 +21684,27 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
21444
21684
  };
21445
21685
  // packages/workit-core/src/hooks/write-gate.ts
21446
21686
  import { realpathSync as realpathSync6 } from "node:fs";
21447
- import path24 from "node:path";
21687
+ import path26 from "node:path";
21448
21688
  var NONE3 = { kind: "none" };
21449
21689
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
21450
21690
  var canonical2 = (file) => {
21451
21691
  let rest = "";
21452
- for (let dir = path24.resolve(file);; dir = path24.dirname(dir)) {
21692
+ for (let dir = path26.resolve(file);; dir = path26.dirname(dir)) {
21453
21693
  try {
21454
- return path24.join(realpathSync6(dir), rest);
21694
+ return path26.join(realpathSync6(dir), rest);
21455
21695
  } catch {
21456
- if (path24.dirname(dir) === dir)
21457
- return path24.resolve(file);
21458
- rest = rest ? path24.join(path24.basename(dir), rest) : path24.basename(dir);
21696
+ if (path26.dirname(dir) === dir)
21697
+ return path26.resolve(file);
21698
+ rest = rest ? path26.join(path26.basename(dir), rest) : path26.basename(dir);
21459
21699
  }
21460
21700
  }
21461
21701
  };
21462
21702
  var exempt = (cwd, root, file, plans) => {
21463
21703
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
21464
- const relative = path24.relative(fold(canonical2(root)), fold(canonical2(path24.resolve(cwd, file))));
21465
- if (relative.startsWith("..") || path24.isAbsolute(relative))
21704
+ const relative = path26.relative(fold(canonical2(root)), fold(canonical2(path26.resolve(cwd, file))));
21705
+ if (relative.startsWith("..") || path26.isAbsolute(relative))
21466
21706
  return true;
21467
- const posix = relative.split(path24.sep).join("/");
21707
+ const posix = relative.split(path26.sep).join("/");
21468
21708
  const segments = posix.split("/");
21469
21709
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
21470
21710
  };
@@ -21562,7 +21802,7 @@ var shellWrites = (command) => {
21562
21802
  return { writes: true, targets: unknown ? null : targets };
21563
21803
  };
21564
21804
  var cache2 = new Map;
21565
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path24.resolve(root, file)) ?? "-").join("\x00");
21805
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path26.resolve(root, file)) ?? "-").join("\x00");
21566
21806
  var gateFor = (cwd) => {
21567
21807
  const store = new TaskStore(cwd);
21568
21808
  const listed = store.listTaskIndex();
@@ -21730,10 +21970,10 @@ function stopObligation(input, descriptor) {
21730
21970
  const branch = currentBranch(input.cwd);
21731
21971
  if (!branch || guardedBranch(input.cwd, branch))
21732
21972
  return null;
21733
- const ledger = readLedger(input.cwd);
21973
+ const ledger = readLedger(input.cwd, { integrity: true });
21734
21974
  if (!ledger.ok)
21735
21975
  return null;
21736
- const all = ledger.value.rows;
21976
+ const all = ledger.value.rows.filter((row) => row.integrity !== "unverified");
21737
21977
  const rows = all.filter((row) => row.branch === branch);
21738
21978
  const mine = rows.filter((row) => sessionOf(row) === session);
21739
21979
  const commits = new Set(mine.filter((row) => row.type === "commit.recorded").map((row) => typeof row.sha === "string" ? row.sha : row.head).filter((sha) => typeof sha === "string"));
@@ -22101,11 +22341,11 @@ var OPENCODE_DESCRIPTOR = {
22101
22341
  ]
22102
22342
  };
22103
22343
  // packages/workit-opencode/src/shared/init-apply.ts
22104
- import path29 from "node:path";
22344
+ import path31 from "node:path";
22105
22345
 
22106
22346
  // packages/workit-core/src/core/gitignore.ts
22107
22347
  import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
22108
- import path25 from "node:path";
22348
+ import path27 from "node:path";
22109
22349
  var GITIGNORE_ENTRIES = [
22110
22350
  "# workit: SDD working state (never commit)",
22111
22351
  "docs/*/sdd/",
@@ -22125,7 +22365,7 @@ var GITIGNORE_ENTRIES = [
22125
22365
  var ensureProjectGitignore = (workspaceRoot, confirmed) => {
22126
22366
  if (!confirmed)
22127
22367
  return { ok: false, error: "confirmed: true required" };
22128
- const file = path25.join(workspaceRoot, ".gitignore");
22368
+ const file = path27.join(workspaceRoot, ".gitignore");
22129
22369
  const existing = existsSync9(file) ? readFileSync6(file, "utf8") : "";
22130
22370
  const existingLines = new Set(existing.split(`
22131
22371
  `).map((l) => l.trim()).filter(Boolean));
@@ -22152,26 +22392,26 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
22152
22392
  };
22153
22393
 
22154
22394
  // packages/workit-core/src/core/hygiene.ts
22155
- import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "node:fs";
22156
- import path26 from "node:path";
22395
+ import { existsSync as existsSync10, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
22396
+ import path28 from "node:path";
22157
22397
  var repoRoot3 = assetRoot();
22158
- var templatesDir = () => path26.join(repoRoot3, "templates", "hygiene");
22398
+ var templatesDir = () => path28.join(repoRoot3, "templates", "hygiene");
22159
22399
  var packageJson = (root) => {
22160
- if (!existsSync10(path26.join(root, "package.json")))
22400
+ if (!existsSync10(path28.join(root, "package.json")))
22161
22401
  return null;
22162
22402
  try {
22163
- return JSON.parse(readFileSync7(path26.join(root, "package.json"), "utf8"));
22403
+ return JSON.parse(readFileSync7(path28.join(root, "package.json"), "utf8"));
22164
22404
  } catch {
22165
22405
  return null;
22166
22406
  }
22167
22407
  };
22168
22408
  var isOpenSource = (root) => {
22169
- if (existsSync10(path26.join(root, "LICENSE")))
22409
+ if (existsSync10(path28.join(root, "LICENSE")))
22170
22410
  return true;
22171
22411
  const pkg = packageJson(root);
22172
22412
  if (pkg && !pkg.private)
22173
22413
  return true;
22174
- return path26.basename(root) === "workflow-toolkit";
22414
+ return path28.basename(root) === "workflow-toolkit";
22175
22415
  };
22176
22416
  var licenseHolder = (root) => {
22177
22417
  const pkg = packageJson(root);
@@ -22194,13 +22434,13 @@ var planHygieneFiles = (root, opts = {}) => {
22194
22434
  files.push("LICENSE", "CONTRIBUTING.md");
22195
22435
  const planned = [];
22196
22436
  for (const file of files) {
22197
- if (existsSync10(path26.join(root, file)))
22437
+ if (existsSync10(path28.join(root, file)))
22198
22438
  continue;
22199
- const tpl = path26.join(templatesDir(), file);
22439
+ const tpl = path28.join(templatesDir(), file);
22200
22440
  if (!existsSync10(tpl))
22201
22441
  continue;
22202
- const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path26.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
22203
- planned.push({ path: path26.join(root, file), content });
22442
+ const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path28.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
22443
+ planned.push({ path: path28.join(root, file), content });
22204
22444
  }
22205
22445
  return planned;
22206
22446
  };
@@ -22210,19 +22450,19 @@ var ensureHygieneFiles = (root, opts) => {
22210
22450
  const created = [];
22211
22451
  for (const planned of planHygieneFiles(root, opts)) {
22212
22452
  writeFileSync5(planned.path, planned.content, "utf8");
22213
- created.push(path26.basename(planned.path));
22453
+ created.push(path28.basename(planned.path));
22214
22454
  }
22215
22455
  return { ok: true, created };
22216
22456
  };
22217
22457
 
22218
22458
  // packages/workit-core/src/core/init.ts
22219
- import fs17 from "node:fs";
22220
- import path28 from "node:path";
22459
+ import fs18 from "node:fs";
22460
+ import path30 from "node:path";
22221
22461
  import { spawnSync as spawnSync18 } from "node:child_process";
22222
22462
 
22223
22463
  // packages/workit-core/src/core/workspace-branch-policy.ts
22224
22464
  import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
22225
- import path27 from "node:path";
22465
+ import path29 from "node:path";
22226
22466
  import { isDeepStrictEqual } from "node:util";
22227
22467
  var readFileSafe = (p) => {
22228
22468
  try {
@@ -22233,7 +22473,7 @@ var readFileSafe = (p) => {
22233
22473
  };
22234
22474
  function applyWorkspaceBranchPolicy(opts) {
22235
22475
  const { workspace_root, env = process.env } = opts;
22236
- const dir = path27.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
22476
+ const dir = path29.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
22237
22477
  const {
22238
22478
  status,
22239
22479
  path: wsPath,
@@ -22245,7 +22485,7 @@ function applyWorkspaceBranchPolicy(opts) {
22245
22485
  if (status === "malformed" || status === "invalid")
22246
22486
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
22247
22487
  const detection = detectBranchPolicy(workspace_root);
22248
- const name = String(env.WORKFLOW_BP_NAME ?? path27.basename(workspace_root));
22488
+ const name = String(env.WORKFLOW_BP_NAME ?? path29.basename(workspace_root));
22249
22489
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
22250
22490
  const policy = {
22251
22491
  preset: detection.preset,
@@ -22294,7 +22534,7 @@ function applyWorkspaceBranchPolicy(opts) {
22294
22534
  ok: false,
22295
22535
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
22296
22536
  };
22297
- mkdirSync4(path27.dirname(wsPath), { recursive: true });
22537
+ mkdirSync4(path29.dirname(wsPath), { recursive: true });
22298
22538
  const tmp = `${wsPath}.${process.pid}.tmp`;
22299
22539
  try {
22300
22540
  writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
@@ -22329,7 +22569,7 @@ function applyWorkspaceBranchPolicy(opts) {
22329
22569
  var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
22330
22570
  var readJson = (p) => {
22331
22571
  try {
22332
- const parsed = JSON.parse(fs17.readFileSync(p, "utf8"));
22572
+ const parsed = JSON.parse(fs18.readFileSync(p, "utf8"));
22333
22573
  return isConfigObject(parsed) ? parsed : null;
22334
22574
  } catch {
22335
22575
  return null;
@@ -22359,7 +22599,7 @@ var youtrackJsonContent = (dir) => {
22359
22599
  const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
22360
22600
  return {
22361
22601
  baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
22362
- tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path28.join(dir, "youtrack.token"),
22602
+ tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path30.join(dir, "youtrack.token"),
22363
22603
  ...meetingIssue ? {
22364
22604
  meetingIssue,
22365
22605
  meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
@@ -22389,19 +22629,19 @@ var vcsJsonContent = () => {
22389
22629
  };
22390
22630
  function initApplyData(action, env = process.env) {
22391
22631
  const dir = String(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
22392
- fs17.mkdirSync(dir, { recursive: true });
22632
+ fs18.mkdirSync(dir, { recursive: true });
22393
22633
  switch (action) {
22394
22634
  case "youtrack_json": {
22395
- const out = path28.join(dir, "youtrack.json");
22396
- fs17.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
22635
+ const out = path30.join(dir, "youtrack.json");
22636
+ fs18.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
22397
22637
  `, "utf8");
22398
22638
  return { action, ok: true, path: out };
22399
22639
  }
22400
22640
  case "youtrack_token_placeholder": {
22401
- const p = path28.join(dir, "youtrack.token");
22641
+ const p = path30.join(dir, "youtrack.token");
22402
22642
  const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
22403
22643
  `, 384) === "preserved";
22404
- const abs = path28.resolve(p);
22644
+ const abs = path30.resolve(p);
22405
22645
  return {
22406
22646
  action,
22407
22647
  ok: true,
@@ -22413,14 +22653,14 @@ function initApplyData(action, env = process.env) {
22413
22653
  };
22414
22654
  }
22415
22655
  case "youtrack_scaffold": {
22416
- const jsonOut = path28.join(dir, "youtrack.json");
22417
- const tokenOut = path28.join(dir, "youtrack.token");
22418
- fs17.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
22656
+ const jsonOut = path30.join(dir, "youtrack.json");
22657
+ const tokenOut = path30.join(dir, "youtrack.token");
22658
+ fs18.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
22419
22659
  `, "utf8");
22420
22660
  const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
22421
22661
  `, 384) === "preserved";
22422
- const configPath = path28.resolve(jsonOut);
22423
- const tokenPath = path28.resolve(tokenOut);
22662
+ const configPath = path30.resolve(jsonOut);
22663
+ const tokenPath = path30.resolve(tokenOut);
22424
22664
  const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
22425
22665
  process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
22426
22666
  let tokenCreate = {};
@@ -22472,10 +22712,10 @@ function initApplyData(action, env = process.env) {
22472
22712
  }
22473
22713
  }
22474
22714
  case "vcs_scaffold": {
22475
- const jsonOut = path28.join(dir, "vcs.json");
22476
- fs17.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
22715
+ const jsonOut = path30.join(dir, "vcs.json");
22716
+ fs18.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
22477
22717
  `, "utf8");
22478
- const configPath = path28.resolve(jsonOut);
22718
+ const configPath = path30.resolve(jsonOut);
22479
22719
  const prev = process.env.WORKFLOW_VCS_CONFIG;
22480
22720
  process.env.WORKFLOW_VCS_CONFIG = configPath;
22481
22721
  try {
@@ -22594,7 +22834,7 @@ var executeInitApply = (args, directory, runtime) => {
22594
22834
  protectedNames: args.branch_policy_protected
22595
22835
  }, current);
22596
22836
  writeConfig(next);
22597
- return output(ok({ action: "config", path: path29.join(configDir(), "config.json"), ...next }));
22837
+ return output(ok({ action: "config", path: path31.join(configDir(), "config.json"), ...next }));
22598
22838
  }
22599
22839
  const env = Object.fromEntries(Object.entries({
22600
22840
  WORKFLOW_YT_BASE_URL: args.base_url,
@@ -22611,14 +22851,14 @@ var executeInitApply = (args, directory, runtime) => {
22611
22851
 
22612
22852
  // packages/workit-opencode/src/shared/session.ts
22613
22853
  import { realpathSync as realpathSync7 } from "node:fs";
22614
- import path30 from "node:path";
22854
+ import path32 from "node:path";
22615
22855
  var sameWorkspace = (expected, observed) => {
22616
22856
  if (typeof observed !== "string" || !observed)
22617
22857
  return false;
22618
22858
  try {
22619
22859
  return realpathSync7(expected) === realpathSync7(observed);
22620
22860
  } catch {
22621
- return path30.resolve(expected) === path30.resolve(observed);
22861
+ return path32.resolve(expected) === path32.resolve(observed);
22622
22862
  }
22623
22863
  };
22624
22864
 
@@ -22690,7 +22930,7 @@ var workitFamilyOf = (toolName) => {
22690
22930
 
22691
22931
  // packages/workit-opencode/src/tools/context.ts
22692
22932
  import { realpathSync as realpathSync8 } from "node:fs";
22693
- import path31 from "node:path";
22933
+ import path33 from "node:path";
22694
22934
  var createContextTool = () => ({
22695
22935
  description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
22696
22936
  execute: async (args, context) => {
@@ -22700,7 +22940,7 @@ var createContextTool = () => ({
22700
22940
  let root;
22701
22941
  try {
22702
22942
  const cwd = parsed.data.cwd;
22703
- root = realpathSync8(cwd ? path31.resolve(context.directory, cwd) : context.directory);
22943
+ root = realpathSync8(cwd ? path33.resolve(context.directory, cwd) : context.directory);
22704
22944
  } catch {
22705
22945
  return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
22706
22946
  }
@@ -23293,7 +23533,7 @@ var childTaskMissing = (store, input) => {
23293
23533
  var childDenial = (identity) => `OpenCode child session has no validated Workit worker: as ${identity} it may only record evidence or findings (workit_evidence/workit_finding action "record") on an existing task; for anything else the lead assigns it a worker with workit_worker before launching it`;
23294
23534
 
23295
23535
  // packages/workit-opencode/src/v2/shell.ts
23296
- import path32 from "node:path";
23536
+ import path34 from "node:path";
23297
23537
  var commandOf = (input) => {
23298
23538
  if (input === null || typeof input !== "object")
23299
23539
  return null;
@@ -23302,7 +23542,7 @@ var commandOf = (input) => {
23302
23542
  };
23303
23543
  var hookInput = (root, sessionID, id, shell) => ({
23304
23544
  host: "opencode",
23305
- cwd: shell.workdir ? path32.resolve(root, shell.workdir) : root,
23545
+ cwd: shell.workdir ? path34.resolve(root, shell.workdir) : root,
23306
23546
  session: { id: sessionID, agentId: null, agentType: null, parentId: null },
23307
23547
  permissionMode: null,
23308
23548
  transcriptPath: null,
@@ -23487,37 +23727,37 @@ var createStopControl = (root, port) => {
23487
23727
 
23488
23728
  // packages/workit-opencode/src/v2/registry.ts
23489
23729
  import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
23490
- import path34 from "node:path";
23730
+ import path36 from "node:path";
23491
23731
 
23492
23732
  // packages/workit-opencode/src/shared/assets.ts
23493
23733
  import { existsSync as existsSync11, realpathSync as realpathSync9 } from "node:fs";
23494
- import path33 from "node:path";
23734
+ import path35 from "node:path";
23495
23735
  import { fileURLToPath as fileURLToPath2 } from "node:url";
23496
23736
  var moduleDir = () => {
23497
23737
  try {
23498
- return realpathSync9(path33.dirname(fileURLToPath2(import.meta.url)));
23738
+ return realpathSync9(path35.dirname(fileURLToPath2(import.meta.url)));
23499
23739
  } catch {
23500
- return path33.dirname(fileURLToPath2(import.meta.url));
23740
+ return path35.dirname(fileURLToPath2(import.meta.url));
23501
23741
  }
23502
23742
  };
23503
23743
  var packageRoot2 = () => {
23504
23744
  const dir = moduleDir();
23505
23745
  for (const candidate of [
23506
- path33.resolve(dir, ".."),
23507
- path33.resolve(dir, "..", ".."),
23508
- path33.resolve(dir, "..", "..", "..")
23746
+ path35.resolve(dir, ".."),
23747
+ path35.resolve(dir, "..", ".."),
23748
+ path35.resolve(dir, "..", "..", "..")
23509
23749
  ]) {
23510
- if (existsSync11(path33.join(candidate, "package.json")))
23750
+ if (existsSync11(path35.join(candidate, "package.json")))
23511
23751
  return candidate;
23512
23752
  }
23513
- return path33.resolve(dir, "..");
23753
+ return path35.resolve(dir, "..");
23514
23754
  };
23515
23755
  var skillsRoot = () => {
23516
23756
  const root = packageRoot2();
23517
- const canonical = path33.resolve(root, "..", "workit-core", "skills");
23518
- if (existsSync11(path33.join(root, "src")) && existsSync11(canonical))
23757
+ const canonical = path35.resolve(root, "..", "workit-core", "skills");
23758
+ if (existsSync11(path35.join(root, "src")) && existsSync11(canonical))
23519
23759
  return canonical;
23520
- return path33.join(root, "assets", "skills");
23760
+ return path35.join(root, "assets", "skills");
23521
23761
  };
23522
23762
 
23523
23763
  // packages/workit-opencode/src/v2/registry.ts
@@ -23544,11 +23784,11 @@ var registerSkills = async (ctx) => {
23544
23784
  if (!existsSync12(skillsDir))
23545
23785
  return new Set;
23546
23786
  const listed = await ctx.skill.list().catch(() => ({ data: [] }));
23547
- const own = (skill) => typeof skill.path === "string" && path34.resolve(skill.path).startsWith(skillsDir + path34.sep);
23787
+ const own = (skill) => typeof skill.path === "string" && path36.resolve(skill.path).startsWith(skillsDir + path36.sep);
23548
23788
  const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
23549
23789
  const packaged = new Map;
23550
23790
  for (const id of WORKIT_METHOD_SKILLS) {
23551
- const file = path34.join(skillsDir, id, "SKILL.md");
23791
+ const file = path36.join(skillsDir, id, "SKILL.md");
23552
23792
  if (!userSkills.has(id) && existsSync12(file))
23553
23793
  packaged.set(id, file);
23554
23794
  }
@@ -23595,23 +23835,23 @@ var registerCommands = async (ctx, registeredSkills) => {
23595
23835
  };
23596
23836
 
23597
23837
  // packages/workit-opencode/src/stale-sources.ts
23598
- import { existsSync as existsSync13, readdirSync as readdirSync5 } from "node:fs";
23599
- import path35 from "node:path";
23838
+ import { existsSync as existsSync13, readdirSync as readdirSync6 } from "node:fs";
23839
+ import path37 from "node:path";
23600
23840
  import { fileURLToPath as fileURLToPath3 } from "node:url";
23601
23841
  var typescriptFiles = (directory) => {
23602
23842
  if (!existsSync13(directory))
23603
23843
  return [];
23604
- return readdirSync5(directory, { withFileTypes: true }).flatMap((entry) => {
23605
- const file = path35.join(directory, entry.name);
23844
+ return readdirSync6(directory, { withFileTypes: true }).flatMap((entry) => {
23845
+ const file = path37.join(directory, entry.name);
23606
23846
  return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
23607
23847
  });
23608
23848
  };
23609
23849
  var pluginSourceFiles = [
23610
23850
  ...new Set([
23611
23851
  fileURLToPath3(import.meta.url),
23612
- ...typescriptFiles(path35.join(packageRoot2(), "src")),
23613
- path35.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
23614
- ...typescriptFiles(path35.join(packageRoot2(), "..", "workit-core", "src", "core"))
23852
+ ...typescriptFiles(path37.join(packageRoot2(), "src")),
23853
+ path37.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
23854
+ ...typescriptFiles(path37.join(packageRoot2(), "..", "workit-core", "src", "core"))
23615
23855
  ])
23616
23856
  ].filter((file) => existsSync13(file)).toSorted();
23617
23857