@brainervirus/workit-mcp 8.12.3 → 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.
Files changed (2) hide show
  1. package/dist/index.js +630 -390
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -6620,11 +6620,11 @@ var require_dist = __commonJS(function(exports, module) {
6620
6620
 
6621
6621
  // packages/workit-mcp/src/index.ts
6622
6622
  import { fileURLToPath as fileURLToPath3 } from "node:url";
6623
- import path21 from "node:path";
6623
+ import path23 from "node:path";
6624
6624
 
6625
6625
  // packages/workit-mcp/src/server.ts
6626
6626
  import { existsSync as existsSync8, readFileSync as readFileSync6, realpathSync as realpathSync6 } from "node:fs";
6627
- import path20 from "node:path";
6627
+ import path22 from "node:path";
6628
6628
 
6629
6629
  // node_modules/zod/v4/core/util.js
6630
6630
  function getEnumValues(entries) {
@@ -16576,7 +16576,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
16576
16576
  // packages/workit-core/src/core.ts
16577
16577
  import { spawnSync as spawnSync14 } from "node:child_process";
16578
16578
  import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
16579
- import path19 from "node:path";
16579
+ import path21 from "node:path";
16580
16580
 
16581
16581
  // packages/workit-core/src/core/task-contract.ts
16582
16582
  import { createHash, randomUUID } from "node:crypto";
@@ -17831,9 +17831,9 @@ function parseAdvertisedOperation(family, input, host) {
17831
17831
  });
17832
17832
  }
17833
17833
  // packages/workit-core/src/core/task-store.ts
17834
- import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
17835
- import * as fs7 from "node:fs";
17836
- import path6 from "node:path";
17834
+ import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
17835
+ import * as fs8 from "node:fs";
17836
+ import path8 from "node:path";
17837
17837
 
17838
17838
  // node_modules/@openclaw/fs-safe/dist/errors.js
17839
17839
  var OPERATIONAL_CODES = new Set([
@@ -18436,8 +18436,14 @@ function acquireFileLockSync(targetPath, options) {
18436
18436
  }
18437
18437
  }
18438
18438
  }
18439
+ // packages/workit-core/src/host-session.ts
18440
+ import { createHash as createHash3, randomBytes as randomBytes2, timingSafeEqual } from "node:crypto";
18441
+ import fs5 from "node:fs";
18442
+ import path5 from "node:path";
18443
+
18439
18444
  // packages/workit-core/src/core/logger.ts
18440
18445
  import os from "node:os";
18446
+ import path3 from "node:path";
18441
18447
  var REDACTED = "[REDACTED]";
18442
18448
  var SECRET_VALUE = /\b(?:Bearer|Basic|Digest|Token)\s+\S+/gi;
18443
18449
  var KEY_EQ_VALUE = /\b([A-Za-z0-9_-]*(?:token|secret|password|passwd|apikey|api[_-]?key|authorization|credential|bearer)[A-Za-z0-9_-]*)([:=]\s*).+/gi;
@@ -18480,6 +18486,22 @@ var SENSITIVE_WORDS = new Set([
18480
18486
  ]);
18481
18487
  var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
18482
18488
  var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18489
+ var resolveStateDir = () => {
18490
+ const override = process.env.WORKFLOW_TOOLKIT_STATE;
18491
+ if (override)
18492
+ return override;
18493
+ const home = os.homedir();
18494
+ if (process.env.XDG_STATE_HOME)
18495
+ return path3.join(process.env.XDG_STATE_HOME, "workit");
18496
+ switch (os.platform()) {
18497
+ case "darwin":
18498
+ return path3.join(home, "Library", "Application Support", "workit");
18499
+ case "win32":
18500
+ return path3.join(process.env.LOCALAPPDATA ?? path3.join(home, "AppData", "Local"), "workit");
18501
+ default:
18502
+ return path3.join(process.env.HOME ?? home, ".local", "state", "workit");
18503
+ }
18504
+ };
18483
18505
  var homePattern = new RegExp(`${escapeRegExp(os.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
18484
18506
  var applyPatterns = (value) => {
18485
18507
  let out = value;
@@ -18497,7 +18519,7 @@ var redactSecrets = (value) => applyPatterns(value);
18497
18519
  import { spawnSync } from "node:child_process";
18498
18520
  import { createHash as createHash2 } from "node:crypto";
18499
18521
  import fs4 from "node:fs";
18500
- import path3 from "node:path";
18522
+ import path4 from "node:path";
18501
18523
  var NOT_A_REPO = /not a git repository \(or any (of the parent directories|parent up to)/u;
18502
18524
  var gitRun = (cwd, args) => {
18503
18525
  const run = spawnSync("git", args, {
@@ -18539,14 +18561,14 @@ var DISCOVERY_ENV = [
18539
18561
  ];
18540
18562
  var isGitDir = (dir) => {
18541
18563
  try {
18542
- return fs4.statSync(path3.join(dir, "HEAD")).isFile() && fs4.statSync(path3.join(dir, "objects")).isDirectory() && fs4.statSync(path3.join(dir, "refs")).isDirectory();
18564
+ return fs4.statSync(path4.join(dir, "HEAD")).isFile() && fs4.statSync(path4.join(dir, "objects")).isDirectory() && fs4.statSync(path4.join(dir, "refs")).isDirectory();
18543
18565
  } catch {
18544
18566
  return false;
18545
18567
  }
18546
18568
  };
18547
18569
  var reftableRefs = (common) => {
18548
18570
  try {
18549
- return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(path3.join(common, "config"), "utf8"));
18571
+ return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs4.readFileSync(path4.join(common, "config"), "utf8"));
18550
18572
  } catch {
18551
18573
  return false;
18552
18574
  }
@@ -18566,7 +18588,7 @@ function fastGit(root) {
18566
18588
  if (device !== null && stat.dev !== device)
18567
18589
  return { kind: "none" };
18568
18590
  device = stat.dev;
18569
- const dotGit = path3.join(dir, ".git");
18591
+ const dotGit = path4.join(dir, ".git");
18570
18592
  let entry = null;
18571
18593
  try {
18572
18594
  entry = fs4.statSync(dotGit);
@@ -18587,19 +18609,19 @@ function fastGit(root) {
18587
18609
  const match = /^gitdir: (.+)$/mu.exec(text);
18588
18610
  if (!match)
18589
18611
  return null;
18590
- gitDir = path3.resolve(dir, match[1].trim());
18612
+ gitDir = path4.resolve(dir, match[1].trim());
18591
18613
  } else
18592
18614
  return null;
18593
- if (!fs4.existsSync(path3.join(gitDir, "HEAD")))
18615
+ if (!fs4.existsSync(path4.join(gitDir, "HEAD")))
18594
18616
  return null;
18595
18617
  } else if (isGitDir(dir))
18596
18618
  gitDir = dir;
18597
18619
  if (gitDir) {
18598
18620
  let common = gitDir;
18599
18621
  try {
18600
- common = path3.resolve(gitDir, fs4.readFileSync(path3.join(gitDir, "commondir"), "utf8").trim());
18622
+ common = path4.resolve(gitDir, fs4.readFileSync(path4.join(gitDir, "commondir"), "utf8").trim());
18601
18623
  } catch {}
18602
- if (!fs4.existsSync(path3.join(common, "objects")))
18624
+ if (!fs4.existsSync(path4.join(common, "objects")))
18603
18625
  return null;
18604
18626
  return {
18605
18627
  kind: "repo",
@@ -18609,26 +18631,26 @@ function fastGit(root) {
18609
18631
  reftable: reftableRefs(common)
18610
18632
  };
18611
18633
  }
18612
- const parent = path3.dirname(dir);
18634
+ const parent = path4.dirname(dir);
18613
18635
  if (parent === dir)
18614
18636
  return { kind: "none" };
18615
18637
  dir = parent;
18616
18638
  }
18617
18639
  }
18618
18640
  function resolveStore(root) {
18619
- root = path3.resolve(root);
18641
+ root = path4.resolve(root);
18620
18642
  let directory = false;
18621
18643
  try {
18622
18644
  directory = fs4.statSync(root).isDirectory();
18623
18645
  } catch {}
18624
18646
  if (!directory)
18625
- return { dir: path3.join(root, ".workit"), shared: false, top: null };
18647
+ return { dir: path4.join(root, ".workit"), shared: false, top: null };
18626
18648
  const fast = fastGit(root);
18627
18649
  if (fast?.kind === "none")
18628
- return { dir: path3.join(root, ".workit"), shared: false, top: null };
18650
+ return { dir: path4.join(root, ".workit"), shared: false, top: null };
18629
18651
  if (fast?.kind === "repo")
18630
18652
  return {
18631
- dir: path3.join(fast.common, "workit"),
18653
+ dir: path4.join(fast.common, "workit"),
18632
18654
  shared: true,
18633
18655
  top: fast.top,
18634
18656
  git: { gitDir: fast.gitDir, top: fast.top, reftable: fast.reftable }
@@ -18638,20 +18660,20 @@ function resolveStore(root) {
18638
18660
  if (run.ok && common) {
18639
18661
  const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
18640
18662
  return {
18641
- dir: path3.join(real(path3.resolve(root, common)), "workit"),
18663
+ dir: path4.join(real(path4.resolve(root, common)), "workit"),
18642
18664
  shared: true,
18643
18665
  top: top.ok && top.stdout.trim() ? real(top.stdout.trim()) : null
18644
18666
  };
18645
18667
  }
18646
18668
  if (run.missing || !run.ok && NOT_A_REPO.test(run.stderr))
18647
- return { dir: path3.join(root, ".workit"), shared: false, top: null };
18669
+ return { dir: path4.join(root, ".workit"), shared: false, top: null };
18648
18670
  return new Error(`cannot locate the workit store: git rev-parse failed (${run.stderr.trim().split(`
18649
18671
  `)[0] || "no output"}); fix the repository (git status should work here)`);
18650
18672
  }
18651
18673
  var rebasingBranch = (gitDir) => {
18652
18674
  for (const state of ["rebase-merge", "rebase-apply"]) {
18653
18675
  try {
18654
- const ref = fs4.readFileSync(path3.join(gitDir, state, "head-name"), "utf8").trim();
18676
+ const ref = fs4.readFileSync(path4.join(gitDir, state, "head-name"), "utf8").trim();
18655
18677
  const match = /^refs\/heads\/(.+)$/u.exec(ref);
18656
18678
  if (match)
18657
18679
  return match[1];
@@ -18666,7 +18688,7 @@ function resolveTaskKey(root, location) {
18666
18688
  if (location.git && !location.git.reftable) {
18667
18689
  let head = "";
18668
18690
  try {
18669
- head = fs4.readFileSync(path3.join(location.git.gitDir, "HEAD"), "utf8").trim();
18691
+ head = fs4.readFileSync(path4.join(location.git.gitDir, "HEAD"), "utf8").trim();
18670
18692
  } catch {}
18671
18693
  const ref = /^ref: refs\/heads\/(.+)$/u.exec(head);
18672
18694
  if (ref && ref[1] !== ".invalid")
@@ -18697,7 +18719,7 @@ function resolveTaskKey(root, location) {
18697
18719
  return { key: `detached-${shortHash(real(worktree))}`, kind: "detached", branch: null };
18698
18720
  }
18699
18721
  function checkoutRootOf(root, location) {
18700
- const resolved = path3.resolve(root);
18722
+ const resolved = path4.resolve(root);
18701
18723
  let canonical = resolved;
18702
18724
  try {
18703
18725
  canonical = fs4.realpathSync(resolved);
@@ -18728,13 +18750,13 @@ function branchRenames(location) {
18728
18750
  }
18729
18751
  for (const entry of entries)
18730
18752
  if (entry.isDirectory())
18731
- walk(path3.join(dir, entry.name));
18753
+ walk(path4.join(dir, entry.name));
18732
18754
  else
18733
- files.push(path3.join(dir, entry.name));
18755
+ files.push(path4.join(dir, entry.name));
18734
18756
  };
18735
- walk(path3.join(path3.dirname(location.dir), "logs", "refs", "heads"));
18757
+ walk(path4.join(path4.dirname(location.dir), "logs", "refs", "heads"));
18736
18758
  if (location.git)
18737
- files.push(path3.join(location.git.gitDir, "logs", "HEAD"));
18759
+ files.push(path4.join(location.git.gitDir, "logs", "HEAD"));
18738
18760
  const seen = new Set;
18739
18761
  const renames = [];
18740
18762
  for (const file of files) {
@@ -18769,7 +18791,7 @@ var checkoutSlug = (root) => {
18769
18791
  let canonical = real(root);
18770
18792
  if (process.platform === "win32")
18771
18793
  canonical = canonical.toLowerCase();
18772
- const base = path3.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
18794
+ const base = path4.basename(canonical).replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 40) || "root";
18773
18795
  return `${base}-${shortHash(canonical)}`;
18774
18796
  };
18775
18797
 
@@ -18801,30 +18823,113 @@ function hostSessionFromEnv(env) {
18801
18823
  }
18802
18824
  return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
18803
18825
  }
18826
+ var hash = (value) => createHash3("sha256").update(value).digest("hex").slice(0, 32);
18827
+ var userIssuedRoot = () => path5.join(resolveStateDir(), "issued");
18828
+ var userTokenRoot = () => path5.join(resolveStateDir(), "issued-tokens");
18829
+ function migrateRepoIssued(cwd) {
18830
+ if (cwd === undefined)
18831
+ return;
18832
+ let old;
18833
+ try {
18834
+ const location = resolveStore(cwd);
18835
+ if (location instanceof Error)
18836
+ return;
18837
+ old = path5.join(location.dir, "hooks", "issued");
18838
+ if (!fs5.existsSync(old))
18839
+ return;
18840
+ } catch {
18841
+ return;
18842
+ }
18843
+ for (const lead of safeList(old)) {
18844
+ try {
18845
+ const from = path5.join(old, lead);
18846
+ if (!fs5.statSync(from).isDirectory())
18847
+ continue;
18848
+ const to = path5.join(userIssuedRoot(), lead);
18849
+ fs5.mkdirSync(to, { recursive: true, mode: 448 });
18850
+ for (const id of fs5.readdirSync(from)) {
18851
+ try {
18852
+ fs5.copyFileSync(path5.join(from, id), path5.join(to, id), fs5.constants.COPYFILE_EXCL);
18853
+ } catch (error) {
18854
+ if (error.code !== "EEXIST")
18855
+ throw error;
18856
+ }
18857
+ fs5.rmSync(path5.join(from, id), { force: true });
18858
+ }
18859
+ removeIfEmpty(from);
18860
+ } catch {}
18861
+ }
18862
+ removeIfEmpty(old);
18863
+ }
18864
+ var safeList = (dir) => {
18865
+ try {
18866
+ return fs5.readdirSync(dir);
18867
+ } catch {
18868
+ return [];
18869
+ }
18870
+ };
18871
+ var removeIfEmpty = (dir) => {
18872
+ try {
18873
+ fs5.rmdirSync(dir);
18874
+ } catch {}
18875
+ };
18876
+ var issuedRecord = (cwd, claimed) => {
18877
+ const cut = claimed.lastIndexOf(":");
18878
+ if (cut <= 0)
18879
+ return null;
18880
+ migrateRepoIssued(cwd);
18881
+ try {
18882
+ return fs5.readFileSync(path5.join(userIssuedRoot(), hash(claimed.slice(0, cut)), hash(claimed)), "utf8");
18883
+ } catch {
18884
+ return null;
18885
+ }
18886
+ };
18887
+ var TOKEN_HASH = /^([0-9a-f]{64})$/mu;
18888
+ var issuedToken = (claimed) => {
18889
+ const cut = claimed.lastIndexOf(":");
18890
+ if (cut <= 0)
18891
+ return { stored: null, leadBound: false };
18892
+ const dir = path5.join(userTokenRoot(), hash(claimed.slice(0, cut)));
18893
+ try {
18894
+ const text = fs5.readFileSync(path5.join(dir, hash(claimed)), "utf8");
18895
+ return { stored: TOKEN_HASH.exec(text)?.[1] ?? null, leadBound: true };
18896
+ } catch {
18897
+ return { stored: null, leadBound: safeList(dir).length > 0 };
18898
+ }
18899
+ };
18900
+ function issuedBinding(cwd, claimed) {
18901
+ const record = issuedRecord(cwd, claimed);
18902
+ const { stored, leadBound } = issuedToken(claimed);
18903
+ if (stored)
18904
+ return "token";
18905
+ if (record === null)
18906
+ return null;
18907
+ return leadBound ? "token" : "legacy";
18908
+ }
18804
18909
  var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
18805
18910
 
18806
18911
  // packages/workit-core/src/core/package-root.ts
18807
18912
  import { existsSync } from "node:fs";
18808
- import path4 from "node:path";
18913
+ import path6 from "node:path";
18809
18914
  import { fileURLToPath } from "node:url";
18810
18915
  var packageRoot = () => {
18811
- let dir = path4.dirname(fileURLToPath(import.meta.url));
18916
+ let dir = path6.dirname(fileURLToPath(import.meta.url));
18812
18917
  while (true) {
18813
- const parent = path4.dirname(dir);
18814
- if (existsSync(path4.join(dir, "package.json")) || parent === dir)
18918
+ const parent = path6.dirname(dir);
18919
+ if (existsSync(path6.join(dir, "package.json")) || parent === dir)
18815
18920
  return dir;
18816
18921
  dir = parent;
18817
18922
  }
18818
18923
  };
18819
18924
  var assetRoot = () => {
18820
18925
  const root = packageRoot();
18821
- const assets = path4.join(root, "assets");
18926
+ const assets = path6.join(root, "assets");
18822
18927
  return existsSync(assets) ? assets : root;
18823
18928
  };
18824
18929
 
18825
18930
  // packages/workit-core/src/core/store-lock.ts
18826
18931
  import { spawnSync as spawnSync2 } from "node:child_process";
18827
- import fs5 from "node:fs";
18932
+ import fs6 from "node:fs";
18828
18933
  import { hostname } from "node:os";
18829
18934
  var metadataLockSchema = object2({
18830
18935
  pid: number2().int().nonnegative().safe(),
@@ -18836,8 +18941,10 @@ var metadataLockSchema = object2({
18836
18941
  var FOREIGN_LOCK_TTL_MS = 10 * 60000;
18837
18942
  var UNREADABLE_LOCK_TTL_MS = 30000;
18838
18943
  var RECLAIM_GUARD_TTL_MS = 30000;
18839
- var defaultLockTimeoutMs = 250;
18944
+ var IN_PROCESS_LOCK_TIMEOUT_MS = 250;
18945
+ var defaultLockTimeoutMs = IN_PROCESS_LOCK_TIMEOUT_MS;
18840
18946
  var defaultLockTimeout = () => defaultLockTimeoutMs;
18947
+ var hostAwareLockTimeout = (cliMs) => defaultLockTimeoutMs > IN_PROCESS_LOCK_TIMEOUT_MS ? cliMs : IN_PROCESS_LOCK_TIMEOUT_MS;
18841
18948
  var parseMetadataLock = (raw) => {
18842
18949
  let value;
18843
18950
  try {
@@ -18866,7 +18973,7 @@ var parseProcStatStart = (stat) => {
18866
18973
  var processStartOf = (pid) => {
18867
18974
  if (process.platform === "linux") {
18868
18975
  try {
18869
- return parseProcStatStart(fs5.readFileSync(`/proc/${pid}/stat`, "utf8"));
18976
+ return parseProcStatStart(fs6.readFileSync(`/proc/${pid}/stat`, "utf8"));
18870
18977
  } catch {
18871
18978
  return null;
18872
18979
  }
@@ -18887,7 +18994,7 @@ var processStartOf = (pid) => {
18887
18994
  };
18888
18995
  var readTrimmed = (file) => {
18889
18996
  try {
18890
- return fs5.readFileSync(file, "utf8").trim() || null;
18997
+ return fs6.readFileSync(file, "utf8").trim() || null;
18891
18998
  } catch {
18892
18999
  return null;
18893
19000
  }
@@ -18898,7 +19005,7 @@ var localLockHost = () => {
18898
19005
  return cachedLockHost;
18899
19006
  let pidns = null;
18900
19007
  try {
18901
- pidns = /\[(\d+)\]/.exec(fs5.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
19008
+ pidns = /\[(\d+)\]/.exec(fs6.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
18902
19009
  } catch {}
18903
19010
  const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
18904
19011
  cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
@@ -18944,7 +19051,7 @@ var classifyLockOwner = (payload, ageMs, localHost = localLockHost(), options =
18944
19051
  };
18945
19052
  var ageOf = (file, nowMs) => {
18946
19053
  try {
18947
- return nowMs - fs5.lstatSync(file).mtimeMs;
19054
+ return nowMs - fs6.lstatSync(file).mtimeMs;
18948
19055
  } catch {
18949
19056
  return null;
18950
19057
  }
@@ -18955,7 +19062,7 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
18955
19062
  if (age === null || age <= RECLAIM_GUARD_TTL_MS)
18956
19063
  return false;
18957
19064
  try {
18958
- fs5.rmdirSync(guard);
19065
+ fs6.rmdirSync(guard);
18959
19066
  return true;
18960
19067
  } catch {
18961
19068
  return false;
@@ -18964,8 +19071,8 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
18964
19071
 
18965
19072
  // packages/workit-core/src/store/event-log.ts
18966
19073
  import { randomUUID as randomUUID2 } from "node:crypto";
18967
- import fs6 from "node:fs";
18968
- import path5 from "node:path";
19074
+ import fs7 from "node:fs";
19075
+ import path7 from "node:path";
18969
19076
  var EVENT_VERSION = 1;
18970
19077
 
18971
19078
  class LogDamage extends Error {
@@ -18994,7 +19101,7 @@ var parseEvent = (line, file, offset) => {
18994
19101
  function readLog(file, from = 0, expectSeq = null) {
18995
19102
  let fd;
18996
19103
  try {
18997
- fd = fs6.openSync(file, "r");
19104
+ fd = fs7.openSync(file, "r");
18998
19105
  } catch (error) {
18999
19106
  if (error.code === "ENOENT")
19000
19107
  return null;
@@ -19003,13 +19110,13 @@ function readLog(file, from = 0, expectSeq = null) {
19003
19110
  let bytes;
19004
19111
  let size;
19005
19112
  try {
19006
- size = fs6.fstatSync(fd).size;
19113
+ size = fs7.fstatSync(fd).size;
19007
19114
  if (from > size)
19008
19115
  throw new LogDamage(`${file}: shorter than its snapshot`);
19009
19116
  bytes = Buffer.alloc(size - from);
19010
19117
  let read = 0;
19011
19118
  while (read < bytes.length) {
19012
- const got = fs6.readSync(fd, bytes, read, bytes.length - read, from + read);
19119
+ const got = fs7.readSync(fd, bytes, read, bytes.length - read, from + read);
19013
19120
  if (got === 0)
19014
19121
  break;
19015
19122
  read += got;
@@ -19017,7 +19124,7 @@ function readLog(file, from = 0, expectSeq = null) {
19017
19124
  bytes = bytes.subarray(0, read);
19018
19125
  size = from + read;
19019
19126
  } finally {
19020
- fs6.closeSync(fd);
19127
+ fs7.closeSync(fd);
19021
19128
  }
19022
19129
  const events = [];
19023
19130
  let start = 0;
@@ -19042,11 +19149,11 @@ function readLog(file, from = 0, expectSeq = null) {
19042
19149
  }
19043
19150
  var fsyncDirectory = (dir) => {
19044
19151
  try {
19045
- const fd = fs6.openSync(dir, "r");
19152
+ const fd = fs7.openSync(dir, "r");
19046
19153
  try {
19047
- fs6.fsyncSync(fd);
19154
+ fs7.fsyncSync(fd);
19048
19155
  } finally {
19049
- fs6.closeSync(fd);
19156
+ fs7.closeSync(fd);
19050
19157
  }
19051
19158
  } catch (error) {
19052
19159
  if (process.platform !== "win32")
@@ -19056,46 +19163,46 @@ var fsyncDirectory = (dir) => {
19056
19163
  function appendEvent(file, event, validEnd) {
19057
19164
  const line = Buffer.from(`${JSON.stringify(event)}
19058
19165
  `, "utf8");
19059
- const created = !fs6.existsSync(file);
19060
- if (!created && fs6.statSync(file).size !== validEnd) {
19061
- const cut = fs6.openSync(file, "r+");
19166
+ const created = !fs7.existsSync(file);
19167
+ if (!created && fs7.statSync(file).size !== validEnd) {
19168
+ const cut = fs7.openSync(file, "r+");
19062
19169
  try {
19063
- fs6.ftruncateSync(cut, validEnd);
19064
- fs6.fsyncSync(cut);
19170
+ fs7.ftruncateSync(cut, validEnd);
19171
+ fs7.fsyncSync(cut);
19065
19172
  } finally {
19066
- fs6.closeSync(cut);
19173
+ fs7.closeSync(cut);
19067
19174
  }
19068
19175
  }
19069
- const fd = fs6.openSync(file, "a", 384);
19176
+ const fd = fs7.openSync(file, "a", 384);
19070
19177
  try {
19071
19178
  let written = 0;
19072
19179
  while (written < line.length)
19073
- written += fs6.writeSync(fd, line, written, line.length - written);
19074
- fs6.fsyncSync(fd);
19180
+ written += fs7.writeSync(fd, line, written, line.length - written);
19181
+ fs7.fsyncSync(fd);
19075
19182
  } finally {
19076
- fs6.closeSync(fd);
19183
+ fs7.closeSync(fd);
19077
19184
  }
19078
19185
  if (created)
19079
- fsyncDirectory(path5.dirname(file));
19186
+ fsyncDirectory(path7.dirname(file));
19080
19187
  return validEnd;
19081
19188
  }
19082
19189
  function replaceFile(file, bytes, durable) {
19083
19190
  const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
19084
19191
  try {
19085
- const fd = fs6.openSync(temporary, "wx", 384);
19192
+ const fd = fs7.openSync(temporary, "wx", 384);
19086
19193
  try {
19087
- fs6.writeSync(fd, bytes);
19194
+ fs7.writeSync(fd, bytes);
19088
19195
  if (durable)
19089
- fs6.fsyncSync(fd);
19196
+ fs7.fsyncSync(fd);
19090
19197
  } finally {
19091
- fs6.closeSync(fd);
19198
+ fs7.closeSync(fd);
19092
19199
  }
19093
19200
  renameWithRetry(temporary, file);
19094
19201
  if (durable)
19095
- fsyncDirectory(path5.dirname(file));
19202
+ fsyncDirectory(path7.dirname(file));
19096
19203
  } catch (error) {
19097
19204
  try {
19098
- fs6.unlinkSync(temporary);
19205
+ fs7.unlinkSync(temporary);
19099
19206
  } catch {}
19100
19207
  throw error;
19101
19208
  }
@@ -19120,7 +19227,7 @@ var retryTransient = (run) => {
19120
19227
  }
19121
19228
  }
19122
19229
  };
19123
- var renameWithRetry = (from, to) => retryTransient(() => fs6.renameSync(from, to));
19230
+ var renameWithRetry = (from, to) => retryTransient(() => fs7.renameSync(from, to));
19124
19231
 
19125
19232
  // packages/workit-core/src/store/patch.ts
19126
19233
  var isPlainObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -19233,7 +19340,7 @@ var isPatch = (value) => Array.isArray(value) && value.every((op) => {
19233
19340
  });
19234
19341
 
19235
19342
  // packages/workit-core/src/store/reduce.ts
19236
- import { createHash as createHash3 } from "node:crypto";
19343
+ import { createHash as createHash4 } from "node:crypto";
19237
19344
  var OPENING_TYPES = new Set([
19238
19345
  "task.opened",
19239
19346
  "task.imported",
@@ -19262,7 +19369,7 @@ var isObject4 = (value) => typeof value === "object" && value !== null && !Array
19262
19369
  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");
19263
19370
  var BLOB_REF = "$blob";
19264
19371
  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;
19265
- var blobDigest = (value) => createHash3("sha256").update(JSON.stringify(value)).digest("hex");
19372
+ var blobDigest = (value) => createHash4("sha256").update(JSON.stringify(value)).digest("hex");
19266
19373
  var toBlob = (value, blobs) => {
19267
19374
  if (!isObject4(value))
19268
19375
  return value;
@@ -19417,7 +19524,7 @@ var indexEntry = (task, key, legacy, file) => {
19417
19524
  };
19418
19525
  var fileSignature = (file) => {
19419
19526
  try {
19420
- const stat = fs7.statSync(file, { bigint: true });
19527
+ const stat = fs8.statSync(file, { bigint: true });
19421
19528
  return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
19422
19529
  } catch {
19423
19530
  return null;
@@ -19435,7 +19542,7 @@ var cachedRuntimeVersion = null;
19435
19542
  var runtimeVersion = () => {
19436
19543
  if (cachedRuntimeVersion === null) {
19437
19544
  try {
19438
- const pkg = JSON.parse(fs7.readFileSync(path6.join(packageRoot(), "package.json"), "utf8"));
19545
+ const pkg = JSON.parse(fs8.readFileSync(path8.join(packageRoot(), "package.json"), "utf8"));
19439
19546
  cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
19440
19547
  } catch {
19441
19548
  cachedRuntimeVersion = "0.0.0";
@@ -19461,14 +19568,14 @@ var isNewerVersion = (candidate, current) => {
19461
19568
  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);
19462
19569
  var readText = (file) => {
19463
19570
  try {
19464
- return fs7.readFileSync(file, "utf8");
19571
+ return fs8.readFileSync(file, "utf8");
19465
19572
  } catch {
19466
19573
  return "";
19467
19574
  }
19468
19575
  };
19469
19576
  var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
19470
19577
  var branchStart = (location, branch, aliases) => {
19471
- const first = readText(path6.join(path6.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
19578
+ const first = readText(path8.join(path8.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
19472
19579
  `, 1)[0];
19473
19580
  const created = REFLOG_LINE.exec(first);
19474
19581
  const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
@@ -19478,7 +19585,7 @@ var branchStart = (location, branch, aliases) => {
19478
19585
  return start;
19479
19586
  if (!location.git)
19480
19587
  return null;
19481
- for (const line of readText(path6.join(location.git.gitDir, "logs", "HEAD")).split(`
19588
+ for (const line of readText(path8.join(location.git.gitDir, "logs", "HEAD")).split(`
19482
19589
  `)) {
19483
19590
  const entry = REFLOG_LINE.exec(line);
19484
19591
  const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
@@ -19488,23 +19595,23 @@ var branchStart = (location, branch, aliases) => {
19488
19595
  }
19489
19596
  return null;
19490
19597
  };
19491
- var refExists = (common, ref) => fs7.existsSync(path6.join(common, ref)) || readText(path6.join(common, "packed-refs")).includes(` ${ref}
19598
+ var refExists = (common, ref) => fs8.existsSync(path8.join(common, ref)) || readText(path8.join(common, "packed-refs")).includes(` ${ref}
19492
19599
  `);
19493
19600
  var origins = new Map;
19494
19601
  var sameDirectoryIdentity = (left, right) => {
19495
- if (!path6.isAbsolute(left) || !path6.isAbsolute(right))
19602
+ if (!path8.isAbsolute(left) || !path8.isAbsolute(right))
19496
19603
  return false;
19497
- const normalizedLeft = path6.resolve(left);
19498
- const normalizedRight = path6.resolve(right);
19604
+ const normalizedLeft = path8.resolve(left);
19605
+ const normalizedRight = path8.resolve(right);
19499
19606
  try {
19500
- const a = fs7.statSync(normalizedLeft, { bigint: true });
19501
- const b = fs7.statSync(normalizedRight, { bigint: true });
19607
+ const a = fs8.statSync(normalizedLeft, { bigint: true });
19608
+ const b = fs8.statSync(normalizedRight, { bigint: true });
19502
19609
  if (!a.isDirectory() || !b.isDirectory())
19503
19610
  return false;
19504
19611
  if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
19505
19612
  return true;
19506
- const canonicalLeft = fs7.realpathSync(normalizedLeft);
19507
- const canonicalRight = fs7.realpathSync(normalizedRight);
19613
+ const canonicalLeft = fs8.realpathSync(normalizedLeft);
19614
+ const canonicalRight = fs8.realpathSync(normalizedRight);
19508
19615
  if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
19509
19616
  return true;
19510
19617
  return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
@@ -19541,8 +19648,8 @@ class TaskStore {
19541
19648
  migrationChecked = false;
19542
19649
  compactDue = new Set;
19543
19650
  constructor(root, options = {}) {
19544
- const resolved = path6.resolve(root);
19545
- this.origin = fs7.existsSync(resolved) ? fs7.realpathSync(resolved) : resolved;
19651
+ const resolved = path8.resolve(root);
19652
+ this.origin = fs8.existsSync(resolved) ? fs8.realpathSync(resolved) : resolved;
19546
19653
  this.located = resolveStore(this.origin);
19547
19654
  this.root = checkoutRootOf(this.origin, this.located);
19548
19655
  this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
@@ -19550,7 +19657,7 @@ class TaskStore {
19550
19657
  this.migrateOnRead = options.migrateOnRead;
19551
19658
  }
19552
19659
  location() {
19553
- if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs7.existsSync(path6.join(this.root, ".git"))) {
19660
+ if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs8.existsSync(path8.join(this.root, ".git"))) {
19554
19661
  this.located = undefined;
19555
19662
  this.migrationChecked = false;
19556
19663
  }
@@ -19614,7 +19721,7 @@ class TaskStore {
19614
19721
  const tasksDir = this.paths().tasks;
19615
19722
  let names;
19616
19723
  try {
19617
- names = fs7.existsSync(tasksDir) ? fs7.readdirSync(tasksDir).filter((name) => validId(name)) : [];
19724
+ names = fs8.existsSync(tasksDir) ? fs8.readdirSync(tasksDir).filter((name) => validId(name)) : [];
19618
19725
  } catch (error) {
19619
19726
  return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
19620
19727
  path: tasksDir
@@ -19655,7 +19762,7 @@ class TaskStore {
19655
19762
  const file = this.paths().workspace;
19656
19763
  let bytes;
19657
19764
  try {
19658
- bytes = retryTransient(() => fs7.readFileSync(file, "utf8"));
19765
+ bytes = retryTransient(() => fs8.readFileSync(file, "utf8"));
19659
19766
  } catch (error) {
19660
19767
  if (error.code === "ENOENT")
19661
19768
  return success(null, null, null);
@@ -19706,7 +19813,7 @@ class TaskStore {
19706
19813
  const moved = this.bindLocked(again.data.id, key, input.now);
19707
19814
  return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
19708
19815
  }
19709
- const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path6.basename(this.root) || this.root;
19816
+ const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path8.basename(this.root) || this.root;
19710
19817
  const intent = {
19711
19818
  objective: input.objective?.trim() || `Work on ${label}`,
19712
19819
  scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
@@ -19766,14 +19873,14 @@ class TaskStore {
19766
19873
  const branch = key.ok ? key.data.branch : null;
19767
19874
  if (!branch || !location.ok)
19768
19875
  return success(null, null, null);
19769
- const common = path6.dirname(location.data.dir);
19770
- const heads = path6.join(common, "logs", "refs", "heads");
19771
- const headLog = location.data.git ? path6.join(location.data.git.gitDir, "logs", "HEAD") : "";
19876
+ const common = path8.dirname(location.data.dir);
19877
+ const heads = path8.join(common, "logs", "refs", "heads");
19878
+ const headLog = location.data.git ? path8.join(location.data.git.gitDir, "logs", "HEAD") : "";
19772
19879
  const signatures = [
19773
- path6.join(heads, branch),
19880
+ path8.join(heads, branch),
19774
19881
  headLog,
19775
19882
  heads,
19776
- path6.join(common, "packed-refs")
19883
+ path8.join(common, "packed-refs")
19777
19884
  ].map((file) => fileSignature(file) ?? "-");
19778
19885
  const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
19779
19886
  let resolved = origins.get(slot);
@@ -19785,7 +19892,7 @@ class TaskStore {
19785
19892
  aliases.push(rename.from);
19786
19893
  const start = branchStart(location.data, branch, aliases);
19787
19894
  const slash = start?.indexOf("/") ?? -1;
19788
- 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)}"]`));
19895
+ 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)}"]`));
19789
19896
  const name = start && (remote ? start.slice(slash + 1) : start);
19790
19897
  resolved = {
19791
19898
  signature: signatures.join("|"),
@@ -20023,13 +20130,13 @@ class TaskStore {
20023
20130
  if (!ready.ok)
20024
20131
  return ready;
20025
20132
  const dirs = this.paths();
20026
- const recovery = path6.join(this.root, ".workit", "recovery");
20027
- if (fs7.existsSync(recovery)) {
20133
+ const recovery = path8.join(this.root, ".workit", "recovery");
20134
+ if (fs8.existsSync(recovery)) {
20028
20135
  let files = 0;
20029
20136
  let bytes = 0;
20030
20137
  try {
20031
- for (const name of fs7.readdirSync(recovery)) {
20032
- const stat = fs7.lstatSync(path6.join(recovery, name));
20138
+ for (const name of fs8.readdirSync(recovery)) {
20139
+ const stat = fs8.lstatSync(path8.join(recovery, name));
20033
20140
  if (!stat.isFile())
20034
20141
  continue;
20035
20142
  files += 1;
@@ -20038,17 +20145,17 @@ class TaskStore {
20038
20145
  } catch {}
20039
20146
  const remove = options.pruneRecovery === true && !dryRun;
20040
20147
  if (remove)
20041
- fs7.rmSync(recovery, { recursive: true, force: true });
20148
+ fs8.rmSync(recovery, { recursive: true, force: true });
20042
20149
  report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
20043
20150
  }
20044
- if (!fs7.existsSync(dirs.tasks))
20151
+ if (!fs8.existsSync(dirs.tasks))
20045
20152
  return success(null, null, report);
20046
- const names = fs7.readdirSync(dirs.tasks).filter((name) => validId(name));
20153
+ const names = fs8.readdirSync(dirs.tasks).filter((name) => validId(name));
20047
20154
  for (const name of names) {
20048
20155
  const file = this.eventsPath(name);
20049
20156
  let size = 0;
20050
20157
  try {
20051
- size = fs7.statSync(file).size;
20158
+ size = fs8.statSync(file).size;
20052
20159
  } catch {
20053
20160
  continue;
20054
20161
  }
@@ -20076,16 +20183,16 @@ class TaskStore {
20076
20183
  const references = new Set;
20077
20184
  for (const name of names)
20078
20185
  try {
20079
- for (const match of fs7.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
20186
+ for (const match of fs8.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
20080
20187
  references.add(match[1]);
20081
20188
  } catch {}
20082
- if (fs7.existsSync(dirs.blobs))
20083
- for (const name of fs7.readdirSync(dirs.blobs)) {
20189
+ if (fs8.existsSync(dirs.blobs))
20190
+ for (const name of fs8.readdirSync(dirs.blobs)) {
20084
20191
  const digest = name.replace(/\.json$/, "");
20085
- const file = path6.join(dirs.blobs, name);
20192
+ const file = path8.join(dirs.blobs, name);
20086
20193
  let stat;
20087
20194
  try {
20088
- stat = fs7.lstatSync(file);
20195
+ stat = fs8.lstatSync(file);
20089
20196
  } catch {
20090
20197
  continue;
20091
20198
  }
@@ -20096,11 +20203,11 @@ class TaskStore {
20096
20203
  report.blobs.removed += 1;
20097
20204
  report.blobs.removedBytes += stat.size;
20098
20205
  if (!dryRun)
20099
- fs7.rmSync(file, { force: true });
20206
+ fs8.rmSync(file, { force: true });
20100
20207
  }
20101
20208
  return success(null, null, null);
20102
20209
  };
20103
- const swept = dryRun ? sweep() : this.withLock(path6.join(dirs.dir, "gc.lock"), sweep);
20210
+ const swept = dryRun ? sweep() : this.withLock(path8.join(dirs.dir, "gc.lock"), sweep);
20104
20211
  if (!swept.ok)
20105
20212
  return swept;
20106
20213
  const temporaryDirs = [
@@ -20108,17 +20215,17 @@ class TaskStore {
20108
20215
  dirs.tasks,
20109
20216
  dirs.blobs,
20110
20217
  dirs.checkout,
20111
- ...names.map((name) => path6.join(dirs.tasks, name))
20218
+ ...names.map((name) => path8.join(dirs.tasks, name))
20112
20219
  ];
20113
20220
  for (const directory of temporaryDirs) {
20114
- if (!fs7.existsSync(directory))
20221
+ if (!fs8.existsSync(directory))
20115
20222
  continue;
20116
- for (const name of fs7.readdirSync(directory)) {
20223
+ for (const name of fs8.readdirSync(directory)) {
20117
20224
  if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
20118
20225
  continue;
20119
- const file = path6.join(directory, name);
20226
+ const file = path8.join(directory, name);
20120
20227
  try {
20121
- const stat = fs7.lstatSync(file);
20228
+ const stat = fs8.lstatSync(file);
20122
20229
  if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
20123
20230
  continue;
20124
20231
  } catch {
@@ -20126,37 +20233,37 @@ class TaskStore {
20126
20233
  }
20127
20234
  report.temporary.removed += 1;
20128
20235
  if (!dryRun)
20129
- fs7.rmSync(file, { force: true });
20236
+ fs8.rmSync(file, { force: true });
20130
20237
  }
20131
20238
  }
20132
20239
  return success(null, null, report);
20133
20240
  }
20134
20241
  paths() {
20135
20242
  const location = this.location();
20136
- const dir = location.ok ? location.data.dir : path6.join(this.root, ".workit");
20137
- const checkout = path6.join(dir, "checkouts", checkoutSlug(this.root));
20243
+ const dir = location.ok ? location.data.dir : path8.join(this.root, ".workit");
20244
+ const checkout = path8.join(dir, "checkouts", checkoutSlug(this.root));
20138
20245
  return {
20139
20246
  dir,
20140
20247
  shared: location.ok ? location.data.shared : false,
20141
- marker: path6.join(dir, "store.json"),
20248
+ marker: path8.join(dir, "store.json"),
20142
20249
  checkout,
20143
- workspace: path6.join(checkout, "workspace.json"),
20144
- checkoutLock: path6.join(checkout, "metadata.lock"),
20145
- tasks: path6.join(dir, "tasks"),
20146
- index: path6.join(dir, "task-index.json"),
20147
- blobs: path6.join(dir, "blobs", "candidates"),
20148
- legacy: path6.join(dir, "legacy", checkoutSlug(this.root), "v2"),
20149
- v2: path6.join(this.root, ".workit")
20250
+ workspace: path8.join(checkout, "workspace.json"),
20251
+ checkoutLock: path8.join(checkout, "metadata.lock"),
20252
+ tasks: path8.join(dir, "tasks"),
20253
+ index: path8.join(dir, "task-index.json"),
20254
+ blobs: path8.join(dir, "blobs", "candidates"),
20255
+ legacy: path8.join(dir, "legacy", checkoutSlug(this.root), "v2"),
20256
+ v2: path8.join(this.root, ".workit")
20150
20257
  };
20151
20258
  }
20152
20259
  eventsPath(taskId) {
20153
- return path6.join(this.paths().tasks, taskId, "events.jsonl");
20260
+ return path8.join(this.paths().tasks, taskId, "events.jsonl");
20154
20261
  }
20155
20262
  snapshotPath(taskId) {
20156
- return path6.join(this.paths().tasks, taskId, "snapshot.json");
20263
+ return path8.join(this.paths().tasks, taskId, "snapshot.json");
20157
20264
  }
20158
20265
  taskLockPath(taskId) {
20159
- return path6.join(this.paths().tasks, taskId, "lock");
20266
+ return path8.join(this.paths().tasks, taskId, "lock");
20160
20267
  }
20161
20268
  ready(write = false) {
20162
20269
  const location = this.location();
@@ -20166,7 +20273,7 @@ class TaskStore {
20166
20273
  return success(null, null, null);
20167
20274
  const dirs = this.paths();
20168
20275
  try {
20169
- const marker = JSON.parse(fs7.readFileSync(dirs.marker, "utf8"));
20276
+ const marker = JSON.parse(fs8.readFileSync(dirs.marker, "utf8"));
20170
20277
  if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
20171
20278
  return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
20172
20279
  } catch {}
@@ -20195,21 +20302,21 @@ class TaskStore {
20195
20302
  return {
20196
20303
  read: (digest) => {
20197
20304
  try {
20198
- return JSON.parse(fs7.readFileSync(path6.join(dir, `${digest}.json`), "utf8"));
20305
+ return JSON.parse(fs8.readFileSync(path8.join(dir, `${digest}.json`), "utf8"));
20199
20306
  } catch (error) {
20200
20307
  throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
20201
20308
  }
20202
20309
  },
20203
20310
  write: (digest, value) => {
20204
- const file = path6.join(dir, `${digest}.json`);
20205
- if (fs7.existsSync(file)) {
20311
+ const file = path8.join(dir, `${digest}.json`);
20312
+ if (fs8.existsSync(file)) {
20206
20313
  const stamp = new Date;
20207
20314
  try {
20208
- fs7.utimesSync(file, stamp, stamp);
20315
+ fs8.utimesSync(file, stamp, stamp);
20209
20316
  return;
20210
20317
  } catch {}
20211
20318
  }
20212
- fs7.mkdirSync(dir, { recursive: true });
20319
+ fs8.mkdirSync(dir, { recursive: true });
20213
20320
  replaceFile(file, JSON.stringify(value), true);
20214
20321
  }
20215
20322
  };
@@ -20291,7 +20398,7 @@ class TaskStore {
20291
20398
  }
20292
20399
  readSnapshot(taskId) {
20293
20400
  try {
20294
- const value = JSON.parse(fs7.readFileSync(this.snapshotPath(taskId), "utf8"));
20401
+ const value = JSON.parse(fs8.readFileSync(this.snapshotPath(taskId), "utf8"));
20295
20402
  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))
20296
20403
  return null;
20297
20404
  return value;
@@ -20393,8 +20500,8 @@ class TaskStore {
20393
20500
  }
20394
20501
  }
20395
20502
  openTask(record, type, key, legacy = null) {
20396
- const dir = path6.join(this.paths().tasks, record.id);
20397
- fs7.mkdirSync(dir, { recursive: true });
20503
+ const dir = path8.join(this.paths().tasks, record.id);
20504
+ fs8.mkdirSync(dir, { recursive: true });
20398
20505
  const blobs = this.blobStore();
20399
20506
  const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
20400
20507
  const event = this.event(record.id, 1, type, {
@@ -20446,11 +20553,11 @@ class TaskStore {
20446
20553
  } catch {}
20447
20554
  if (!latest || latest.events[0]?.id !== last.id)
20448
20555
  return failure2("busy", "the task log changed during compaction; retry", { taskId });
20449
- const appended = fs7.readFileSync(file).subarray(log.end, latest.end);
20556
+ const appended = fs8.readFileSync(file).subarray(log.end, latest.end);
20450
20557
  const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
20451
20558
  replaceFile(file, bytes.toString("utf8"), true);
20452
20559
  try {
20453
- fs7.rmSync(this.snapshotPath(taskId), { force: true });
20560
+ fs8.rmSync(this.snapshotPath(taskId), { force: true });
20454
20561
  } catch {}
20455
20562
  const reloaded = this.load(taskId);
20456
20563
  if (reloaded.ok && reloaded.data)
@@ -20463,7 +20570,7 @@ class TaskStore {
20463
20570
  }
20464
20571
  countEvents(file) {
20465
20572
  try {
20466
- const bytes = fs7.readFileSync(file);
20573
+ const bytes = fs8.readFileSync(file);
20467
20574
  let count = 0;
20468
20575
  for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
20469
20576
  count += 1;
@@ -20485,8 +20592,8 @@ class TaskStore {
20485
20592
  return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
20486
20593
  }
20487
20594
  withKeyLock(key, operation) {
20488
- const digest = createHash4("sha256").update(key).digest("hex").slice(0, 32);
20489
- return this.withLock(path6.join(this.paths().dir, "keys", `${digest}.lock`), operation);
20595
+ const digest = createHash5("sha256").update(key).digest("hex").slice(0, 32);
20596
+ return this.withLock(path8.join(this.paths().dir, "keys", `${digest}.lock`), operation);
20490
20597
  }
20491
20598
  bindLocked(taskId, key, timestamp) {
20492
20599
  const current = this.readWorkspace();
@@ -20627,7 +20734,7 @@ class TaskStore {
20627
20734
  writeWorkspace(record) {
20628
20735
  const file = this.paths().workspace;
20629
20736
  try {
20630
- fs7.mkdirSync(path6.dirname(file), { recursive: true });
20737
+ fs8.mkdirSync(path8.dirname(file), { recursive: true });
20631
20738
  replaceFile(file, `${canonicalJson(record)}
20632
20739
  `, true);
20633
20740
  return success(null, null, null);
@@ -20637,7 +20744,7 @@ class TaskStore {
20637
20744
  }
20638
20745
  readIndex() {
20639
20746
  try {
20640
- const value = JSON.parse(fs7.readFileSync(this.paths().index, "utf8"));
20747
+ const value = JSON.parse(fs8.readFileSync(this.paths().index, "utf8"));
20641
20748
  if (!isObject5(value) || value.version !== INDEX_VERSION || !isObject5(value.tasks))
20642
20749
  return {};
20643
20750
  const tasks = {};
@@ -20651,7 +20758,7 @@ class TaskStore {
20651
20758
  }
20652
20759
  writeIndex(entries) {
20653
20760
  try {
20654
- fs7.mkdirSync(this.paths().tasks, { recursive: true });
20761
+ fs8.mkdirSync(this.paths().tasks, { recursive: true });
20655
20762
  replaceFile(this.paths().index, JSON.stringify({
20656
20763
  version: INDEX_VERSION,
20657
20764
  tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
@@ -20705,23 +20812,23 @@ class TaskStore {
20705
20812
  return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
20706
20813
  }
20707
20814
  v2Dirs() {
20708
- return [...new Set([this.root, this.origin])].map((dir) => path6.join(dir, ".workit"));
20815
+ return [...new Set([this.root, this.origin])].map((dir) => path8.join(dir, ".workit"));
20709
20816
  }
20710
20817
  pendingMigrations() {
20711
20818
  return this.v2Dirs().filter((dir) => {
20712
- const raw = this.readV2Workspace(path6.join(dir, "workspace.json"));
20819
+ const raw = this.readV2Workspace(path8.join(dir, "workspace.json"));
20713
20820
  return raw !== null && raw !== TOMBSTONE;
20714
20821
  });
20715
20822
  }
20716
20823
  migrateV2(v2) {
20717
20824
  const dirs = this.paths();
20718
- const v2Workspace = path6.join(v2, "workspace.json");
20719
- const v2Tasks = path6.join(v2, "tasks");
20720
- const legacy = path6.join(dirs.dir, "legacy", checkoutSlug(path6.dirname(v2)), "v2");
20825
+ const v2Workspace = path8.join(v2, "workspace.json");
20826
+ const v2Tasks = path8.join(v2, "tasks");
20827
+ const legacy = path8.join(dirs.dir, "legacy", checkoutSlug(path8.dirname(v2)), "v2");
20721
20828
  const raw = this.readV2Workspace(v2Workspace);
20722
20829
  if (raw === null || raw === TOMBSTONE)
20723
20830
  return success(null, null, null);
20724
- return this.withLock(dirs.checkoutLock, () => this.withLock(path6.join(v2, "metadata.lock"), () => {
20831
+ return this.withLock(dirs.checkoutLock, () => this.withLock(path8.join(v2, "metadata.lock"), () => {
20725
20832
  const workspace = this.readV2Workspace(v2Workspace);
20726
20833
  if (workspace === null || workspace === TOMBSTONE)
20727
20834
  return success(null, null, null);
@@ -20730,7 +20837,7 @@ class TaskStore {
20730
20837
  return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
20731
20838
  let files = [];
20732
20839
  try {
20733
- files = fs7.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path6.join(v2Tasks, name)));
20840
+ files = fs8.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path8.join(v2Tasks, name)));
20734
20841
  } catch {}
20735
20842
  const current = this.readWorkspace();
20736
20843
  if (!current.ok)
@@ -20748,22 +20855,22 @@ class TaskStore {
20748
20855
  let skipped = 0;
20749
20856
  const done = new Map;
20750
20857
  try {
20751
- fs7.mkdirSync(path6.join(legacy, "tasks"), { recursive: true });
20858
+ fs8.mkdirSync(path8.join(legacy, "tasks"), { recursive: true });
20752
20859
  for (const name of ["workspace.json", "index.json"])
20753
- if (fs7.existsSync(path6.join(v2, name)))
20754
- fs7.copyFileSync(path6.join(v2, name), path6.join(legacy, name));
20860
+ if (fs8.existsSync(path8.join(v2, name)))
20861
+ fs8.copyFileSync(path8.join(v2, name), path8.join(legacy, name));
20755
20862
  for (const name of files) {
20756
20863
  const id = name.slice(0, -5);
20757
- const file = path6.join(v2Tasks, name);
20758
- const bytes = fs7.readFileSync(file, "utf8");
20864
+ const file = path8.join(v2Tasks, name);
20865
+ const bytes = fs8.readFileSync(file, "utf8");
20759
20866
  const digest = sha256(bytes);
20760
- fs7.writeFileSync(path6.join(legacy, "tasks", name), bytes);
20867
+ fs8.writeFileSync(path8.join(legacy, "tasks", name), bytes);
20761
20868
  const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
20762
20869
  if (!parsed.ok || parsed.data.id !== id)
20763
20870
  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 });
20764
20871
  const record = { ...parsed.data, workspaceId: target.id };
20765
- const origin = { path: path6.join(legacy, "tasks", name), digest };
20766
- if (!fs7.existsSync(this.eventsPath(id))) {
20872
+ const origin = { path: path8.join(legacy, "tasks", name), digest };
20873
+ if (!fs8.existsSync(this.eventsPath(id))) {
20767
20874
  this.openTask(record, "migrated.from_v2", null, origin);
20768
20875
  migrated += 1;
20769
20876
  } else {
@@ -20798,8 +20905,8 @@ class TaskStore {
20798
20905
  try {
20799
20906
  let complete = true;
20800
20907
  for (const [name, digest] of done) {
20801
- const file = path6.join(v2Tasks, name);
20802
- if (sha256(fs7.readFileSync(file, "utf8")) !== digest) {
20908
+ const file = path8.join(v2Tasks, name);
20909
+ if (sha256(fs8.readFileSync(file, "utf8")) !== digest) {
20803
20910
  complete = false;
20804
20911
  continue;
20805
20912
  }
@@ -20809,7 +20916,7 @@ class TaskStore {
20809
20916
  return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
20810
20917
  path: v2
20811
20918
  });
20812
- fs7.rmSync(path6.join(v2, "index.json"), { force: true });
20919
+ fs8.rmSync(path8.join(v2, "index.json"), { force: true });
20813
20920
  this.writeTombstone(v2, target.id);
20814
20921
  } catch (error) {
20815
20922
  return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
@@ -20829,45 +20936,45 @@ class TaskStore {
20829
20936
  }
20830
20937
  adoptLocalStore() {
20831
20938
  const dirs = this.paths();
20832
- const marker = path6.join(dirs.v2, "store.json");
20833
- if (!dirs.shared || !fs7.existsSync(marker))
20939
+ const marker = path8.join(dirs.v2, "store.json");
20940
+ if (!dirs.shared || !fs8.existsSync(marker))
20834
20941
  return success(null, null, null);
20835
20942
  return this.withLock(dirs.checkoutLock, () => {
20836
- if (!fs7.existsSync(marker))
20943
+ if (!fs8.existsSync(marker))
20837
20944
  return success(null, null, null);
20838
20945
  let tasks = 0;
20839
20946
  try {
20840
20947
  const merge = (from, to) => {
20841
- if (!fs7.existsSync(to)) {
20842
- fs7.mkdirSync(path6.dirname(to), { recursive: true });
20948
+ if (!fs8.existsSync(to)) {
20949
+ fs8.mkdirSync(path8.dirname(to), { recursive: true });
20843
20950
  try {
20844
- fs7.renameSync(from, to);
20951
+ fs8.renameSync(from, to);
20845
20952
  } catch {
20846
- fs7.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
20847
- fs7.rmSync(from, { recursive: true, force: true });
20953
+ fs8.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
20954
+ fs8.rmSync(from, { recursive: true, force: true });
20848
20955
  }
20849
20956
  return;
20850
20957
  }
20851
- if (fs7.lstatSync(from).isDirectory() && fs7.lstatSync(to).isDirectory())
20852
- for (const name of fs7.readdirSync(from))
20853
- merge(path6.join(from, name), path6.join(to, name));
20958
+ if (fs8.lstatSync(from).isDirectory() && fs8.lstatSync(to).isDirectory())
20959
+ for (const name of fs8.readdirSync(from))
20960
+ merge(path8.join(from, name), path8.join(to, name));
20854
20961
  };
20855
20962
  for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
20856
- const from = path6.join(dirs.v2, sub);
20857
- if (!fs7.existsSync(from))
20963
+ const from = path8.join(dirs.v2, sub);
20964
+ if (!fs8.existsSync(from))
20858
20965
  continue;
20859
20966
  if (sub === "tasks") {
20860
- for (const name of fs7.readdirSync(from))
20861
- if (validId(name) && !fs7.existsSync(path6.join(dirs.dir, sub, name)))
20967
+ for (const name of fs8.readdirSync(from))
20968
+ if (validId(name) && !fs8.existsSync(path8.join(dirs.dir, sub, name)))
20862
20969
  tasks += 1;
20863
20970
  }
20864
- merge(from, path6.join(dirs.dir, sub));
20971
+ merge(from, path8.join(dirs.dir, sub));
20865
20972
  }
20866
- const ledger = path6.join(dirs.v2, "ledger");
20867
- if (fs7.existsSync(ledger) && !fs7.existsSync(path6.join(dirs.dir, "ledger")))
20868
- merge(ledger, path6.join(dirs.dir, "ledger"));
20869
- fs7.rmSync(path6.join(dirs.v2, "task-index.json"), { force: true });
20870
- fs7.renameSync(marker, path6.join(dirs.v2, "store.moved.json"));
20973
+ const ledger = path8.join(dirs.v2, "ledger");
20974
+ if (fs8.existsSync(ledger) && !fs8.existsSync(path8.join(dirs.dir, "ledger")))
20975
+ merge(ledger, path8.join(dirs.dir, "ledger"));
20976
+ fs8.rmSync(path8.join(dirs.v2, "task-index.json"), { force: true });
20977
+ fs8.renameSync(marker, path8.join(dirs.v2, "store.moved.json"));
20871
20978
  } catch (error) {
20872
20979
  return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
20873
20980
  path: dirs.v2
@@ -20887,7 +20994,7 @@ class TaskStore {
20887
20994
  readV2Workspace(file) {
20888
20995
  let bytes;
20889
20996
  try {
20890
- bytes = fs7.readFileSync(file, "utf8");
20997
+ bytes = fs8.readFileSync(file, "utf8");
20891
20998
  } catch {
20892
20999
  return null;
20893
21000
  }
@@ -20905,7 +21012,7 @@ class TaskStore {
20905
21012
  schemaVersion: SCHEMA_VERSION,
20906
21013
  id: workspaceId ?? newId(),
20907
21014
  revision: newRevision(),
20908
- root: path6.dirname(v2),
21015
+ root: path8.dirname(v2),
20909
21016
  runtime: { createdWith: version, updatedWith: version },
20910
21017
  writer: null,
20911
21018
  store: {
@@ -20916,12 +21023,12 @@ class TaskStore {
20916
21023
  },
20917
21024
  critical: ["store"]
20918
21025
  };
20919
- fs7.mkdirSync(v2, { recursive: true });
20920
- const ignore = path6.join(v2, ".gitignore");
20921
- if (!fs7.existsSync(ignore))
20922
- fs7.writeFileSync(ignore, `*
21026
+ fs8.mkdirSync(v2, { recursive: true });
21027
+ const ignore = path8.join(v2, ".gitignore");
21028
+ if (!fs8.existsSync(ignore))
21029
+ fs8.writeFileSync(ignore, `*
20923
21030
  `);
20924
- replaceFile(path6.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
21031
+ replaceFile(path8.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
20925
21032
  `, true);
20926
21033
  }
20927
21034
  stubFor(taskId) {
@@ -20937,7 +21044,7 @@ class TaskStore {
20937
21044
  }
20938
21045
  isStub(file) {
20939
21046
  try {
20940
- const value = JSON.parse(fs7.readFileSync(file, "utf8"));
21047
+ const value = JSON.parse(fs8.readFileSync(file, "utf8"));
20941
21048
  return isObject5(value) && isObject5(value.store) && value.store.format === "workit-store";
20942
21049
  } catch {
20943
21050
  return false;
@@ -20945,32 +21052,32 @@ class TaskStore {
20945
21052
  }
20946
21053
  writeTombstoneIfMissing() {
20947
21054
  try {
20948
- const v2 = path6.join(this.root, ".workit");
20949
- if (fs7.existsSync(path6.join(v2, "workspace.json")))
21055
+ const v2 = path8.join(this.root, ".workit");
21056
+ if (fs8.existsSync(path8.join(v2, "workspace.json")))
20950
21057
  return;
20951
21058
  let id = null;
20952
21059
  try {
20953
- id = JSON.parse(fs7.readFileSync(this.paths().workspace, "utf8")).id ?? null;
21060
+ id = JSON.parse(fs8.readFileSync(this.paths().workspace, "utf8")).id ?? null;
20954
21061
  } catch {}
20955
21062
  this.writeTombstone(v2, id);
20956
21063
  } catch {}
20957
21064
  }
20958
21065
  initializeStorage() {
20959
21066
  const dirs = this.paths();
20960
- retryTransient(() => fs7.mkdirSync(dirs.checkout, { recursive: true }));
20961
- retryTransient(() => fs7.mkdirSync(dirs.tasks, { recursive: true }));
20962
- if (!fs7.existsSync(dirs.marker))
21067
+ retryTransient(() => fs8.mkdirSync(dirs.checkout, { recursive: true }));
21068
+ retryTransient(() => fs8.mkdirSync(dirs.tasks, { recursive: true }));
21069
+ if (!fs8.existsSync(dirs.marker))
20963
21070
  replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
20964
21071
  `, false);
20965
21072
  if (!dirs.shared) {
20966
- const ignore = path6.join(dirs.dir, ".gitignore");
21073
+ const ignore = path8.join(dirs.dir, ".gitignore");
20967
21074
  let current = null;
20968
21075
  try {
20969
- current = fs7.readFileSync(ignore, "utf8");
21076
+ current = fs8.readFileSync(ignore, "utf8");
20970
21077
  } catch {}
20971
21078
  if (current !== `*
20972
21079
  `)
20973
- retryTransient(() => fs7.writeFileSync(ignore, `*
21080
+ retryTransient(() => fs8.writeFileSync(ignore, `*
20974
21081
  `));
20975
21082
  }
20976
21083
  this.writeTombstoneIfMissing();
@@ -20986,7 +21093,7 @@ class TaskStore {
20986
21093
  shouldReclaim: ({ payload, nowMs }) => {
20987
21094
  let ageMs = null;
20988
21095
  try {
20989
- ageMs = nowMs - fs7.lstatSync(lockPath).mtimeMs;
21096
+ ageMs = nowMs - fs8.lstatSync(lockPath).mtimeMs;
20990
21097
  } catch {}
20991
21098
  return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
20992
21099
  },
@@ -21023,7 +21130,7 @@ class TaskStore {
21023
21130
  withLock(lockPath, operation) {
21024
21131
  try {
21025
21132
  this.initializeStorage();
21026
- fs7.mkdirSync(path6.dirname(lockPath), { recursive: true });
21133
+ fs8.mkdirSync(path8.dirname(lockPath), { recursive: true });
21027
21134
  } catch (error) {
21028
21135
  return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
21029
21136
  path: this.paths().dir
@@ -21063,18 +21170,18 @@ class TaskStore {
21063
21170
  lockDirWritable(lockPath) {
21064
21171
  const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
21065
21172
  try {
21066
- fs7.closeSync(fs7.openSync(probe, "wx", 384));
21173
+ fs8.closeSync(fs8.openSync(probe, "wx", 384));
21067
21174
  } catch {
21068
21175
  return false;
21069
21176
  }
21070
21177
  try {
21071
- fs7.rmSync(probe, { force: true });
21178
+ fs8.rmSync(probe, { force: true });
21072
21179
  } catch {}
21073
21180
  return true;
21074
21181
  }
21075
21182
  lockHolderPresent(lockPath) {
21076
21183
  try {
21077
- fs7.lstatSync(lockPath);
21184
+ fs8.lstatSync(lockPath);
21078
21185
  return true;
21079
21186
  } catch (error) {
21080
21187
  return error?.code !== "ENOENT";
@@ -21086,12 +21193,12 @@ class TaskStore {
21086
21193
  if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
21087
21194
  return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
21088
21195
  path: lockPath,
21089
- guidance: `Check permissions and the read-only attribute on ${path6.dirname(lockPath)}.`
21196
+ guidance: `Check permissions and the read-only attribute on ${path8.dirname(lockPath)}.`
21090
21197
  });
21091
21198
  if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
21092
21199
  let lock = null;
21093
21200
  try {
21094
- lock = parseMetadataLockOrNull(fs7.readFileSync(lockPath, "utf8"));
21201
+ lock = parseMetadataLockOrNull(fs8.readFileSync(lockPath, "utf8"));
21095
21202
  } catch {}
21096
21203
  return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
21097
21204
  outcome: "not_started",
@@ -21109,15 +21216,15 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
21109
21216
  var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
21110
21217
 
21111
21218
  // packages/workit-core/src/core/task-evaluation.ts
21112
- import { createHash as createHash9 } from "node:crypto";
21113
- import * as fs12 from "node:fs";
21219
+ import { createHash as createHash10 } from "node:crypto";
21220
+ import * as fs13 from "node:fs";
21114
21221
  import { spawnSync as spawnSync10 } from "node:child_process";
21115
- import path14 from "node:path";
21222
+ import path16 from "node:path";
21116
21223
 
21117
21224
  // packages/workit-core/src/check-config.ts
21118
21225
  import { spawnSync as spawnSync3 } from "node:child_process";
21119
- import fs8 from "node:fs";
21120
- import path7 from "node:path";
21226
+ import fs9 from "node:fs";
21227
+ import path9 from "node:path";
21121
21228
  var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
21122
21229
  var CHECKS_FILE = "workit.checks.json";
21123
21230
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -21170,10 +21277,10 @@ var git = (cwd, args) => {
21170
21277
  return value || null;
21171
21278
  };
21172
21279
  function checkRoot(cwd) {
21173
- return path7.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
21280
+ return path9.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
21174
21281
  }
21175
21282
  var packageManager = (root) => {
21176
- const has = (file) => fs8.existsSync(path7.join(root, file));
21283
+ const has = (file) => fs9.existsSync(path9.join(root, file));
21177
21284
  if (has("bun.lock") || has("bun.lockb"))
21178
21285
  return "bun";
21179
21286
  if (has("pnpm-lock.yaml"))
@@ -21193,7 +21300,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
21193
21300
  function detectedChecks(root) {
21194
21301
  const read = (file) => {
21195
21302
  try {
21196
- return fs8.readFileSync(path7.join(root, file), "utf8");
21303
+ return fs9.readFileSync(path9.join(root, file), "utf8");
21197
21304
  } catch {
21198
21305
  return null;
21199
21306
  }
@@ -21232,7 +21339,7 @@ function detectedChecks(root) {
21232
21339
  var GATES = ["testing", "verification"];
21233
21340
  function loadCheckConfig(cwd) {
21234
21341
  const root = checkRoot(cwd);
21235
- const file = path7.join(root, CHECKS_FILE);
21342
+ const file = path9.join(root, CHECKS_FILE);
21236
21343
  const none = (error = null) => ({
21237
21344
  source: error ? CHECKS_FILE : "none",
21238
21345
  root,
@@ -21242,7 +21349,7 @@ function loadCheckConfig(cwd) {
21242
21349
  });
21243
21350
  let text = null;
21244
21351
  try {
21245
- text = fs8.readFileSync(file, "utf8");
21352
+ text = fs9.readFileSync(file, "utf8");
21246
21353
  } catch (error) {
21247
21354
  if (error.code !== "ENOENT")
21248
21355
  return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
@@ -21305,10 +21412,10 @@ function workitAvailable(env = process.env) {
21305
21412
  return workitOnPath;
21306
21413
  const windows = process.platform === "win32";
21307
21414
  const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
21308
- const dirs = (env.PATH ?? env.Path ?? "").split(path7.delimiter).filter(Boolean);
21415
+ const dirs = (env.PATH ?? env.Path ?? "").split(path9.delimiter).filter(Boolean);
21309
21416
  workitOnPath = dirs.some((dir) => exts.some((ext) => {
21310
21417
  try {
21311
- return fs8.statSync(path7.join(dir, `workit${ext}`)).isFile();
21418
+ return fs9.statSync(path9.join(dir, `workit${ext}`)).isFile();
21312
21419
  } catch {
21313
21420
  return false;
21314
21421
  }
@@ -21321,10 +21428,10 @@ function checkCommand(args, version, onPath = workitAvailable()) {
21321
21428
 
21322
21429
  // packages/workit-core/src/git/rev.ts
21323
21430
  import { spawnSync as spawnSync4 } from "node:child_process";
21324
- import { createHash as createHash5 } from "node:crypto";
21325
- import fs9 from "node:fs";
21431
+ import { createHash as createHash6 } from "node:crypto";
21432
+ import fs10 from "node:fs";
21326
21433
  import os2 from "node:os";
21327
- import path8 from "node:path";
21434
+ import path10 from "node:path";
21328
21435
  var MAX_BUFFER = 256 * 1024 * 1024;
21329
21436
  var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
21330
21437
  var git2 = (cwd, args, options = {}) => {
@@ -21359,10 +21466,10 @@ function headSha(cwd) {
21359
21466
  var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
21360
21467
  var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
21361
21468
  var seedIndex = (from, to) => {
21362
- fs9.copyFileSync(from, to);
21363
- const { atimeNs, mtimeNs } = fs9.statSync(from, { bigint: true });
21469
+ fs10.copyFileSync(from, to);
21470
+ const { atimeNs, mtimeNs } = fs10.statSync(from, { bigint: true });
21364
21471
  const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
21365
- fs9.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
21472
+ fs10.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
21366
21473
  };
21367
21474
  function worktreeTree(cwd, options = {}) {
21368
21475
  const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
@@ -21371,10 +21478,10 @@ function worktreeTree(cwd, options = {}) {
21371
21478
  return null;
21372
21479
  const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
21373
21480
  const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
21374
- const scratch = fs9.mkdtempSync(path8.join(os2.tmpdir(), "workit-tree-"));
21481
+ const scratch = fs10.mkdtempSync(path10.join(os2.tmpdir(), "workit-tree-"));
21375
21482
  try {
21376
- const tempIndex = path8.join(scratch, "index");
21377
- if (fs9.existsSync(indexPath))
21483
+ const tempIndex = path10.join(scratch, "index");
21484
+ if (fs10.existsSync(indexPath))
21378
21485
  seedIndex(indexPath, tempIndex);
21379
21486
  const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
21380
21487
  const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
@@ -21386,7 +21493,7 @@ function worktreeTree(cwd, options = {}) {
21386
21493
  const skipped = [];
21387
21494
  for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
21388
21495
  try {
21389
- const stat = fs9.statSync(path8.join(top, file));
21496
+ const stat = fs10.statSync(path10.join(top, file));
21390
21497
  if (stat.isFile() && stat.size > cap)
21391
21498
  skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
21392
21499
  } catch {}
@@ -21403,10 +21510,10 @@ function worktreeTree(cwd, options = {}) {
21403
21510
  if (!tree)
21404
21511
  return null;
21405
21512
  const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
21406
- const key = skipped.length ? `sha256:${createHash5("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
21513
+ const key = skipped.length ? `sha256:${createHash6("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
21407
21514
  return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
21408
21515
  } finally {
21409
- fs9.rmSync(scratch, { recursive: true, force: true });
21516
+ fs10.rmSync(scratch, { recursive: true, force: true });
21410
21517
  }
21411
21518
  }
21412
21519
  function worktreeSignal(cwd, options = {}) {
@@ -21426,16 +21533,16 @@ function worktreeSignal(cwd, options = {}) {
21426
21533
  });
21427
21534
  if (!status.ok)
21428
21535
  return null;
21429
- const hash = createHash5("sha256");
21536
+ const hash = createHash6("sha256");
21430
21537
  hash.update(`${headSha(top) ?? "unborn"}\x00`);
21431
21538
  for (const entry of status.stdout.split("\x00")) {
21432
21539
  if (entry.length < 4)
21433
21540
  continue;
21434
21541
  const file = entry.slice(3);
21435
- const absolute = path8.join(top, file);
21542
+ const absolute = path10.join(top, file);
21436
21543
  let stat = "-";
21437
21544
  try {
21438
- const info = fs9.lstatSync(absolute, { bigint: true });
21545
+ const info = fs10.lstatSync(absolute, { bigint: true });
21439
21546
  stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
21440
21547
  if (info.isDirectory())
21441
21548
  stat += `:${headSha(absolute) ?? "-"}`;
@@ -21502,17 +21609,17 @@ function pushRemoteName(cwd, branch) {
21502
21609
 
21503
21610
  // packages/workit-core/src/core/policy/derive.ts
21504
21611
  import { existsSync as existsSync4 } from "node:fs";
21505
- import path13 from "node:path";
21612
+ import path15 from "node:path";
21506
21613
 
21507
21614
  // packages/workit-core/src/ledger.ts
21508
21615
  import { spawnSync as spawnSync9 } from "node:child_process";
21509
- import { createHash as createHash8, randomBytes as randomBytes2 } from "node:crypto";
21510
- import fs11 from "node:fs";
21511
- import path12 from "node:path";
21616
+ import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
21617
+ import fs12 from "node:fs";
21618
+ import path14 from "node:path";
21512
21619
 
21513
21620
  // packages/workit-core/src/core/vcs-config.ts
21514
- import fs10 from "node:fs";
21515
- import path11 from "node:path";
21621
+ import fs11 from "node:fs";
21622
+ import path13 from "node:path";
21516
21623
  import { spawnSync as spawnSync8 } from "node:child_process";
21517
21624
 
21518
21625
  // packages/workit-core/src/core/config.ts
@@ -21526,7 +21633,7 @@ import {
21526
21633
  writeFileSync as writeFileSync2
21527
21634
  } from "node:fs";
21528
21635
  import os3 from "node:os";
21529
- import path9 from "node:path";
21636
+ import path11 from "node:path";
21530
21637
 
21531
21638
  // packages/workit-core/src/core/boundary.ts
21532
21639
  import { statSync as statSync2 } from "node:fs";
@@ -21585,7 +21692,7 @@ var mergePreset = (preset, input = {}, current = {
21585
21692
  protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
21586
21693
  };
21587
21694
  };
21588
- 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");
21695
+ 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");
21589
21696
  var migratedDir = null;
21590
21697
  var migrationFailed = false;
21591
21698
  var ensureConfigDir = (dir = resolveConfigDir()) => {
@@ -21595,7 +21702,7 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
21595
21702
  migratedDir = dir;
21596
21703
  return dir;
21597
21704
  }
21598
- const legacy = path9.join(process.env.XDG_CONFIG_HOME || path9.join(os3.homedir(), ".config"), "workflow-toolkit");
21705
+ const legacy = path11.join(process.env.XDG_CONFIG_HOME || path11.join(os3.homedir(), ".config"), "workflow-toolkit");
21599
21706
  if (!existsSync3(legacy)) {
21600
21707
  migratedDir = dir;
21601
21708
  return dir;
@@ -21608,8 +21715,8 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
21608
21715
  mkdirSync2(dir, { recursive: true });
21609
21716
  diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
21610
21717
  for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
21611
- const src = path9.join(legacy, entry.name);
21612
- const dest = path9.join(dir, entry.name);
21718
+ const src = path11.join(legacy, entry.name);
21719
+ const dest = path11.join(dir, entry.name);
21613
21720
  if (existsSync3(dest))
21614
21721
  continue;
21615
21722
  try {
@@ -21681,7 +21788,7 @@ var parseConfigResult = (raw, file) => {
21681
21788
  return { status: "valid", path: file, config };
21682
21789
  };
21683
21790
  var readConfigTyped = (dir) => {
21684
- const file = path9.join(dir ?? configDir(), "config.json");
21791
+ const file = path11.join(dir ?? configDir(), "config.json");
21685
21792
  return parseConfigResult(readSafe(file), file);
21686
21793
  };
21687
21794
  var readConfig = () => {
@@ -21690,7 +21797,7 @@ var readConfig = () => {
21690
21797
  throw new Error(result.error);
21691
21798
  return result.config ?? DEFAULTS;
21692
21799
  };
21693
- var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path9.join(configDir(), "vcs.json");
21800
+ var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path11.join(configDir(), "vcs.json");
21694
21801
  var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
21695
21802
  var resolveBranchPolicy = (config, workspace, repoPreset) => {
21696
21803
  const wp = workspace?.branchPolicy ?? {};
@@ -21722,9 +21829,9 @@ var COMMIT_PRESETS = [
21722
21829
 
21723
21830
  // packages/workit-core/src/core/workspaces.ts
21724
21831
  import { spawnSync as spawnSync6 } from "node:child_process";
21725
- import { createHash as createHash6 } from "node:crypto";
21832
+ import { createHash as createHash7 } from "node:crypto";
21726
21833
  import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
21727
- import path10 from "node:path";
21834
+ import path12 from "node:path";
21728
21835
 
21729
21836
  // packages/workit-core/src/core/branch-policy.ts
21730
21837
  import { spawnSync as spawnSync5 } from "node:child_process";
@@ -21849,7 +21956,7 @@ var releaseTrackSchema = object2({
21849
21956
  context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
21850
21957
  }
21851
21958
  }
21852
- 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(".."))) {
21959
+ 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(".."))) {
21853
21960
  context.addIssue({
21854
21961
  code: "custom",
21855
21962
  path: ["versionSource", "path"],
@@ -21910,7 +22017,7 @@ var workspacesFileSchema = object2({ workspaces: array(workspaceConfigSchema).op
21910
22017
  names.add(workspace.name);
21911
22018
  }
21912
22019
  });
21913
- var workspacesRevision = (contents) => createHash6("sha256").update(contents ?? "<missing>").digest("hex");
22020
+ var workspacesRevision = (contents) => createHash7("sha256").update(contents ?? "<missing>").digest("hex");
21914
22021
  var validateWorkspacesDocument = (parsed, file) => {
21915
22022
  if (!isConfigObject(parsed)) {
21916
22023
  return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
@@ -21948,7 +22055,7 @@ var validateWorkspacesDocument = (parsed, file) => {
21948
22055
  };
21949
22056
  };
21950
22057
  var readWorkspacesResult = (dir = configDir()) => {
21951
- const file = path10.join(dir, "workspaces.json");
22058
+ const file = path12.join(dir, "workspaces.json");
21952
22059
  let raw;
21953
22060
  try {
21954
22061
  raw = readFileSync3(file, "utf8");
@@ -22059,9 +22166,9 @@ var mainCheckoutOf = (cwd) => {
22059
22166
  let main = null;
22060
22167
  const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
22061
22168
  const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
22062
- if (gitDir && common && path10.resolve(gitDir) !== path10.resolve(common)) {
22169
+ if (gitDir && common && path12.resolve(gitDir) !== path12.resolve(common)) {
22063
22170
  const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
22064
- main = listed?.trim() || (path10.basename(common) === ".git" ? path10.dirname(common) : common);
22171
+ main = listed?.trim() || (path12.basename(common) === ".git" ? path12.dirname(common) : common);
22065
22172
  }
22066
22173
  mainCheckouts.set(cwd, main);
22067
22174
  return main;
@@ -22152,7 +22259,7 @@ var runtimeWorkspaceIndexSchema = object2({
22152
22259
  workspaces: array(object2({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
22153
22260
  }).passthrough();
22154
22261
  var matchRuntimeWorkspace = (cwd, dir = configDir()) => {
22155
- const file = path10.join(dir, "workspaces.json");
22262
+ const file = path12.join(dir, "workspaces.json");
22156
22263
  let raw;
22157
22264
  try {
22158
22265
  raw = readFileSync3(file, "utf8");
@@ -22227,7 +22334,7 @@ function workspaceDefaultTarget(cwd, globalDefault) {
22227
22334
  }
22228
22335
 
22229
22336
  // packages/workit-core/src/core/release-tracks.ts
22230
- import { createHash as createHash7 } from "node:crypto";
22337
+ import { createHash as createHash8 } from "node:crypto";
22231
22338
  import { spawnSync as spawnSync7 } from "node:child_process";
22232
22339
  var KNOWN_FIELDS = new Set([
22233
22340
  "strategy",
@@ -22597,7 +22704,7 @@ function resolveTrackFor(cwd, options) {
22597
22704
  ]) ?? "",
22598
22705
  git3(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
22599
22706
  ] : [];
22600
- key = createHash7("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
22707
+ key = createHash8("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
22601
22708
  const hit = memo2.get(key);
22602
22709
  if (hit)
22603
22710
  return hit;
@@ -22612,7 +22719,7 @@ function resolveTrackFor(cwd, options) {
22612
22719
  }
22613
22720
 
22614
22721
  // packages/workit-core/src/core/vcs-config.ts
22615
- var workspacesPath = () => path11.join(configDir(), "workspaces.json");
22722
+ var workspacesPath = () => path13.join(configDir(), "workspaces.json");
22616
22723
  var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
22617
22724
  var remoteProvider = (cwd) => {
22618
22725
  const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
@@ -22702,7 +22809,7 @@ var readVcsConfig = () => {
22702
22809
  const file = vcsConfigPath();
22703
22810
  let raw;
22704
22811
  try {
22705
- raw = fs10.readFileSync(file, "utf8");
22812
+ raw = fs11.readFileSync(file, "utf8");
22706
22813
  } catch {
22707
22814
  return { status: "missing", path: file, config: {} };
22708
22815
  }
@@ -22787,7 +22894,7 @@ function vcsConfig(mode, cwd, options = {}) {
22787
22894
  const prov = cfg[provider] ?? {};
22788
22895
  const out = {
22789
22896
  ok: true,
22790
- configPath: path11.resolve(cfgPath),
22897
+ configPath: path13.resolve(cfgPath),
22791
22898
  provider,
22792
22899
  defaultTargetBranch: workspaceDefault,
22793
22900
  pr: cfg.pr ?? {},
@@ -22998,7 +23105,7 @@ function diffHash(cwd, base, head) {
22998
23105
  ], GIT_TIMEOUTS.worktree);
22999
23106
  if (!diff.ok || !diff.stdout)
23000
23107
  return null;
23001
- const hash = createHash8("sha256");
23108
+ const hash = createHash9("sha256");
23002
23109
  for (const line of diff.stdout.split(`
23003
23110
  `)) {
23004
23111
  if (line.startsWith("index "))
@@ -23010,16 +23117,16 @@ function diffHash(cwd, base, head) {
23010
23117
  return `sha256:${hash.digest("hex")}`;
23011
23118
  }
23012
23119
  function storeRoot(cwd) {
23013
- const location = resolveStore(path12.resolve(cwd));
23120
+ const location = resolveStore(path14.resolve(cwd));
23014
23121
  if (location instanceof Error)
23015
23122
  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");
23016
23123
  return { ok: true, value: { root: location.dir, shared: location.shared } };
23017
23124
  }
23018
23125
  function ledgerPath(cwd) {
23019
23126
  const root = storeRoot(cwd);
23020
- return root.ok ? { ok: true, value: path12.join(root.value.root, "ledger", "ledger.jsonl") } : root;
23127
+ return root.ok ? { ok: true, value: path14.join(root.value.root, "ledger", "ledger.jsonl") } : root;
23021
23128
  }
23022
- var newId2 = () => `${Date.now().toString(36)}-${randomBytes2(6).toString("hex")}`;
23129
+ var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
23023
23130
  var NETWORK_FS_TYPES = new Set([
23024
23131
  26985,
23025
23132
  20859,
@@ -23037,7 +23144,7 @@ var onNetworkFs = (dir) => {
23037
23144
  if (process.platform !== "linux")
23038
23145
  return false;
23039
23146
  try {
23040
- return isNetworkFsType(fs11.statfsSync(dir).type);
23147
+ return isNetworkFsType(fs12.statfsSync(dir).type);
23041
23148
  } catch {
23042
23149
  return false;
23043
23150
  }
@@ -23047,15 +23154,15 @@ var LOCK_STALE_MS = 30000;
23047
23154
  var LOCK_OWNER = "owner";
23048
23155
  var lockToken = (lock) => {
23049
23156
  try {
23050
- return fs11.readFileSync(path12.join(lock, LOCK_OWNER), "utf8");
23157
+ return fs12.readFileSync(path14.join(lock, LOCK_OWNER), "utf8");
23051
23158
  } catch {
23052
23159
  return null;
23053
23160
  }
23054
23161
  };
23055
23162
  var lockAgeMs = (lock) => {
23056
- for (const target of [path12.join(lock, LOCK_OWNER), lock]) {
23163
+ for (const target of [path14.join(lock, LOCK_OWNER), lock]) {
23057
23164
  try {
23058
- return Date.now() - fs11.statSync(target).mtimeMs;
23165
+ return Date.now() - fs12.statSync(target).mtimeMs;
23059
23166
  } catch {}
23060
23167
  }
23061
23168
  return null;
@@ -23063,55 +23170,55 @@ var lockAgeMs = (lock) => {
23063
23170
  var ledgerLock = {
23064
23171
  acquire(lock) {
23065
23172
  try {
23066
- fs11.mkdirSync(lock);
23173
+ fs12.mkdirSync(lock);
23067
23174
  } catch (error) {
23068
23175
  if (error.code === "EEXIST")
23069
23176
  return null;
23070
23177
  throw error;
23071
23178
  }
23072
- const token = `${process.pid}-${randomBytes2(8).toString("hex")}`;
23073
- fs11.writeFileSync(path12.join(lock, LOCK_OWNER), token);
23179
+ const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
23180
+ fs12.writeFileSync(path14.join(lock, LOCK_OWNER), token);
23074
23181
  return token;
23075
23182
  },
23076
23183
  release(lock, token) {
23077
23184
  if (lockToken(lock) !== token)
23078
23185
  return false;
23079
- const tomb = `${lock}.release-${randomBytes2(6).toString("hex")}`;
23186
+ const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
23080
23187
  try {
23081
- fs11.renameSync(lock, tomb);
23188
+ fs12.renameSync(lock, tomb);
23082
23189
  } catch {
23083
23190
  return false;
23084
23191
  }
23085
23192
  if (lockToken(tomb) !== token) {
23086
23193
  try {
23087
- fs11.renameSync(tomb, lock);
23194
+ fs12.renameSync(tomb, lock);
23088
23195
  } catch {}
23089
23196
  return false;
23090
23197
  }
23091
- fs11.rmSync(tomb, { recursive: true, force: true });
23198
+ fs12.rmSync(tomb, { recursive: true, force: true });
23092
23199
  return true;
23093
23200
  },
23094
23201
  token: lockToken,
23095
23202
  reap(lock, observed) {
23096
- const tomb = `${lock}.reap-${randomBytes2(6).toString("hex")}`;
23203
+ const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
23097
23204
  try {
23098
- fs11.renameSync(lock, tomb);
23205
+ fs12.renameSync(lock, tomb);
23099
23206
  } catch {
23100
23207
  return false;
23101
23208
  }
23102
23209
  if (lockToken(tomb) === observed) {
23103
- fs11.rmSync(tomb, { recursive: true, force: true });
23210
+ fs12.rmSync(tomb, { recursive: true, force: true });
23104
23211
  return true;
23105
23212
  }
23106
23213
  try {
23107
- fs11.renameSync(tomb, lock);
23214
+ fs12.renameSync(tomb, lock);
23108
23215
  } catch {
23109
- fs11.rmSync(tomb, { recursive: true, force: true });
23216
+ fs12.rmSync(tomb, { recursive: true, force: true });
23110
23217
  }
23111
23218
  return false;
23112
23219
  }
23113
23220
  };
23114
- function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
23221
+ function withLedgerLock(file, fn, waitMs = hostAwareLockTimeout(LOCK_WAIT_MS)) {
23115
23222
  const lock = `${file}.lock`;
23116
23223
  const deadline = Date.now() + waitMs;
23117
23224
  let token = null;
@@ -23133,14 +23240,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
23133
23240
  ledgerLock.release(lock, token);
23134
23241
  }
23135
23242
  }
23136
- var sha2562 = (text) => createHash8("sha256").update(text).digest("hex");
23243
+ var sha2562 = (text) => createHash9("sha256").update(text).digest("hex");
23137
23244
  var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
23138
23245
  ${body}`);
23139
23246
  var TAIL_BYTES = 64 * 1024;
23140
23247
  var lastLine = (fd, size) => {
23141
23248
  for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
23142
23249
  const buffer = Buffer.alloc(span);
23143
- fs11.readSync(fd, buffer, 0, span, size - span);
23250
+ fs12.readSync(fd, buffer, 0, span, size - span);
23144
23251
  const end = buffer.lastIndexOf(10);
23145
23252
  if (end < 0) {
23146
23253
  if (span === size)
@@ -23169,15 +23276,23 @@ var lastLine = (fd, size) => {
23169
23276
  }
23170
23277
  return null;
23171
23278
  };
23279
+ var tighten = (fd) => {
23280
+ try {
23281
+ if ((fs12.fstatSync(fd).mode & 63) !== 0)
23282
+ fs12.fchmodSync(fd, 384);
23283
+ } catch {}
23284
+ };
23172
23285
  function appendLine(file, build, options) {
23173
23286
  const write = () => {
23174
- const fd = fs11.openSync(file, "a+");
23287
+ const fd = fs12.openSync(file, "a+", 384);
23175
23288
  try {
23176
- const size = fs11.fstatSync(fd).size;
23289
+ if (process.platform !== "win32")
23290
+ tighten(fd);
23291
+ const size = fs12.fstatSync(fd).size;
23177
23292
  let fence = "";
23178
23293
  if (size > 0) {
23179
23294
  const last = Buffer.alloc(1);
23180
- fs11.readSync(fd, last, 0, 1, size - 1);
23295
+ fs12.readSync(fd, last, 0, 1, size - 1);
23181
23296
  if (last[0] !== 10)
23182
23297
  fence = `
23183
23298
  `;
@@ -23186,17 +23301,17 @@ function appendLine(file, build, options) {
23186
23301
  if (!line.ok)
23187
23302
  return line;
23188
23303
  const bytes = Buffer.from(fence + line.value);
23189
- const written = fs11.writeSync(fd, bytes);
23304
+ const written = fs12.writeSync(fd, bytes);
23190
23305
  if (written !== bytes.length)
23191
23306
  return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
23192
23307
  return { ok: true, value: undefined };
23193
23308
  } finally {
23194
- fs11.closeSync(fd);
23309
+ fs12.closeSync(fd);
23195
23310
  }
23196
23311
  };
23197
23312
  try {
23198
- fs11.mkdirSync(path12.dirname(file), { recursive: true });
23199
- if (!options.forceLock && !onNetworkFs(path12.dirname(file)))
23313
+ fs12.mkdirSync(path14.dirname(file), { recursive: true });
23314
+ if (!options.forceLock && !onNetworkFs(path14.dirname(file)))
23200
23315
  return write();
23201
23316
  const locked = withLedgerLock(file, write);
23202
23317
  return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
@@ -23264,8 +23379,10 @@ function readLedger(cwd, options = {}) {
23264
23379
  if (!file.ok)
23265
23380
  return file;
23266
23381
  let text = "";
23382
+ let stat = null;
23267
23383
  try {
23268
- text = fs11.readFileSync(file.value, "utf8");
23384
+ stat = fs12.statSync(file.value);
23385
+ text = fs12.readFileSync(file.value, "utf8");
23269
23386
  } catch (error) {
23270
23387
  if (error.code === "ENOENT")
23271
23388
  return {
@@ -23284,6 +23401,7 @@ function readLedger(cwd, options = {}) {
23284
23401
  const integrity = { chained: 0, legacy: 0, unverified: [] };
23285
23402
  const earlier = new Set([null]);
23286
23403
  let chainStarted = false;
23404
+ const ids = new Set;
23287
23405
  for (const raw of text.split(`
23288
23406
  `)) {
23289
23407
  const trimmed = raw.trim();
@@ -23317,6 +23435,10 @@ function readLedger(cwd, options = {}) {
23317
23435
  reason = "unsigned";
23318
23436
  else
23319
23437
  rowIntegrity = "legacy";
23438
+ if (options.integrity && !reason && ids.has(parsed.id))
23439
+ reason = "duplicate_id";
23440
+ if (options.integrity)
23441
+ ids.add(parsed.id);
23320
23442
  if (reason)
23321
23443
  rowIntegrity = "unverified";
23322
23444
  const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
@@ -23340,7 +23462,8 @@ function readLedger(cwd, options = {}) {
23340
23462
  seq: rows.length + 1,
23341
23463
  superseded: false,
23342
23464
  supersedeIgnored: false,
23343
- ...rowIntegrity ? { integrity: rowIntegrity } : {}
23465
+ ...rowIntegrity ? { integrity: rowIntegrity } : {},
23466
+ ...reason ? { integrityReason: reason } : {}
23344
23467
  };
23345
23468
  rows.push(row);
23346
23469
  if (!options.integrity)
@@ -23368,18 +23491,48 @@ function readLedger(cwd, options = {}) {
23368
23491
  const byId = new Map;
23369
23492
  for (const row of rows) {
23370
23493
  const target = row.supersedes ? byId.get(row.supersedes) : undefined;
23371
- if (target && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
23494
+ if (target && row.integrity !== "unverified" && supersedeAllowed(row, target, renameAwareBranch(scopeOf, row.branch)) === null)
23372
23495
  target.superseded = true;
23373
23496
  else if (row.supersedes)
23374
23497
  row.supersedeIgnored = true;
23375
23498
  if (!byId.has(row.id))
23376
23499
  byId.set(row.id, row);
23377
23500
  }
23501
+ if (options.integrity && stat && Buffer.byteLength(text) === stat.size)
23502
+ tamperCache.set(file.value, {
23503
+ size: stat.size,
23504
+ stamp: fileStamp(stat),
23505
+ breaks: integrity.unverified.filter((row) => TAMPER_REASONS.has(row.reason))
23506
+ });
23378
23507
  return {
23379
23508
  ok: true,
23380
23509
  value: { path: file.value, rows, skipped, integrity: options.integrity ? integrity : null }
23381
23510
  };
23382
23511
  }
23512
+ var TAMPER_REASONS = new Set(["hash_mismatch", "broken_link"]);
23513
+ var tamperCache = new Map;
23514
+ var fileStamp = (stat) => `${stat.ino}:${stat.mtimeMs}:${stat.ctimeMs}`;
23515
+ function ledgerTampering(cwd) {
23516
+ const file = ledgerPath(cwd);
23517
+ if (!file.ok)
23518
+ return [];
23519
+ try {
23520
+ const stat = fs12.statSync(file.value);
23521
+ const cached = tamperCache.get(file.value);
23522
+ if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
23523
+ return cached.breaks;
23524
+ } catch {
23525
+ return [];
23526
+ }
23527
+ const read = readLedger(cwd, { integrity: true });
23528
+ return read.ok ? (read.value.integrity?.unverified ?? []).filter((row) => TAMPER_REASONS.has(row.reason)) : [];
23529
+ }
23530
+ function tamperedUnblock(cwd) {
23531
+ const file = ledgerPath(cwd);
23532
+ const where = file.ok ? file.value : "the ledger file";
23533
+ 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`;
23534
+ }
23535
+ 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)}`;
23383
23536
  function verdictBasis(verdict, current) {
23384
23537
  if (verdict.head && verdict.head === current.head)
23385
23538
  return "fresh";
@@ -23387,7 +23540,7 @@ function verdictBasis(verdict, current) {
23387
23540
  return "carried";
23388
23541
  return "stale";
23389
23542
  }
23390
- var isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.superseded;
23543
+ var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
23391
23544
  function trailerSessions(cwd, base, head) {
23392
23545
  if (!base || !head || !safeRef(base) || !safeRef(head))
23393
23546
  return [];
@@ -23468,6 +23621,56 @@ var authoredBy = (authors, session) => {
23468
23621
  return true;
23469
23622
  return false;
23470
23623
  };
23624
+ var TRUST_REASONS = new Set([
23625
+ "unsigned",
23626
+ "hash_mismatch",
23627
+ "broken_link",
23628
+ "duplicate_id",
23629
+ "unissued_session",
23630
+ "unbound_session"
23631
+ ]);
23632
+ var SHELL_VOUCHED_HOSTS = new Set([
23633
+ "cli",
23634
+ "opencode",
23635
+ "pi",
23636
+ "codex_cli",
23637
+ "codex_desktop"
23638
+ ]);
23639
+ function sessionTrust(cwd, row) {
23640
+ const session = row.actor.session;
23641
+ if (!session || row.self === true)
23642
+ return null;
23643
+ const source = typeof row.sessionSource === "string" ? row.sessionSource : "env";
23644
+ const host = row.actor.host;
23645
+ const issuedOnly = source === "flag" ? host !== "cli" : source === "env" && session.includes(":") && !SHELL_VOUCHED_HOSTS.has(host);
23646
+ if (!issuedOnly)
23647
+ return null;
23648
+ const binding = issuedBinding(cwd, session);
23649
+ if (binding === null)
23650
+ return "unissued_session";
23651
+ return binding === "token" && row.sessionBound !== true ? "unbound_session" : null;
23652
+ }
23653
+ function trustReasons(cwd, row) {
23654
+ if (row.integrity === "unverified")
23655
+ return [row.integrityReason ?? "unsigned"];
23656
+ if (row.integrity !== "verified")
23657
+ return ["unsigned"];
23658
+ const session = sessionTrust(cwd, row);
23659
+ return session ? [session] : [];
23660
+ }
23661
+ function withIntegrity(cwd, rows) {
23662
+ if (rows.every((row) => row.integrity !== undefined))
23663
+ return rows;
23664
+ const read = readLedger(cwd, { integrity: true });
23665
+ const bySeq = new Map(read.ok ? read.value.rows.map((row) => [row.seq, row]) : []);
23666
+ return rows.map((row) => {
23667
+ if (row.integrity !== undefined)
23668
+ return row;
23669
+ const fresh = bySeq.get(row.seq);
23670
+ return fresh && fresh.id === row.id && fresh.at === row.at ? fresh : row;
23671
+ });
23672
+ }
23673
+ var untrusted = (reasons) => reasons.some((reason) => TRUST_REASONS.has(reason));
23471
23674
  var independenceReasons = (row, authors) => {
23472
23675
  const reasons = [];
23473
23676
  if (!row.actor.session)
@@ -23478,15 +23681,17 @@ var independenceReasons = (row, authors) => {
23478
23681
  reasons.push("self");
23479
23682
  return reasons;
23480
23683
  };
23481
- function effectiveOf(rows, authors, sameCode, isCurrent) {
23684
+ function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
23482
23685
  let effective = null;
23483
23686
  for (const row of rows) {
23484
23687
  if (!effective) {
23485
23688
  effective = row;
23486
23689
  continue;
23487
23690
  }
23488
- const rowIndependent = independenceReasons(row, authors).length === 0;
23489
- const effIndependent = independenceReasons(effective, authors).length === 0;
23691
+ if (untrusted(reasonsOf(row)) && !untrusted(reasonsOf(effective)))
23692
+ continue;
23693
+ const rowIndependent = reasonsOf(row).length === 0;
23694
+ const effIndependent = reasonsOf(effective).length === 0;
23490
23695
  if (effIndependent && !rowIndependent && isCurrent(effective))
23491
23696
  continue;
23492
23697
  if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session && sameCode(effective, row))
@@ -23500,7 +23705,7 @@ function restackCarries(rows, branch, from, to) {
23500
23705
  return false;
23501
23706
  const edges = new Map;
23502
23707
  for (const row of rows)
23503
- if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
23708
+ if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
23504
23709
  edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
23505
23710
  const seen = new Set([from]);
23506
23711
  const queue = [from];
@@ -23516,7 +23721,8 @@ function restackCarries(rows, branch, from, to) {
23516
23721
  }
23517
23722
  return false;
23518
23723
  }
23519
- function checkVerdicts(cwd, branch, rows) {
23724
+ function checkVerdicts(cwd, branch, ledgerRows) {
23725
+ const rows = withIntegrity(cwd, ledgerRows);
23520
23726
  const head = branchHead(cwd, branch);
23521
23727
  const fallbackBase = defaultBase(cwd);
23522
23728
  const scope = branchScope(cwd, branch);
@@ -23546,21 +23752,47 @@ function checkVerdicts(cwd, branch, rows) {
23546
23752
  return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
23547
23753
  };
23548
23754
  const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
23755
+ const failureAuthorCache = new Map;
23756
+ const failureAuthors = (row) => {
23757
+ let set = failureAuthorCache.get(row.seq);
23758
+ if (!set) {
23759
+ set = authorSessions(cwd, rows.filter((other) => other.integrity === "verified" && other.seq < row.seq), branch, { base: null, head: null }, scope);
23760
+ failureAuthorCache.set(row.seq, set);
23761
+ }
23762
+ return set;
23763
+ };
23764
+ const reasonCache = new Map;
23765
+ const reasonsOf = (row) => {
23766
+ let reasons = reasonCache.get(row);
23767
+ if (!reasons) {
23768
+ const failing = FAILING.has(String(row.result));
23769
+ reasons = [
23770
+ ...trustReasons(cwd, row),
23771
+ ...independenceReasons(row, failing ? failureAuthors(row) : authors)
23772
+ ];
23773
+ reasonCache.set(row, reasons);
23774
+ }
23775
+ return reasons;
23776
+ };
23777
+ const retired = new Set;
23778
+ for (const row of rows)
23779
+ if (row.type === "verdict" && row.supersedes && !row.supersedeIgnored && trustReasons(cwd, row).length === 0)
23780
+ retired.add(row.supersedes);
23549
23781
  const byKind = new Map;
23550
23782
  for (const row of rows)
23551
- if (isLiveVerdict(row) && scope(row)) {
23783
+ if (isLiveVerdict(row, retired) && scope(row)) {
23552
23784
  const kind = typeof row.kind === "string" ? row.kind : "review";
23553
23785
  byKind.set(kind, [...byKind.get(kind) ?? [], row]);
23554
23786
  }
23555
23787
  const verdicts = [];
23556
23788
  const warnings = [];
23557
23789
  for (const [kind, list] of byKind) {
23558
- const row = effectiveOf(list, authors, sameCode, (candidate) => basisOf(candidate) !== "stale");
23790
+ const row = effectiveOf(list, reasonsOf, sameCode, (candidate) => basisOf(candidate) !== "stale");
23559
23791
  if (!row)
23560
23792
  continue;
23561
23793
  const basis = basisOf(row);
23562
23794
  const current = basis !== "stale";
23563
- const independence = independenceReasons(row, authors);
23795
+ const independence = reasonsOf(row);
23564
23796
  const reasons = [
23565
23797
  ...current ? [] : ["stale"],
23566
23798
  ...PASSING.has(String(row.result)) ? [] : ["not_passing"],
@@ -23577,23 +23809,31 @@ function checkVerdicts(cwd, branch, rows) {
23577
23809
  });
23578
23810
  if (reasons.length === 0) {
23579
23811
  for (const later of list)
23580
- if (later.seq > row.seq && independenceReasons(later, authors).length > 0 && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
23812
+ if (later.seq > row.seq && reasonsOf(later).length > 0 && !untrusted(reasonsOf(later)) && FAILING.has(String(later.result)) && basisOf(later) !== "stale")
23581
23813
  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`);
23582
23814
  }
23583
23815
  }
23584
23816
  for (const row of rows)
23585
23817
  if (row.integrity === "unverified" && scope(row))
23586
23818
  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)`);
23819
+ const tampered = ledgerTampering(cwd);
23820
+ if (tampered.length) {
23821
+ warnings.push(tamperedWarning(cwd, tampered));
23822
+ for (const entry of verdicts) {
23823
+ entry.accepted = false;
23824
+ entry.reasons = [...entry.reasons, "ledger_tampered"];
23825
+ }
23826
+ }
23587
23827
  verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
23588
23828
  const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
23589
23829
  const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
23590
23830
  const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
23591
- const accepted = newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
23831
+ const accepted = tampered.length ? { accepted: false, verdict: null, reasons: ["ledger_tampered"] } : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
23592
23832
  accepted: false,
23593
23833
  verdict: failing?.verdict ?? null,
23594
23834
  reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
23595
23835
  };
23596
- const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
23836
+ const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
23597
23837
  return {
23598
23838
  branch,
23599
23839
  head,
@@ -23756,7 +23996,7 @@ var productDecision = (task, ledger) => {
23756
23996
  };
23757
23997
  };
23758
23998
  var planRecorded = (task, root) => {
23759
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path13.resolve(root, ref.path)));
23999
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path15.resolve(root, ref.path)));
23760
24000
  if (cited)
23761
24001
  return {
23762
24002
  met: true,
@@ -23804,15 +24044,15 @@ function derivedRuleStatus(task, requirement, root, ledger) {
23804
24044
  }
23805
24045
 
23806
24046
  // packages/workit-core/src/core/task-evaluation.ts
23807
- var digestBytes = (value) => createHash9("sha256").update(value).digest("hex");
23808
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path14.sep}`);
23809
- var relative = (root, candidate) => path14.relative(root, candidate).split(path14.sep).join("/") || ".";
24047
+ var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
24048
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
24049
+ var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
23810
24050
  var canonicalPath = (value) => {
23811
24051
  if (typeof value !== "string" || !value)
23812
24052
  return null;
23813
24053
  if (value.includes("\\") || value.split("/").includes(".."))
23814
24054
  return null;
23815
- const normalized = path14.posix.normalize(value);
24055
+ const normalized = path16.posix.normalize(value);
23816
24056
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
23817
24057
  return null;
23818
24058
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -23860,21 +24100,21 @@ var gitPaths = (root) => {
23860
24100
  git: !/not a git repository/i.test(stderr)
23861
24101
  };
23862
24102
  }
23863
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path14.sep).join("/")) : [];
24103
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
23864
24104
  const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
23865
24105
  if (stagedDeleted.status !== 0) {
23866
24106
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
23867
24107
  if (!/not a git repository/i.test(stderr))
23868
24108
  return { paths, uncertain: true, git: true };
23869
24109
  } else if (stagedDeleted.stdout) {
23870
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path14.sep).join("/")));
24110
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
23871
24111
  }
23872
24112
  return { paths: [...new Set(paths)], uncertain: false, git: true };
23873
24113
  };
23874
24114
  var walk = (root, directory, output) => {
23875
24115
  let names;
23876
24116
  try {
23877
- names = fs12.readdirSync(directory);
24117
+ names = fs13.readdirSync(directory);
23878
24118
  } catch {
23879
24119
  return true;
23880
24120
  }
@@ -23882,10 +24122,10 @@ var walk = (root, directory, output) => {
23882
24122
  for (const name of names) {
23883
24123
  if (name === ".git" || name === ".workit")
23884
24124
  continue;
23885
- const target = path14.join(directory, name);
24125
+ const target = path16.join(directory, name);
23886
24126
  let stat;
23887
24127
  try {
23888
- stat = fs12.lstatSync(target);
24128
+ stat = fs13.lstatSync(target);
23889
24129
  } catch {
23890
24130
  uncertain = true;
23891
24131
  continue;
@@ -23900,14 +24140,14 @@ var walk = (root, directory, output) => {
23900
24140
  var scopeRoots = (root, scope) => {
23901
24141
  const roots = [];
23902
24142
  for (const item of scope.paths.length ? scope.paths : ["."]) {
23903
- const target = path14.resolve(root, item);
24143
+ const target = path16.resolve(root, item);
23904
24144
  if (!inside(root, target))
23905
24145
  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` }] });
23906
24146
  let ancestor = target;
23907
- while (!fs12.existsSync(ancestor) && ancestor !== root)
23908
- ancestor = path14.dirname(ancestor);
24147
+ while (!fs13.existsSync(ancestor) && ancestor !== root)
24148
+ ancestor = path16.dirname(ancestor);
23909
24149
  try {
23910
- if (!inside(root, fs12.realpathSync(ancestor)))
24150
+ if (!inside(root, fs13.realpathSync(ancestor)))
23911
24151
  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` }] });
23912
24152
  } catch {
23913
24153
  return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
@@ -23919,7 +24159,7 @@ var scopeRoots = (root, scope) => {
23919
24159
  function captureCandidate(root, scope, environment = []) {
23920
24160
  let checkout;
23921
24161
  try {
23922
- checkout = fs12.realpathSync(root);
24162
+ checkout = fs13.realpathSync(root);
23923
24163
  } catch {
23924
24164
  return failure2("invalid_input", "candidate root does not exist");
23925
24165
  }
@@ -23937,11 +24177,11 @@ function captureCandidate(root, scope, environment = []) {
23937
24177
  if (item === ".git" || item === ".workit")
23938
24178
  continue;
23939
24179
  names.add(item);
23940
- if (!fs12.existsSync(target))
24180
+ if (!fs13.existsSync(target))
23941
24181
  continue;
23942
24182
  let stat;
23943
24183
  try {
23944
- stat = fs12.lstatSync(target);
24184
+ stat = fs13.lstatSync(target);
23945
24185
  } catch {
23946
24186
  uncertain = true;
23947
24187
  continue;
@@ -23957,17 +24197,17 @@ function captureCandidate(root, scope, environment = []) {
23957
24197
  for (const item of [...names].toSorted()) {
23958
24198
  if (!scopeMatches(item, normalizedScope))
23959
24199
  continue;
23960
- const target = path14.join(checkout, item);
24200
+ const target = path16.join(checkout, item);
23961
24201
  try {
23962
- const stat = fs12.lstatSync(target);
24202
+ const stat = fs13.lstatSync(target);
23963
24203
  if (stat.isSymbolicLink()) {
23964
- const link = fs12.readlinkSync(target);
24204
+ const link = fs13.readlinkSync(target);
23965
24205
  files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
23966
24206
  } else if (stat.isFile()) {
23967
24207
  files.push({
23968
24208
  path: item,
23969
24209
  kind: "file",
23970
- digest: digestBytes(fs12.readFileSync(target)),
24210
+ digest: digestBytes(fs13.readFileSync(target)),
23971
24211
  executable: (stat.mode & 73) !== 0
23972
24212
  });
23973
24213
  } else if (!stat.isDirectory()) {
@@ -24132,7 +24372,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
24132
24372
  var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
24133
24373
  let root;
24134
24374
  try {
24135
- root = fs12.realpathSync(checkoutRoot);
24375
+ root = fs13.realpathSync(checkoutRoot);
24136
24376
  } catch {
24137
24377
  return failure2("permission_denied", "checkout root is unavailable");
24138
24378
  }
@@ -24141,23 +24381,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
24141
24381
  continue;
24142
24382
  if (!reference.digest)
24143
24383
  return failure2("invalid_input", "document references require a byte digest");
24144
- const target = path14.resolve(root, reference.path);
24145
- if (target !== root && !target.startsWith(`${root}${path14.sep}`))
24384
+ const target = path16.resolve(root, reference.path);
24385
+ if (target !== root && !target.startsWith(`${root}${path16.sep}`))
24146
24386
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
24147
24387
  fields: [
24148
24388
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
24149
24389
  ]
24150
24390
  });
24151
24391
  try {
24152
- const below = path14.relative(root, target);
24392
+ const below = path16.relative(root, target);
24153
24393
  let current = root;
24154
- for (const segment of below.split(path14.sep)) {
24394
+ for (const segment of below.split(path16.sep)) {
24155
24395
  if (!segment)
24156
24396
  continue;
24157
- current = path14.join(current, segment);
24158
- const stat = fs12.lstatSync(current);
24159
- const real = fs12.realpathSync(current);
24160
- if (real !== root && !real.startsWith(`${root}${path14.sep}`))
24397
+ current = path16.join(current, segment);
24398
+ const stat = fs13.lstatSync(current);
24399
+ const real = fs13.realpathSync(current);
24400
+ if (real !== root && !real.startsWith(`${root}${path16.sep}`))
24161
24401
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
24162
24402
  fields: [
24163
24403
  {
@@ -24173,7 +24413,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
24173
24413
  if (current !== target && !stat.isDirectory())
24174
24414
  return failure2("invalid_input", "document reference ancestor is not a directory");
24175
24415
  }
24176
- if (digestBytes(fs12.readFileSync(target)) !== reference.digest)
24416
+ if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
24177
24417
  return failure2("permission_denied", "decision document bytes have changed");
24178
24418
  } catch {
24179
24419
  return failure2("invalid_input", "decision document is unavailable");
@@ -25031,7 +25271,7 @@ var judgeInputSummary = (raw) => {
25031
25271
 
25032
25272
  // packages/workit-core/src/autonomy.ts
25033
25273
  import os4 from "node:os";
25034
- import path15 from "node:path";
25274
+ import path17 from "node:path";
25035
25275
  var accountHome = () => {
25036
25276
  try {
25037
25277
  return os4.userInfo().homedir;
@@ -25040,10 +25280,10 @@ var accountHome = () => {
25040
25280
  }
25041
25281
  };
25042
25282
  var grantsHome = { resolve: accountHome };
25043
- var grantsDir = () => path15.join(grantsHome.resolve(), ".config", "workit");
25283
+ var grantsDir = () => path17.join(grantsHome.resolve(), ".config", "workit");
25044
25284
  var grantsOverride = () => {
25045
- const configured = path15.resolve(resolveConfigDir());
25046
- return configured === path15.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 ${path15.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
25285
+ const configured = path17.resolve(resolveConfigDir());
25286
+ return configured === path17.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path17.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
25047
25287
  };
25048
25288
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
25049
25289
  var DEFAULT_GRANTS = Object.freeze({
@@ -26815,7 +27055,7 @@ class WorkitCore {
26815
27055
  // packages/workit-core/src/core/repo-context.ts
26816
27056
  import { spawnSync as spawnSync11 } from "node:child_process";
26817
27057
  import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
26818
- import path16 from "node:path";
27058
+ import path18 from "node:path";
26819
27059
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
26820
27060
  try {
26821
27061
  gitRevisionParts(value);
@@ -26949,7 +27189,7 @@ var findPrTemplate = (cwd) => {
26949
27189
  ]) {
26950
27190
  const parts = rel.split("/");
26951
27191
  const base = parts[parts.length - 1];
26952
- const dir = parts.length > 1 ? path16.join(cwd, ...parts.slice(0, -1)) : cwd;
27192
+ const dir = parts.length > 1 ? path18.join(cwd, ...parts.slice(0, -1)) : cwd;
26953
27193
  const actual = probeCaseInsensitive(dir, base);
26954
27194
  if (actual)
26955
27195
  return [...parts.slice(0, -1), actual].join("/");
@@ -26980,7 +27220,7 @@ var diffStatForRange = (cwd, range) => {
26980
27220
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
26981
27221
  };
26982
27222
  var packageScripts = (cwd, keys) => {
26983
- const pkgPath = path16.join(cwd, "package.json");
27223
+ const pkgPath = path18.join(cwd, "package.json");
26984
27224
  if (!existsSync6(pkgPath))
26985
27225
  return [];
26986
27226
  try {
@@ -27006,9 +27246,9 @@ var documentationFiles = (cwd) => {
27006
27246
  if (entry.isDirectory()) {
27007
27247
  if (excluded.has(entry.name))
27008
27248
  continue;
27009
- walk(path16.join(dir, entry.name), depth + 1);
27249
+ walk(path18.join(dir, entry.name), depth + 1);
27010
27250
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
27011
- matches.push("./" + path16.relative(cwd, path16.join(dir, entry.name)).replaceAll("\\", "/"));
27251
+ matches.push("./" + path18.relative(cwd, path18.join(dir, entry.name)).replaceAll("\\", "/"));
27012
27252
  }
27013
27253
  }
27014
27254
  };
@@ -27024,7 +27264,7 @@ var readTrimmed2 = (file, maxLines) => {
27024
27264
  `);
27025
27265
  };
27026
27266
  function prReadyContext(root, range) {
27027
- const cwd = path16.resolve(repoRoot(root));
27267
+ const cwd = path18.resolve(repoRoot(root));
27028
27268
  if (range !== undefined && !isResolvableContextRange(cwd, range))
27029
27269
  return invalidRange(cwd);
27030
27270
  let stdout = "";
@@ -27082,7 +27322,7 @@ ${status.stderr}` : "");
27082
27322
  stdout += `template_path: ${template}
27083
27323
 
27084
27324
  `;
27085
- stdout += readTrimmed2(path16.join(cwd, template), 220);
27325
+ stdout += readTrimmed2(path18.join(cwd, template), 220);
27086
27326
  } else {
27087
27327
  stdout += `template_path: none
27088
27328
 
@@ -27092,7 +27332,7 @@ ${status.stderr}` : "");
27092
27332
  stdout += `
27093
27333
  `;
27094
27334
  stdout += printSection("Recent Validation Signals");
27095
- if (existsSync6(path16.join(cwd, "package.json"))) {
27335
+ if (existsSync6(path18.join(cwd, "package.json"))) {
27096
27336
  stdout += `package.json detected. Common scripts:
27097
27337
  `;
27098
27338
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -27101,7 +27341,7 @@ ${status.stderr}` : "");
27101
27341
  `) + `
27102
27342
  `;
27103
27343
  }
27104
- if (existsSync6(path16.join(cwd, "Cargo.toml")) || existsSync6(path16.join(cwd, "src-tauri/Cargo.toml"))) {
27344
+ if (existsSync6(path18.join(cwd, "Cargo.toml")) || existsSync6(path18.join(cwd, "src-tauri/Cargo.toml"))) {
27105
27345
  stdout += `Rust project detected.
27106
27346
  `;
27107
27347
  }
@@ -27134,7 +27374,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
27134
27374
  - Apply with the host-native editor, preserving the repository changelog conventions.
27135
27375
  - If Unreleased already has duplicate category headings, normalize_only first.`;
27136
27376
  function changelogContext(root, range) {
27137
- const cwd = path16.resolve(repoRoot(root));
27377
+ const cwd = path18.resolve(repoRoot(root));
27138
27378
  if (range !== undefined && !isResolvableContextRange(cwd, range))
27139
27379
  return invalidRange(cwd);
27140
27380
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -27152,7 +27392,7 @@ function changelogContext(root, range) {
27152
27392
  stdout += CHANGELOG_RULES + `
27153
27393
  `;
27154
27394
  stdout += printSection("Existing CHANGELOG.md");
27155
- const changelogPath = path16.join(cwd, "CHANGELOG.md");
27395
+ const changelogPath = path18.join(cwd, "CHANGELOG.md");
27156
27396
  if (existsSync6(changelogPath)) {
27157
27397
  stdout += readTrimmed2(changelogPath, 260) + `
27158
27398
  `;
@@ -27172,7 +27412,7 @@ function changelogContext(root, range) {
27172
27412
  return { stdout, stderr: "", exitCode: 0, cwd };
27173
27413
  }
27174
27414
  function docsRefreshContext(root, range) {
27175
- const cwd = path16.resolve(repoRoot(root));
27415
+ const cwd = path18.resolve(repoRoot(root));
27176
27416
  if (range !== undefined && !isResolvableContextRange(cwd, range))
27177
27417
  return invalidRange(cwd);
27178
27418
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -27194,7 +27434,7 @@ function docsRefreshContext(root, range) {
27194
27434
  `) + `
27195
27435
  `;
27196
27436
  stdout += printSection("README Preview");
27197
- const readme = path16.join(cwd, "README.md");
27437
+ const readme = path18.join(cwd, "README.md");
27198
27438
  if (existsSync6(readme)) {
27199
27439
  stdout += readTrimmed2(readme, 220) + `
27200
27440
  `;
@@ -27203,7 +27443,7 @@ function docsRefreshContext(root, range) {
27203
27443
  `;
27204
27444
  }
27205
27445
  stdout += printSection("Package Scripts");
27206
- const pkgPath = path16.join(cwd, "package.json");
27446
+ const pkgPath = path18.join(cwd, "package.json");
27207
27447
  if (existsSync6(pkgPath)) {
27208
27448
  try {
27209
27449
  const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
@@ -27217,7 +27457,7 @@ function docsRefreshContext(root, range) {
27217
27457
  return { stdout, stderr: "", exitCode: 0, cwd };
27218
27458
  }
27219
27459
  function releaseNotesContext(root, rangeOrTag) {
27220
- const cwd = path16.resolve(repoRoot(root));
27460
+ const cwd = path18.resolve(repoRoot(root));
27221
27461
  if (!rangeOrTag) {
27222
27462
  return { stdout: "", stderr: `ERROR: release tag or range required
27223
27463
  `, exitCode: 1, cwd };
@@ -27255,7 +27495,7 @@ function releaseNotesContext(root, rangeOrTag) {
27255
27495
  `;
27256
27496
  stdout += printSection("Existing Release Files");
27257
27497
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
27258
- if (existsSync6(path16.join(cwd, rel)))
27498
+ if (existsSync6(path18.join(cwd, rel)))
27259
27499
  stdout += rel + `
27260
27500
  `;
27261
27501
  }
@@ -27286,8 +27526,8 @@ function releaseNotesContext(root, rangeOrTag) {
27286
27526
  import { spawnSync as spawnSync13 } from "node:child_process";
27287
27527
 
27288
27528
  // packages/workit-core/src/core/pr-create.ts
27289
- import fs13 from "node:fs";
27290
- import path17 from "node:path";
27529
+ import fs14 from "node:fs";
27530
+ import path19 from "node:path";
27291
27531
  import { spawnSync as spawnSync12 } from "node:child_process";
27292
27532
  // packages/workit-core/src/forge/pr-body.ts
27293
27533
  function parseGhIssue(value) {
@@ -27298,13 +27538,13 @@ function parseGhIssue(value) {
27298
27538
  // packages/workit-core/src/core/pr-create.ts
27299
27539
  function whichOnPath(tool) {
27300
27540
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
27301
- for (const dir of (process.env.PATH ?? "").split(path17.delimiter)) {
27541
+ for (const dir of (process.env.PATH ?? "").split(path19.delimiter)) {
27302
27542
  if (!dir)
27303
27543
  continue;
27304
27544
  for (const name of names) {
27305
- const candidate = path17.join(dir, name);
27545
+ const candidate = path19.join(dir, name);
27306
27546
  try {
27307
- fs13.accessSync(candidate, fs13.constants.X_OK);
27547
+ fs14.accessSync(candidate, fs14.constants.X_OK);
27308
27548
  return candidate;
27309
27549
  } catch {}
27310
27550
  }
@@ -27422,8 +27662,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
27422
27662
  };
27423
27663
 
27424
27664
  // packages/workit-core/src/core/youtrack.ts
27425
- import fs14 from "node:fs";
27426
- import path18 from "node:path";
27665
+ import fs15 from "node:fs";
27666
+ import path20 from "node:path";
27427
27667
 
27428
27668
  // packages/workit-core/src/core/templates.ts
27429
27669
  var repoRoot2 = assetRoot();
@@ -27436,22 +27676,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
27436
27676
  // packages/workit-core/src/core/youtrack.ts
27437
27677
  var ISSUE_RE = /^[A-Z]+-\d+$/;
27438
27678
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
27439
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(configDir(), "youtrack.json");
27440
- var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(resolveConfigDir(), "youtrack.json");
27679
+ var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(configDir(), "youtrack.json");
27680
+ var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path20.join(resolveConfigDir(), "youtrack.json");
27441
27681
  var youTrackTokenModeOk = (p) => {
27442
27682
  if (process.platform === "win32")
27443
27683
  return true;
27444
- const mode = fs14.statSync(p).mode & 511;
27684
+ const mode = fs15.statSync(p).mode & 511;
27445
27685
  return mode === 384;
27446
27686
  };
27447
27687
  function readYouTrackConfig(required) {
27448
27688
  const cfgPath = youTrackConfigPath();
27449
- if (!fs14.existsSync(cfgPath)) {
27689
+ if (!fs15.existsSync(cfgPath)) {
27450
27690
  return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
27451
27691
  }
27452
27692
  let parsed;
27453
27693
  try {
27454
- parsed = JSON.parse(fs14.readFileSync(cfgPath, "utf8"));
27694
+ parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
27455
27695
  } catch {
27456
27696
  return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
27457
27697
  }
@@ -27462,11 +27702,11 @@ function readYouTrackConfig(required) {
27462
27702
  }
27463
27703
  var readYouTrackContextConfig = () => {
27464
27704
  const cfgPath = youTrackReadOnlyConfigPath();
27465
- if (!fs14.existsSync(cfgPath))
27705
+ if (!fs15.existsSync(cfgPath))
27466
27706
  return { data: {} };
27467
27707
  let parsed;
27468
27708
  try {
27469
- parsed = JSON.parse(fs14.readFileSync(cfgPath, "utf8"));
27709
+ parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
27470
27710
  } catch {
27471
27711
  return { error: "YouTrack configuration is unavailable" };
27472
27712
  }
@@ -27505,12 +27745,12 @@ var youTrackToken = () => {
27505
27745
  if ("error" in loaded)
27506
27746
  return loaded;
27507
27747
  const tokenFile = String(loaded.config.tokenFile ?? "");
27508
- const tokenPath = tokenFile ? path18.isAbsolute(tokenFile) ? path18.resolve(tokenFile) : path18.resolve(process.cwd(), tokenFile) : "";
27509
- if (!tokenPath || !fs14.existsSync(tokenPath))
27748
+ const tokenPath = tokenFile ? path20.isAbsolute(tokenFile) ? path20.resolve(tokenFile) : path20.resolve(process.cwd(), tokenFile) : "";
27749
+ if (!tokenPath || !fs15.existsSync(tokenPath))
27510
27750
  return { error: "missing youtrack.token" };
27511
27751
  if (!youTrackTokenModeOk(tokenPath))
27512
27752
  return { error: "youtrack.token mode must be 0600" };
27513
- const token = fs14.readFileSync(tokenPath, "utf8").trim();
27753
+ const token = fs15.readFileSync(tokenPath, "utf8").trim();
27514
27754
  if (!token)
27515
27755
  return { error: "empty token file" };
27516
27756
  if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
@@ -27575,9 +27815,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
27575
27815
  } catch {
27576
27816
  continue;
27577
27817
  }
27578
- if (!fs14.existsSync(full))
27818
+ if (!fs15.existsSync(full))
27579
27819
  continue;
27580
- const text = fs14.readFileSync(full, "utf8");
27820
+ const text = fs15.readFileSync(full, "utf8");
27581
27821
  const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
27582
27822
  if (m)
27583
27823
  return m[1];
@@ -27847,15 +28087,15 @@ function resolveGitRevision(root, value) {
27847
28087
  }
27848
28088
  function resolveInside(root, candidate) {
27849
28089
  const base = realpathSync5(root);
27850
- const target = path19.resolve(base, candidate);
27851
- if (target !== base && !target.startsWith(base + path19.sep)) {
28090
+ const target = path21.resolve(base, candidate);
28091
+ if (target !== base && !target.startsWith(base + path21.sep)) {
27852
28092
  throw new Error("path must stay inside repository root");
27853
28093
  }
27854
28094
  let ancestor = target;
27855
28095
  while (!existsSync7(ancestor))
27856
- ancestor = path19.dirname(ancestor);
28096
+ ancestor = path21.dirname(ancestor);
27857
28097
  const canonicalAncestor = realpathSync5(ancestor);
27858
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path19.sep)) {
28098
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path21.sep)) {
27859
28099
  throw new Error("path must stay inside repository root");
27860
28100
  }
27861
28101
  return target;
@@ -27913,13 +28153,13 @@ var rootVariants = (workspaceRoot) => {
27913
28153
  if (!workspaceRoot)
27914
28154
  return [];
27915
28155
  const roots = new Set;
27916
- for (const candidate of [workspaceRoot, path20.resolve(workspaceRoot)]) {
27917
- if (path20.isAbsolute(candidate) && candidate !== path20.parse(candidate).root)
28156
+ for (const candidate of [workspaceRoot, path22.resolve(workspaceRoot)]) {
28157
+ if (path22.isAbsolute(candidate) && candidate !== path22.parse(candidate).root)
27918
28158
  roots.add(candidate);
27919
28159
  }
27920
28160
  try {
27921
28161
  const canonical = realpathSync6(workspaceRoot);
27922
- if (canonical !== path20.parse(canonical).root)
28162
+ if (canonical !== path22.parse(canonical).root)
27923
28163
  roots.add(canonical);
27924
28164
  } catch {}
27925
28165
  return [...roots].toSorted((left, right) => right.length - left.length);
@@ -28001,11 +28241,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
28001
28241
  };
28002
28242
  function createMcpServer(host, contextProvider) {
28003
28243
  assertMcpHost(host);
28004
- const here = path20.dirname(fileURLToPath2(import.meta.url));
28244
+ const here = path22.dirname(fileURLToPath2(import.meta.url));
28005
28245
  const sourceMarker = markSourcesLoaded([
28006
- path20.join(here, "server.ts"),
28007
- path20.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
28008
- path20.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
28246
+ path22.join(here, "server.ts"),
28247
+ path22.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
28248
+ path22.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
28009
28249
  ].filter((file) => existsSync8(file)));
28010
28250
  let staleWarned = false;
28011
28251
  const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
@@ -28185,8 +28425,8 @@ var main = async () => {
28185
28425
  process.exitCode = 2;
28186
28426
  }
28187
28427
  };
28188
- var entryPath = process.argv[1] ? path21.resolve(process.argv[1]) : "";
28189
- if (entryPath === path21.resolve(fileURLToPath3(import.meta.url)))
28428
+ var entryPath = process.argv[1] ? path23.resolve(process.argv[1]) : "";
28429
+ if (entryPath === path23.resolve(fileURLToPath3(import.meta.url)))
28190
28430
  await main();
28191
28431
  export {
28192
28432
  McpCapabilityUnavailableError,