@brainervirus/workit-cursor 8.9.1 → 8.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8377,7 +8377,7 @@ import { realpathSync as realpathSync4 } from "node:fs";
8377
8377
 
8378
8378
  // packages/workit-core/src/core/policy/derive.ts
8379
8379
  import { existsSync as existsSync2 } from "node:fs";
8380
- import path7 from "node:path";
8380
+ import path8 from "node:path";
8381
8381
 
8382
8382
  // packages/workit-core/src/git/rev.ts
8383
8383
  import { spawnSync } from "node:child_process";
@@ -8562,9 +8562,9 @@ function pushRemoteName(cwd, branch) {
8562
8562
 
8563
8563
  // packages/workit-core/src/ledger.ts
8564
8564
  import { spawnSync as spawnSync7 } from "node:child_process";
8565
- import { createHash as createHash6, randomBytes } from "node:crypto";
8566
- import fs4 from "node:fs";
8567
- import path6 from "node:path";
8565
+ import { createHash as createHash7, randomBytes } from "node:crypto";
8566
+ import fs5 from "node:fs";
8567
+ import path7 from "node:path";
8568
8568
 
8569
8569
  // packages/workit-core/src/core/vcs-config.ts
8570
8570
  import fs2 from "node:fs";
@@ -8792,6 +8792,7 @@ var branchNames = (workspaceRoot) => {
8792
8792
  return new Set((r.stdout ?? "").split(`
8793
8793
  `).filter(Boolean).map((ref) => ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : ref.slice("refs/remotes/origin/".length)));
8794
8794
  };
8795
+ var productionBranchOf = (names) => names.has("main") ? "main" : names.has("master") ? "master" : "main";
8795
8796
  var detectBranchPolicy = (workspaceRoot) => {
8796
8797
  const names = branchNames(workspaceRoot);
8797
8798
  const develop = names.has("develop");
@@ -9799,33 +9800,9 @@ function vcsConfig(mode, cwd, options = {}) {
9799
9800
  }
9800
9801
 
9801
9802
  // packages/workit-core/src/host-session.ts
9802
- var HOST_SESSION_VARS = [
9803
- ["OPENCODE_SESSION_ID", () => "opencode"],
9804
- ["PI_SESSION_ID", () => "pi"],
9805
- [
9806
- "CODEX_THREAD_ID",
9807
- (env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
9808
- ]
9809
- ];
9810
- var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
9811
- function hostSessionFromEnv(env) {
9812
- const value = (key) => env[key]?.trim() || null;
9813
- let inner = null;
9814
- for (const [key, host] of HOST_SESSION_VARS) {
9815
- const session = value(key);
9816
- if (session) {
9817
- inner = { host: host(env), session };
9818
- break;
9819
- }
9820
- }
9821
- const declared = value("WORKIT_HOST");
9822
- if (env.WORKIT_SESSION_ID !== undefined) {
9823
- const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
9824
- if (!inherited)
9825
- return { host: declared, session: value("WORKIT_SESSION_ID") };
9826
- }
9827
- return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
9828
- }
9803
+ import { createHash as createHash6 } from "node:crypto";
9804
+ import fs4 from "node:fs";
9805
+ import path6 from "node:path";
9829
9806
 
9830
9807
  // packages/workit-core/src/store/paths.ts
9831
9808
  import { spawnSync as spawnSync6 } from "node:child_process";
@@ -10107,6 +10084,63 @@ var checkoutSlug = (root) => {
10107
10084
  return `${base}-${shortHash(canonical)}`;
10108
10085
  };
10109
10086
 
10087
+ // packages/workit-core/src/host-session.ts
10088
+ var HOST_SESSION_VARS = [
10089
+ ["OPENCODE_SESSION_ID", () => "opencode"],
10090
+ ["PI_SESSION_ID", () => "pi"],
10091
+ [
10092
+ "CODEX_THREAD_ID",
10093
+ (env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
10094
+ ]
10095
+ ];
10096
+ var family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
10097
+ function hostSessionFromEnv(env) {
10098
+ const value = (key) => env[key]?.trim() || null;
10099
+ let inner = null;
10100
+ for (const [key, host] of HOST_SESSION_VARS) {
10101
+ const session = value(key);
10102
+ if (session) {
10103
+ inner = { host: host(env), session };
10104
+ break;
10105
+ }
10106
+ }
10107
+ const declared = value("WORKIT_HOST");
10108
+ if (env.WORKIT_SESSION_ID !== undefined) {
10109
+ const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
10110
+ if (!inherited)
10111
+ return { host: declared, session: value("WORKIT_SESSION_ID") };
10112
+ }
10113
+ return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
10114
+ }
10115
+ var ISSUED_PER_LEAD = 64;
10116
+ var ISSUED_SESSIONS_MARKER = "workit-issued-sessions/1";
10117
+ var hash = (value) => createHash6("sha256").update(value).digest("hex").slice(0, 32);
10118
+ var issuedRoot = (cwd) => {
10119
+ const location = resolveStore(cwd);
10120
+ return location instanceof Error ? null : path6.join(location.dir, "hooks", "issued");
10121
+ };
10122
+ var issuedDir = (cwd, lead) => {
10123
+ const root = issuedRoot(cwd);
10124
+ return root === null ? null : path6.join(root, hash(lead));
10125
+ };
10126
+ function recordIssuedSession(cwd, lead, issued) {
10127
+ try {
10128
+ const dir = issuedDir(cwd, lead);
10129
+ if (!dir)
10130
+ return;
10131
+ fs4.mkdirSync(dir, { recursive: true });
10132
+ const own = hash(issued);
10133
+ fs4.writeFileSync(path6.join(dir, own), ISSUED_SESSIONS_MARKER);
10134
+ const entries = fs4.readdirSync(dir);
10135
+ if (entries.length <= ISSUED_PER_LEAD)
10136
+ return;
10137
+ const byAge = entries.filter((name) => name !== own).map((name) => ({ name, at: fs4.statSync(path6.join(dir, name)).mtimeMs })).toSorted((a, b) => a.at - b.at);
10138
+ for (const { name } of byAge.slice(0, entries.length - ISSUED_PER_LEAD))
10139
+ fs4.rmSync(path6.join(dir, name), { force: true });
10140
+ } catch {}
10141
+ }
10142
+ var OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
10143
+
10110
10144
  // packages/workit-core/src/ledger.ts
10111
10145
  var LEDGER_VERSION = 1;
10112
10146
  var MAX_LINE_BYTES = 4096;
@@ -10212,7 +10246,7 @@ function diffHash(cwd, base, head) {
10212
10246
  ], GIT_TIMEOUTS.worktree);
10213
10247
  if (!diff.ok || !diff.stdout)
10214
10248
  return null;
10215
- const hash = createHash6("sha256");
10249
+ const hash = createHash7("sha256");
10216
10250
  for (const line of diff.stdout.split(`
10217
10251
  `)) {
10218
10252
  if (line.startsWith("index "))
@@ -10224,14 +10258,14 @@ function diffHash(cwd, base, head) {
10224
10258
  return `sha256:${hash.digest("hex")}`;
10225
10259
  }
10226
10260
  function storeRoot(cwd) {
10227
- const location = resolveStore(path6.resolve(cwd));
10261
+ const location = resolveStore(path7.resolve(cwd));
10228
10262
  if (location instanceof Error)
10229
10263
  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");
10230
10264
  return { ok: true, value: { root: location.dir, shared: location.shared } };
10231
10265
  }
10232
10266
  function ledgerPath(cwd) {
10233
10267
  const root = storeRoot(cwd);
10234
- return root.ok ? { ok: true, value: path6.join(root.value.root, "ledger", "ledger.jsonl") } : root;
10268
+ return root.ok ? { ok: true, value: path7.join(root.value.root, "ledger", "ledger.jsonl") } : root;
10235
10269
  }
10236
10270
  var newId2 = () => `${Date.now().toString(36)}-${randomBytes(6).toString("hex")}`;
10237
10271
  var NETWORK_FS_TYPES = new Set([
@@ -10251,7 +10285,7 @@ var onNetworkFs = (dir) => {
10251
10285
  if (process.platform !== "linux")
10252
10286
  return false;
10253
10287
  try {
10254
- return isNetworkFsType(fs4.statfsSync(dir).type);
10288
+ return isNetworkFsType(fs5.statfsSync(dir).type);
10255
10289
  } catch {
10256
10290
  return false;
10257
10291
  }
@@ -10261,15 +10295,15 @@ var LOCK_STALE_MS = 30000;
10261
10295
  var LOCK_OWNER = "owner";
10262
10296
  var lockToken = (lock) => {
10263
10297
  try {
10264
- return fs4.readFileSync(path6.join(lock, LOCK_OWNER), "utf8");
10298
+ return fs5.readFileSync(path7.join(lock, LOCK_OWNER), "utf8");
10265
10299
  } catch {
10266
10300
  return null;
10267
10301
  }
10268
10302
  };
10269
10303
  var lockAgeMs = (lock) => {
10270
- for (const target of [path6.join(lock, LOCK_OWNER), lock]) {
10304
+ for (const target of [path7.join(lock, LOCK_OWNER), lock]) {
10271
10305
  try {
10272
- return Date.now() - fs4.statSync(target).mtimeMs;
10306
+ return Date.now() - fs5.statSync(target).mtimeMs;
10273
10307
  } catch {}
10274
10308
  }
10275
10309
  return null;
@@ -10277,14 +10311,14 @@ var lockAgeMs = (lock) => {
10277
10311
  var ledgerLock = {
10278
10312
  acquire(lock) {
10279
10313
  try {
10280
- fs4.mkdirSync(lock);
10314
+ fs5.mkdirSync(lock);
10281
10315
  } catch (error) {
10282
10316
  if (error.code === "EEXIST")
10283
10317
  return null;
10284
10318
  throw error;
10285
10319
  }
10286
10320
  const token = `${process.pid}-${randomBytes(8).toString("hex")}`;
10287
- fs4.writeFileSync(path6.join(lock, LOCK_OWNER), token);
10321
+ fs5.writeFileSync(path7.join(lock, LOCK_OWNER), token);
10288
10322
  return token;
10289
10323
  },
10290
10324
  release(lock, token) {
@@ -10292,35 +10326,35 @@ var ledgerLock = {
10292
10326
  return false;
10293
10327
  const tomb = `${lock}.release-${randomBytes(6).toString("hex")}`;
10294
10328
  try {
10295
- fs4.renameSync(lock, tomb);
10329
+ fs5.renameSync(lock, tomb);
10296
10330
  } catch {
10297
10331
  return false;
10298
10332
  }
10299
10333
  if (lockToken(tomb) !== token) {
10300
10334
  try {
10301
- fs4.renameSync(tomb, lock);
10335
+ fs5.renameSync(tomb, lock);
10302
10336
  } catch {}
10303
10337
  return false;
10304
10338
  }
10305
- fs4.rmSync(tomb, { recursive: true, force: true });
10339
+ fs5.rmSync(tomb, { recursive: true, force: true });
10306
10340
  return true;
10307
10341
  },
10308
10342
  token: lockToken,
10309
10343
  reap(lock, observed) {
10310
10344
  const tomb = `${lock}.reap-${randomBytes(6).toString("hex")}`;
10311
10345
  try {
10312
- fs4.renameSync(lock, tomb);
10346
+ fs5.renameSync(lock, tomb);
10313
10347
  } catch {
10314
10348
  return false;
10315
10349
  }
10316
10350
  if (lockToken(tomb) === observed) {
10317
- fs4.rmSync(tomb, { recursive: true, force: true });
10351
+ fs5.rmSync(tomb, { recursive: true, force: true });
10318
10352
  return true;
10319
10353
  }
10320
10354
  try {
10321
- fs4.renameSync(tomb, lock);
10355
+ fs5.renameSync(tomb, lock);
10322
10356
  } catch {
10323
- fs4.rmSync(tomb, { recursive: true, force: true });
10357
+ fs5.rmSync(tomb, { recursive: true, force: true });
10324
10358
  }
10325
10359
  return false;
10326
10360
  }
@@ -10347,14 +10381,14 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
10347
10381
  ledgerLock.release(lock, token);
10348
10382
  }
10349
10383
  }
10350
- var sha2562 = (text) => createHash6("sha256").update(text).digest("hex");
10384
+ var sha2562 = (text) => createHash7("sha256").update(text).digest("hex");
10351
10385
  var chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
10352
10386
  ${body}`);
10353
10387
  var TAIL_BYTES = 64 * 1024;
10354
10388
  var lastLine = (fd, size) => {
10355
10389
  for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
10356
10390
  const buffer = Buffer.alloc(span);
10357
- fs4.readSync(fd, buffer, 0, span, size - span);
10391
+ fs5.readSync(fd, buffer, 0, span, size - span);
10358
10392
  const end = buffer.lastIndexOf(10);
10359
10393
  if (end < 0) {
10360
10394
  if (span === size)
@@ -10385,13 +10419,13 @@ var lastLine = (fd, size) => {
10385
10419
  };
10386
10420
  function appendLine(file, build, options) {
10387
10421
  const write = () => {
10388
- const fd = fs4.openSync(file, "a+");
10422
+ const fd = fs5.openSync(file, "a+");
10389
10423
  try {
10390
- const size = fs4.fstatSync(fd).size;
10424
+ const size = fs5.fstatSync(fd).size;
10391
10425
  let fence = "";
10392
10426
  if (size > 0) {
10393
10427
  const last = Buffer.alloc(1);
10394
- fs4.readSync(fd, last, 0, 1, size - 1);
10428
+ fs5.readSync(fd, last, 0, 1, size - 1);
10395
10429
  if (last[0] !== 10)
10396
10430
  fence = `
10397
10431
  `;
@@ -10400,17 +10434,17 @@ function appendLine(file, build, options) {
10400
10434
  if (!line.ok)
10401
10435
  return line;
10402
10436
  const bytes = Buffer.from(fence + line.value);
10403
- const written = fs4.writeSync(fd, bytes);
10437
+ const written = fs5.writeSync(fd, bytes);
10404
10438
  if (written !== bytes.length)
10405
10439
  return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
10406
10440
  return { ok: true, value: undefined };
10407
10441
  } finally {
10408
- fs4.closeSync(fd);
10442
+ fs5.closeSync(fd);
10409
10443
  }
10410
10444
  };
10411
10445
  try {
10412
- fs4.mkdirSync(path6.dirname(file), { recursive: true });
10413
- if (!options.forceLock && !onNetworkFs(path6.dirname(file)))
10446
+ fs5.mkdirSync(path7.dirname(file), { recursive: true });
10447
+ if (!options.forceLock && !onNetworkFs(path7.dirname(file)))
10414
10448
  return write();
10415
10449
  const locked = withLedgerLock(file, write);
10416
10450
  return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
@@ -10482,7 +10516,7 @@ function readLedger(cwd, options = {}) {
10482
10516
  return file;
10483
10517
  let text = "";
10484
10518
  try {
10485
- text = fs4.readFileSync(file.value, "utf8");
10519
+ text = fs5.readFileSync(file.value, "utf8");
10486
10520
  } catch (error) {
10487
10521
  if (error.code === "ENOENT")
10488
10522
  return {
@@ -10676,11 +10710,20 @@ function authorSessions(cwd, rows, branch, range = { base: null, head: null }, s
10676
10710
  sessions.add(session);
10677
10711
  return sessions;
10678
10712
  }
10713
+ var derivesFrom = (derived, bare) => !bare.includes(":") && derived.endsWith(`:${bare}`);
10714
+ var authoredBy = (authors, session) => {
10715
+ if (authors.has(session))
10716
+ return true;
10717
+ for (const author of authors)
10718
+ if (derivesFrom(author, session) || derivesFrom(session, author))
10719
+ return true;
10720
+ return false;
10721
+ };
10679
10722
  var independenceReasons = (row, authors) => {
10680
10723
  const reasons = [];
10681
10724
  if (!row.actor.session)
10682
10725
  reasons.push("no_session");
10683
- else if (authors.has(row.actor.session))
10726
+ else if (authoredBy(authors, row.actor.session))
10684
10727
  reasons.push("author_session");
10685
10728
  if (row.self === true && !reasons.includes("no_session"))
10686
10729
  reasons.push("self");
@@ -10964,7 +11007,7 @@ var productDecision = (task, ledger) => {
10964
11007
  };
10965
11008
  };
10966
11009
  var planRecorded = (task, root) => {
10967
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync2(path7.resolve(root, ref.path)));
11010
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync2(path8.resolve(root, ref.path)));
10968
11011
  if (cited)
10969
11012
  return {
10970
11013
  met: true,
@@ -11580,7 +11623,7 @@ var judgeInputSummary = (raw) => {
11580
11623
 
11581
11624
  // packages/workit-core/src/autonomy.ts
11582
11625
  import os3 from "node:os";
11583
- import path8 from "node:path";
11626
+ import path9 from "node:path";
11584
11627
  var accountHome = () => {
11585
11628
  try {
11586
11629
  return os3.userInfo().homedir;
@@ -11589,10 +11632,10 @@ var accountHome = () => {
11589
11632
  }
11590
11633
  };
11591
11634
  var grantsHome = { resolve: accountHome };
11592
- var grantsDir = () => path8.join(grantsHome.resolve(), ".config", "workit");
11635
+ var grantsDir = () => path9.join(grantsHome.resolve(), ".config", "workit");
11593
11636
  var grantsOverride = () => {
11594
- const configured = path8.resolve(resolveConfigDir());
11595
- return configured === path8.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 ${path8.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
11637
+ const configured = path9.resolve(resolveConfigDir());
11638
+ return configured === path9.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 ${path9.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
11596
11639
  };
11597
11640
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
11598
11641
  var DEFAULT_GRANTS = Object.freeze({
@@ -11716,9 +11759,9 @@ var suggested = (kind) => kind === "merge" ? "verified" : "true";
11716
11759
  var grantHint = (workspace, kind) => `ask the user to run, in their own terminal: workit grant set ${workspace ?? "<workspace>"} ${kind}=${suggested(kind)}`;
11717
11760
 
11718
11761
  // packages/workit-core/src/core/task-store.ts
11719
- import { createHash as createHash8, randomUUID as randomUUID3 } from "node:crypto";
11720
- import * as fs10 from "node:fs";
11721
- import path13 from "node:path";
11762
+ import { createHash as createHash9, randomUUID as randomUUID3 } from "node:crypto";
11763
+ import * as fs11 from "node:fs";
11764
+ import path14 from "node:path";
11722
11765
 
11723
11766
  // node_modules/@openclaw/fs-safe/dist/errors.js
11724
11767
  var OPERATIONAL_CODES = new Set([
@@ -11769,10 +11812,10 @@ function sameFileIdentity(left, right, platform = process.platform) {
11769
11812
  // node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
11770
11813
  import { randomBytes as randomBytes2 } from "node:crypto";
11771
11814
  import fsSync from "node:fs";
11772
- import path9 from "node:path";
11815
+ import path10 from "node:path";
11773
11816
 
11774
11817
  // node_modules/@openclaw/fs-safe/dist/bounded-read.js
11775
- import fs5 from "node:fs";
11818
+ import fs6 from "node:fs";
11776
11819
 
11777
11820
  // node_modules/@openclaw/fs-safe/dist/byte-budget.js
11778
11821
  function normalizeMaxBytes(value, options = {}) {
@@ -11809,7 +11852,7 @@ function readFileDescriptorBoundedSync(fd, maxBytes) {
11809
11852
  let total = 0;
11810
11853
  while (true) {
11811
11854
  const length = nextReadLength(total, maxBytes, scratch.length);
11812
- const bytesRead = fs5.readSync(fd, scratch, 0, length, null);
11855
+ const bytesRead = fs6.readSync(fd, scratch, 0, length, null);
11813
11856
  if (bytesRead === 0) {
11814
11857
  return Buffer.concat(chunks, total);
11815
11858
  }
@@ -11818,9 +11861,9 @@ function readFileDescriptorBoundedSync(fd, maxBytes) {
11818
11861
  }
11819
11862
 
11820
11863
  // node_modules/@openclaw/fs-safe/dist/read-open-flags.js
11821
- import fs6 from "node:fs";
11864
+ import fs7 from "node:fs";
11822
11865
  function resolveReadOpenFlags(options) {
11823
- const constants = options?.constants ?? fs6.constants;
11866
+ const constants = options?.constants ?? fs7.constants;
11824
11867
  const noFollow = process.platform !== "win32" && options?.followSymlinks !== true && typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0;
11825
11868
  const nonBlocking = process.platform !== "win32" && typeof constants.O_NONBLOCK === "number" ? constants.O_NONBLOCK : 0;
11826
11869
  return constants.O_RDONLY | noFollow | nonBlocking;
@@ -11859,13 +11902,13 @@ ${ownershipToken}
11859
11902
  return { raw, ownershipToken };
11860
11903
  }
11861
11904
  function relativeSidecarLockPath(lockRoot, lockPath) {
11862
- const resolved = path9.resolve(lockPath);
11863
- const lexicalRelative = path9.relative(lockRoot.rootDir, resolved);
11864
- const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path9.sep}`) && !path9.isAbsolute(lexicalRelative) ? lexicalRelative : path9.relative(lockRoot.rootReal, resolved);
11865
- if (!relative || relative === ".." || relative.startsWith(`..${path9.sep}`) || path9.isAbsolute(relative)) {
11905
+ const resolved = path10.resolve(lockPath);
11906
+ const lexicalRelative = path10.relative(lockRoot.rootDir, resolved);
11907
+ const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path10.sep}`) && !path10.isAbsolute(lexicalRelative) ? lexicalRelative : path10.relative(lockRoot.rootReal, resolved);
11908
+ if (!relative || relative === ".." || relative.startsWith(`..${path10.sep}`) || path10.isAbsolute(relative)) {
11866
11909
  throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
11867
11910
  }
11868
- return relative.split(path9.sep).join(path9.posix.sep);
11911
+ return relative.split(path10.sep).join(path10.posix.sep);
11869
11912
  }
11870
11913
  function parseSidecarLockPayload(raw, parser) {
11871
11914
  if (parser) {
@@ -12011,8 +12054,8 @@ function getFsSafeLockConfig() {
12011
12054
  }
12012
12055
 
12013
12056
  // node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
12014
- import fs7 from "node:fs";
12015
- import path10 from "node:path";
12057
+ import fs8 from "node:fs";
12058
+ import path11 from "node:path";
12016
12059
 
12017
12060
  // node_modules/@openclaw/fs-safe/dist/timing.js
12018
12061
  function sleepSync(ms) {
@@ -12042,7 +12085,7 @@ function releaseAllSyncHeldLocks() {
12042
12085
  }
12043
12086
  try {
12044
12087
  if (held.fd !== undefined)
12045
- fs7.closeSync(held.fd);
12088
+ fs8.closeSync(held.fd);
12046
12089
  } catch {}
12047
12090
  try {
12048
12091
  removeSidecarLockIfUnchangedSync(held.lockPath, held.snapshot);
@@ -12077,7 +12120,7 @@ function releaseSyncHeldLock(held) {
12077
12120
  if (held.fd !== undefined) {
12078
12121
  const fd = held.fd;
12079
12122
  held.fd = undefined;
12080
- fs7.closeSync(fd);
12123
+ fs8.closeSync(fd);
12081
12124
  }
12082
12125
  removeSidecarLockIfUnchangedSync(held.lockPath, held.snapshot);
12083
12126
  heldLocks.delete(held.normalizedTargetPath);
@@ -12100,29 +12143,29 @@ function createSyncHeldLockHandle(held) {
12100
12143
  };
12101
12144
  }
12102
12145
  function canonicalLockParentSync(parent) {
12103
- return process.platform === "win32" ? fs7.realpathSync.native(parent) : fs7.realpathSync(parent);
12146
+ return process.platform === "win32" ? fs8.realpathSync.native(parent) : fs8.realpathSync(parent);
12104
12147
  }
12105
12148
  function normalizeTargetPath(targetPath) {
12106
- const resolved = path10.resolve(targetPath);
12107
- fs7.mkdirSync(path10.dirname(resolved), { recursive: true });
12149
+ const resolved = path11.resolve(targetPath);
12150
+ fs8.mkdirSync(path11.dirname(resolved), { recursive: true });
12108
12151
  try {
12109
- return path10.join(canonicalLockParentSync(path10.dirname(resolved)), path10.basename(resolved));
12152
+ return path11.join(canonicalLockParentSync(path11.dirname(resolved)), path11.basename(resolved));
12110
12153
  } catch {
12111
12154
  return resolved;
12112
12155
  }
12113
12156
  }
12114
12157
  function boundedLockPath(lockPath, lockRoot) {
12115
- const resolved = path10.resolve(lockPath);
12158
+ const resolved = path11.resolve(lockPath);
12116
12159
  if (!lockRoot)
12117
12160
  return resolved;
12118
12161
  relativeSidecarLockPath(lockRoot, resolved);
12119
- const parent = path10.dirname(resolved);
12162
+ const parent = path11.dirname(resolved);
12120
12163
  const parentReal = canonicalLockParentSync(parent);
12121
- const parentRelative = path10.relative(lockRoot.rootReal, parentReal);
12122
- if (parentRelative === ".." || parentRelative.startsWith(`..${path10.sep}`) || path10.isAbsolute(parentRelative)) {
12164
+ const parentRelative = path11.relative(lockRoot.rootReal, parentReal);
12165
+ if (parentRelative === ".." || parentRelative.startsWith(`..${path11.sep}`) || path11.isAbsolute(parentRelative)) {
12123
12166
  throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
12124
12167
  }
12125
- return path10.join(parentReal, path10.basename(resolved));
12168
+ return path11.join(parentReal, path11.basename(resolved));
12126
12169
  }
12127
12170
  function defaultShouldReclaim(snapshot, staleMs, nowMs) {
12128
12171
  const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
@@ -12132,7 +12175,7 @@ function defaultShouldReclaim(snapshot, staleMs, nowMs) {
12132
12175
  }
12133
12176
  function reclaimGuardExists(reclaimGuardPath) {
12134
12177
  try {
12135
- fs7.lstatSync(reclaimGuardPath);
12178
+ fs8.lstatSync(reclaimGuardPath);
12136
12179
  return true;
12137
12180
  } catch (error) {
12138
12181
  if (error.code === "ENOENT")
@@ -12196,19 +12239,19 @@ function acquireFileLockSync(targetPath, options) {
12196
12239
  const { raw, ownershipToken } = serializeSidecarLockPayload(payload);
12197
12240
  let lockFileCreateDenied = false;
12198
12241
  try {
12199
- const noFollow = process.platform !== "win32" && typeof fs7.constants.O_NOFOLLOW === "number" ? fs7.constants.O_NOFOLLOW : 0;
12242
+ const noFollow = process.platform !== "win32" && typeof fs8.constants.O_NOFOLLOW === "number" ? fs8.constants.O_NOFOLLOW : 0;
12200
12243
  try {
12201
- fd = fs7.openSync(lockPath, fs7.constants.O_WRONLY | fs7.constants.O_CREAT | fs7.constants.O_EXCL | noFollow, 384);
12244
+ fd = fs8.openSync(lockPath, fs8.constants.O_WRONLY | fs8.constants.O_CREAT | fs8.constants.O_EXCL | noFollow, 384);
12202
12245
  } catch (error) {
12203
12246
  lockFileCreateDenied = isTransientLockFileDenial(error, lockPath);
12204
12247
  throw error;
12205
12248
  }
12206
- fs7.writeFileSync(fd, raw, "utf8");
12207
- fs7.fsyncSync(fd);
12249
+ fs8.writeFileSync(fd, raw, "utf8");
12250
+ fs8.fsyncSync(fd);
12208
12251
  const snapshot = {
12209
12252
  raw,
12210
12253
  payload,
12211
- stat: fs7.fstatSync(fd),
12254
+ stat: fs8.fstatSync(fd),
12212
12255
  ownershipToken
12213
12256
  };
12214
12257
  const createdHeld = {
@@ -12243,8 +12286,8 @@ function acquireFileLockSync(targetPath, options) {
12243
12286
  return returnedHandle;
12244
12287
  } catch (error) {
12245
12288
  if (fd !== undefined) {
12246
- const failed = { payload: null, stat: fs7.fstatSync(fd) };
12247
- fs7.closeSync(fd);
12289
+ const failed = { payload: null, stat: fs8.fstatSync(fd) };
12290
+ fs8.closeSync(fd);
12248
12291
  fd = undefined;
12249
12292
  removeSidecarLockIfUnchangedSync(lockPath, failed);
12250
12293
  }
@@ -12293,7 +12336,7 @@ function acquireFileLockSync(targetPath, options) {
12293
12336
  })) {
12294
12337
  let ownsReclaimGuard = false;
12295
12338
  try {
12296
- fs7.mkdirSync(reclaimGuardPath);
12339
+ fs8.mkdirSync(reclaimGuardPath);
12297
12340
  ownsReclaimGuard = true;
12298
12341
  if (removeSidecarLockIfUnchangedSync(lockPath, snapshot))
12299
12342
  continue;
@@ -12306,7 +12349,7 @@ function acquireFileLockSync(targetPath, options) {
12306
12349
  } finally {
12307
12350
  if (ownsReclaimGuard) {
12308
12351
  try {
12309
- fs7.rmdirSync(reclaimGuardPath);
12352
+ fs8.rmdirSync(reclaimGuardPath);
12310
12353
  } catch {}
12311
12354
  }
12312
12355
  }
@@ -12323,13 +12366,13 @@ function acquireFileLockSync(targetPath, options) {
12323
12366
  }
12324
12367
  // packages/workit-core/src/core/package-root.ts
12325
12368
  import { existsSync as existsSync3 } from "node:fs";
12326
- import path11 from "node:path";
12369
+ import path12 from "node:path";
12327
12370
  import { fileURLToPath } from "node:url";
12328
12371
  var packageRoot = () => {
12329
- let dir = path11.dirname(fileURLToPath(import.meta.url));
12372
+ let dir = path12.dirname(fileURLToPath(import.meta.url));
12330
12373
  while (true) {
12331
- const parent = path11.dirname(dir);
12332
- if (existsSync3(path11.join(dir, "package.json")) || parent === dir)
12374
+ const parent = path12.dirname(dir);
12375
+ if (existsSync3(path12.join(dir, "package.json")) || parent === dir)
12333
12376
  return dir;
12334
12377
  dir = parent;
12335
12378
  }
@@ -12337,7 +12380,7 @@ var packageRoot = () => {
12337
12380
 
12338
12381
  // packages/workit-core/src/core/store-lock.ts
12339
12382
  import { spawnSync as spawnSync8 } from "node:child_process";
12340
- import fs8 from "node:fs";
12383
+ import fs9 from "node:fs";
12341
12384
  import { hostname } from "node:os";
12342
12385
  var metadataLockSchema = object({
12343
12386
  pid: number2().int().nonnegative().safe(),
@@ -12379,7 +12422,7 @@ var parseProcStatStart = (stat) => {
12379
12422
  var processStartOf = (pid) => {
12380
12423
  if (process.platform === "linux") {
12381
12424
  try {
12382
- return parseProcStatStart(fs8.readFileSync(`/proc/${pid}/stat`, "utf8"));
12425
+ return parseProcStatStart(fs9.readFileSync(`/proc/${pid}/stat`, "utf8"));
12383
12426
  } catch {
12384
12427
  return null;
12385
12428
  }
@@ -12400,7 +12443,7 @@ var processStartOf = (pid) => {
12400
12443
  };
12401
12444
  var readTrimmed = (file) => {
12402
12445
  try {
12403
- return fs8.readFileSync(file, "utf8").trim() || null;
12446
+ return fs9.readFileSync(file, "utf8").trim() || null;
12404
12447
  } catch {
12405
12448
  return null;
12406
12449
  }
@@ -12411,7 +12454,7 @@ var localLockHost = () => {
12411
12454
  return cachedLockHost;
12412
12455
  let pidns = null;
12413
12456
  try {
12414
- pidns = /\[(\d+)\]/.exec(fs8.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
12457
+ pidns = /\[(\d+)\]/.exec(fs9.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
12415
12458
  } catch {}
12416
12459
  const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
12417
12460
  cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
@@ -12457,7 +12500,7 @@ var classifyLockOwner = (payload, ageMs, localHost = localLockHost(), options =
12457
12500
  };
12458
12501
  var ageOf = (file, nowMs) => {
12459
12502
  try {
12460
- return nowMs - fs8.lstatSync(file).mtimeMs;
12503
+ return nowMs - fs9.lstatSync(file).mtimeMs;
12461
12504
  } catch {
12462
12505
  return null;
12463
12506
  }
@@ -12468,7 +12511,7 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
12468
12511
  if (age === null || age <= RECLAIM_GUARD_TTL_MS)
12469
12512
  return false;
12470
12513
  try {
12471
- fs8.rmdirSync(guard);
12514
+ fs9.rmdirSync(guard);
12472
12515
  return true;
12473
12516
  } catch {
12474
12517
  return false;
@@ -12477,8 +12520,8 @@ var clearAbandonedReclaimGuard = (lockPath, nowMs = Date.now()) => {
12477
12520
 
12478
12521
  // packages/workit-core/src/store/event-log.ts
12479
12522
  import { randomUUID as randomUUID2 } from "node:crypto";
12480
- import fs9 from "node:fs";
12481
- import path12 from "node:path";
12523
+ import fs10 from "node:fs";
12524
+ import path13 from "node:path";
12482
12525
  var EVENT_VERSION = 1;
12483
12526
 
12484
12527
  class LogDamage extends Error {
@@ -12507,7 +12550,7 @@ var parseEvent = (line, file, offset) => {
12507
12550
  function readLog(file, from = 0, expectSeq = null) {
12508
12551
  let fd;
12509
12552
  try {
12510
- fd = fs9.openSync(file, "r");
12553
+ fd = fs10.openSync(file, "r");
12511
12554
  } catch (error) {
12512
12555
  if (error.code === "ENOENT")
12513
12556
  return null;
@@ -12516,13 +12559,13 @@ function readLog(file, from = 0, expectSeq = null) {
12516
12559
  let bytes;
12517
12560
  let size;
12518
12561
  try {
12519
- size = fs9.fstatSync(fd).size;
12562
+ size = fs10.fstatSync(fd).size;
12520
12563
  if (from > size)
12521
12564
  throw new LogDamage(`${file}: shorter than its snapshot`);
12522
12565
  bytes = Buffer.alloc(size - from);
12523
12566
  let read = 0;
12524
12567
  while (read < bytes.length) {
12525
- const got = fs9.readSync(fd, bytes, read, bytes.length - read, from + read);
12568
+ const got = fs10.readSync(fd, bytes, read, bytes.length - read, from + read);
12526
12569
  if (got === 0)
12527
12570
  break;
12528
12571
  read += got;
@@ -12530,7 +12573,7 @@ function readLog(file, from = 0, expectSeq = null) {
12530
12573
  bytes = bytes.subarray(0, read);
12531
12574
  size = from + read;
12532
12575
  } finally {
12533
- fs9.closeSync(fd);
12576
+ fs10.closeSync(fd);
12534
12577
  }
12535
12578
  const events = [];
12536
12579
  let start = 0;
@@ -12555,11 +12598,11 @@ function readLog(file, from = 0, expectSeq = null) {
12555
12598
  }
12556
12599
  var fsyncDirectory = (dir) => {
12557
12600
  try {
12558
- const fd = fs9.openSync(dir, "r");
12601
+ const fd = fs10.openSync(dir, "r");
12559
12602
  try {
12560
- fs9.fsyncSync(fd);
12603
+ fs10.fsyncSync(fd);
12561
12604
  } finally {
12562
- fs9.closeSync(fd);
12605
+ fs10.closeSync(fd);
12563
12606
  }
12564
12607
  } catch (error) {
12565
12608
  if (process.platform !== "win32")
@@ -12569,46 +12612,46 @@ var fsyncDirectory = (dir) => {
12569
12612
  function appendEvent(file, event, validEnd) {
12570
12613
  const line = Buffer.from(`${JSON.stringify(event)}
12571
12614
  `, "utf8");
12572
- const created = !fs9.existsSync(file);
12573
- if (!created && fs9.statSync(file).size !== validEnd) {
12574
- const cut = fs9.openSync(file, "r+");
12615
+ const created = !fs10.existsSync(file);
12616
+ if (!created && fs10.statSync(file).size !== validEnd) {
12617
+ const cut = fs10.openSync(file, "r+");
12575
12618
  try {
12576
- fs9.ftruncateSync(cut, validEnd);
12577
- fs9.fsyncSync(cut);
12619
+ fs10.ftruncateSync(cut, validEnd);
12620
+ fs10.fsyncSync(cut);
12578
12621
  } finally {
12579
- fs9.closeSync(cut);
12622
+ fs10.closeSync(cut);
12580
12623
  }
12581
12624
  }
12582
- const fd = fs9.openSync(file, "a", 384);
12625
+ const fd = fs10.openSync(file, "a", 384);
12583
12626
  try {
12584
12627
  let written = 0;
12585
12628
  while (written < line.length)
12586
- written += fs9.writeSync(fd, line, written, line.length - written);
12587
- fs9.fsyncSync(fd);
12629
+ written += fs10.writeSync(fd, line, written, line.length - written);
12630
+ fs10.fsyncSync(fd);
12588
12631
  } finally {
12589
- fs9.closeSync(fd);
12632
+ fs10.closeSync(fd);
12590
12633
  }
12591
12634
  if (created)
12592
- fsyncDirectory(path12.dirname(file));
12635
+ fsyncDirectory(path13.dirname(file));
12593
12636
  return validEnd;
12594
12637
  }
12595
12638
  function replaceFile(file, bytes, durable) {
12596
12639
  const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
12597
12640
  try {
12598
- const fd = fs9.openSync(temporary, "wx", 384);
12641
+ const fd = fs10.openSync(temporary, "wx", 384);
12599
12642
  try {
12600
- fs9.writeSync(fd, bytes);
12643
+ fs10.writeSync(fd, bytes);
12601
12644
  if (durable)
12602
- fs9.fsyncSync(fd);
12645
+ fs10.fsyncSync(fd);
12603
12646
  } finally {
12604
- fs9.closeSync(fd);
12647
+ fs10.closeSync(fd);
12605
12648
  }
12606
12649
  renameWithRetry(temporary, file);
12607
12650
  if (durable)
12608
- fsyncDirectory(path12.dirname(file));
12651
+ fsyncDirectory(path13.dirname(file));
12609
12652
  } catch (error) {
12610
12653
  try {
12611
- fs9.unlinkSync(temporary);
12654
+ fs10.unlinkSync(temporary);
12612
12655
  } catch {}
12613
12656
  throw error;
12614
12657
  }
@@ -12633,7 +12676,7 @@ var retryTransient = (run) => {
12633
12676
  }
12634
12677
  }
12635
12678
  };
12636
- var renameWithRetry = (from, to) => retryTransient(() => fs9.renameSync(from, to));
12679
+ var renameWithRetry = (from, to) => retryTransient(() => fs10.renameSync(from, to));
12637
12680
 
12638
12681
  // packages/workit-core/src/store/patch.ts
12639
12682
  var isPlainObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -12746,7 +12789,7 @@ var isPatch = (value) => Array.isArray(value) && value.every((op) => {
12746
12789
  });
12747
12790
 
12748
12791
  // packages/workit-core/src/store/reduce.ts
12749
- import { createHash as createHash7 } from "node:crypto";
12792
+ import { createHash as createHash8 } from "node:crypto";
12750
12793
  var OPENING_TYPES = new Set([
12751
12794
  "task.opened",
12752
12795
  "task.imported",
@@ -12775,7 +12818,7 @@ var isObject5 = (value) => typeof value === "object" && value !== null && !Array
12775
12818
  var isKey = (value) => isObject5(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string");
12776
12819
  var BLOB_REF = "$blob";
12777
12820
  var blobRef = (value) => isObject5(value) && Object.keys(value).length === 1 && typeof value[BLOB_REF] === "string" && /^[0-9a-f]{64}$/.test(value[BLOB_REF]) ? value[BLOB_REF] : null;
12778
- var blobDigest = (value) => createHash7("sha256").update(JSON.stringify(value)).digest("hex");
12821
+ var blobDigest = (value) => createHash8("sha256").update(JSON.stringify(value)).digest("hex");
12779
12822
  var toBlob = (value, blobs) => {
12780
12823
  if (!isObject5(value))
12781
12824
  return value;
@@ -12930,7 +12973,7 @@ var indexEntry = (task, key, legacy, file) => {
12930
12973
  };
12931
12974
  var fileSignature = (file) => {
12932
12975
  try {
12933
- const stat = fs10.statSync(file, { bigint: true });
12976
+ const stat = fs11.statSync(file, { bigint: true });
12934
12977
  return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
12935
12978
  } catch {
12936
12979
  return null;
@@ -12948,7 +12991,7 @@ var cachedRuntimeVersion = null;
12948
12991
  var runtimeVersion = () => {
12949
12992
  if (cachedRuntimeVersion === null) {
12950
12993
  try {
12951
- const pkg = JSON.parse(fs10.readFileSync(path13.join(packageRoot(), "package.json"), "utf8"));
12994
+ const pkg = JSON.parse(fs11.readFileSync(path14.join(packageRoot(), "package.json"), "utf8"));
12952
12995
  cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
12953
12996
  } catch {
12954
12997
  cachedRuntimeVersion = "0.0.0";
@@ -12974,14 +13017,14 @@ var isNewerVersion = (candidate, current) => {
12974
13017
  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);
12975
13018
  var readText = (file) => {
12976
13019
  try {
12977
- return fs10.readFileSync(file, "utf8");
13020
+ return fs11.readFileSync(file, "utf8");
12978
13021
  } catch {
12979
13022
  return "";
12980
13023
  }
12981
13024
  };
12982
13025
  var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
12983
13026
  var branchStart = (location, branch, aliases) => {
12984
- const first = readText(path13.join(path13.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
13027
+ const first = readText(path14.join(path14.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
12985
13028
  `, 1)[0];
12986
13029
  const created = REFLOG_LINE.exec(first);
12987
13030
  const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
@@ -12991,7 +13034,7 @@ var branchStart = (location, branch, aliases) => {
12991
13034
  return start;
12992
13035
  if (!location.git)
12993
13036
  return null;
12994
- for (const line of readText(path13.join(location.git.gitDir, "logs", "HEAD")).split(`
13037
+ for (const line of readText(path14.join(location.git.gitDir, "logs", "HEAD")).split(`
12995
13038
  `)) {
12996
13039
  const entry = REFLOG_LINE.exec(line);
12997
13040
  const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
@@ -13001,23 +13044,23 @@ var branchStart = (location, branch, aliases) => {
13001
13044
  }
13002
13045
  return null;
13003
13046
  };
13004
- var refExists = (common, ref) => fs10.existsSync(path13.join(common, ref)) || readText(path13.join(common, "packed-refs")).includes(` ${ref}
13047
+ var refExists = (common, ref) => fs11.existsSync(path14.join(common, ref)) || readText(path14.join(common, "packed-refs")).includes(` ${ref}
13005
13048
  `);
13006
13049
  var origins = new Map;
13007
13050
  var sameDirectoryIdentity = (left, right) => {
13008
- if (!path13.isAbsolute(left) || !path13.isAbsolute(right))
13051
+ if (!path14.isAbsolute(left) || !path14.isAbsolute(right))
13009
13052
  return false;
13010
- const normalizedLeft = path13.resolve(left);
13011
- const normalizedRight = path13.resolve(right);
13053
+ const normalizedLeft = path14.resolve(left);
13054
+ const normalizedRight = path14.resolve(right);
13012
13055
  try {
13013
- const a = fs10.statSync(normalizedLeft, { bigint: true });
13014
- const b = fs10.statSync(normalizedRight, { bigint: true });
13056
+ const a = fs11.statSync(normalizedLeft, { bigint: true });
13057
+ const b = fs11.statSync(normalizedRight, { bigint: true });
13015
13058
  if (!a.isDirectory() || !b.isDirectory())
13016
13059
  return false;
13017
13060
  if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
13018
13061
  return true;
13019
- const canonicalLeft = fs10.realpathSync(normalizedLeft);
13020
- const canonicalRight = fs10.realpathSync(normalizedRight);
13062
+ const canonicalLeft = fs11.realpathSync(normalizedLeft);
13063
+ const canonicalRight = fs11.realpathSync(normalizedRight);
13021
13064
  if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
13022
13065
  return true;
13023
13066
  return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
@@ -13054,8 +13097,8 @@ class TaskStore {
13054
13097
  migrationChecked = false;
13055
13098
  compactDue = new Set;
13056
13099
  constructor(root, options = {}) {
13057
- const resolved = path13.resolve(root);
13058
- this.origin = fs10.existsSync(resolved) ? fs10.realpathSync(resolved) : resolved;
13100
+ const resolved = path14.resolve(root);
13101
+ this.origin = fs11.existsSync(resolved) ? fs11.realpathSync(resolved) : resolved;
13059
13102
  this.located = resolveStore(this.origin);
13060
13103
  this.root = checkoutRootOf(this.origin, this.located);
13061
13104
  this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
@@ -13063,7 +13106,7 @@ class TaskStore {
13063
13106
  this.migrateOnRead = options.migrateOnRead;
13064
13107
  }
13065
13108
  location() {
13066
- if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs10.existsSync(path13.join(this.root, ".git"))) {
13109
+ if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs11.existsSync(path14.join(this.root, ".git"))) {
13067
13110
  this.located = undefined;
13068
13111
  this.migrationChecked = false;
13069
13112
  }
@@ -13127,7 +13170,7 @@ class TaskStore {
13127
13170
  const tasksDir = this.paths().tasks;
13128
13171
  let names;
13129
13172
  try {
13130
- names = fs10.existsSync(tasksDir) ? fs10.readdirSync(tasksDir).filter((name) => validId(name)) : [];
13173
+ names = fs11.existsSync(tasksDir) ? fs11.readdirSync(tasksDir).filter((name) => validId(name)) : [];
13131
13174
  } catch (error) {
13132
13175
  return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
13133
13176
  path: tasksDir
@@ -13168,7 +13211,7 @@ class TaskStore {
13168
13211
  const file = this.paths().workspace;
13169
13212
  let bytes;
13170
13213
  try {
13171
- bytes = retryTransient(() => fs10.readFileSync(file, "utf8"));
13214
+ bytes = retryTransient(() => fs11.readFileSync(file, "utf8"));
13172
13215
  } catch (error) {
13173
13216
  if (error.code === "ENOENT")
13174
13217
  return success(null, null, null);
@@ -13219,7 +13262,7 @@ class TaskStore {
13219
13262
  const moved = this.bindLocked(again.data.id, key, input.now);
13220
13263
  return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
13221
13264
  }
13222
- const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path13.basename(this.root) || this.root;
13265
+ const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path14.basename(this.root) || this.root;
13223
13266
  const intent = {
13224
13267
  objective: input.objective?.trim() || `Work on ${label}`,
13225
13268
  scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
@@ -13279,14 +13322,14 @@ class TaskStore {
13279
13322
  const branch = key.ok ? key.data.branch : null;
13280
13323
  if (!branch || !location.ok)
13281
13324
  return success(null, null, null);
13282
- const common = path13.dirname(location.data.dir);
13283
- const heads = path13.join(common, "logs", "refs", "heads");
13284
- const headLog = location.data.git ? path13.join(location.data.git.gitDir, "logs", "HEAD") : "";
13325
+ const common = path14.dirname(location.data.dir);
13326
+ const heads = path14.join(common, "logs", "refs", "heads");
13327
+ const headLog = location.data.git ? path14.join(location.data.git.gitDir, "logs", "HEAD") : "";
13285
13328
  const signatures = [
13286
- path13.join(heads, branch),
13329
+ path14.join(heads, branch),
13287
13330
  headLog,
13288
13331
  heads,
13289
- path13.join(common, "packed-refs")
13332
+ path14.join(common, "packed-refs")
13290
13333
  ].map((file) => fileSignature(file) ?? "-");
13291
13334
  const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
13292
13335
  let resolved = origins.get(slot);
@@ -13298,7 +13341,7 @@ class TaskStore {
13298
13341
  aliases.push(rename.from);
13299
13342
  const start = branchStart(location.data, branch, aliases);
13300
13343
  const slash = start?.indexOf("/") ?? -1;
13301
- const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path13.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
13344
+ const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path14.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
13302
13345
  const name = start && (remote ? start.slice(slash + 1) : start);
13303
13346
  resolved = {
13304
13347
  signature: signatures.join("|"),
@@ -13536,13 +13579,13 @@ class TaskStore {
13536
13579
  if (!ready.ok)
13537
13580
  return ready;
13538
13581
  const dirs = this.paths();
13539
- const recovery = path13.join(this.root, ".workit", "recovery");
13540
- if (fs10.existsSync(recovery)) {
13582
+ const recovery = path14.join(this.root, ".workit", "recovery");
13583
+ if (fs11.existsSync(recovery)) {
13541
13584
  let files = 0;
13542
13585
  let bytes = 0;
13543
13586
  try {
13544
- for (const name of fs10.readdirSync(recovery)) {
13545
- const stat = fs10.lstatSync(path13.join(recovery, name));
13587
+ for (const name of fs11.readdirSync(recovery)) {
13588
+ const stat = fs11.lstatSync(path14.join(recovery, name));
13546
13589
  if (!stat.isFile())
13547
13590
  continue;
13548
13591
  files += 1;
@@ -13551,17 +13594,17 @@ class TaskStore {
13551
13594
  } catch {}
13552
13595
  const remove = options.pruneRecovery === true && !dryRun;
13553
13596
  if (remove)
13554
- fs10.rmSync(recovery, { recursive: true, force: true });
13597
+ fs11.rmSync(recovery, { recursive: true, force: true });
13555
13598
  report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
13556
13599
  }
13557
- if (!fs10.existsSync(dirs.tasks))
13600
+ if (!fs11.existsSync(dirs.tasks))
13558
13601
  return success(null, null, report);
13559
- const names = fs10.readdirSync(dirs.tasks).filter((name) => validId(name));
13602
+ const names = fs11.readdirSync(dirs.tasks).filter((name) => validId(name));
13560
13603
  for (const name of names) {
13561
13604
  const file = this.eventsPath(name);
13562
13605
  let size = 0;
13563
13606
  try {
13564
- size = fs10.statSync(file).size;
13607
+ size = fs11.statSync(file).size;
13565
13608
  } catch {
13566
13609
  continue;
13567
13610
  }
@@ -13589,16 +13632,16 @@ class TaskStore {
13589
13632
  const references = new Set;
13590
13633
  for (const name of names)
13591
13634
  try {
13592
- for (const match of fs10.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
13635
+ for (const match of fs11.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
13593
13636
  references.add(match[1]);
13594
13637
  } catch {}
13595
- if (fs10.existsSync(dirs.blobs))
13596
- for (const name of fs10.readdirSync(dirs.blobs)) {
13638
+ if (fs11.existsSync(dirs.blobs))
13639
+ for (const name of fs11.readdirSync(dirs.blobs)) {
13597
13640
  const digest = name.replace(/\.json$/, "");
13598
- const file = path13.join(dirs.blobs, name);
13641
+ const file = path14.join(dirs.blobs, name);
13599
13642
  let stat;
13600
13643
  try {
13601
- stat = fs10.lstatSync(file);
13644
+ stat = fs11.lstatSync(file);
13602
13645
  } catch {
13603
13646
  continue;
13604
13647
  }
@@ -13609,11 +13652,11 @@ class TaskStore {
13609
13652
  report.blobs.removed += 1;
13610
13653
  report.blobs.removedBytes += stat.size;
13611
13654
  if (!dryRun)
13612
- fs10.rmSync(file, { force: true });
13655
+ fs11.rmSync(file, { force: true });
13613
13656
  }
13614
13657
  return success(null, null, null);
13615
13658
  };
13616
- const swept = dryRun ? sweep() : this.withLock(path13.join(dirs.dir, "gc.lock"), sweep);
13659
+ const swept = dryRun ? sweep() : this.withLock(path14.join(dirs.dir, "gc.lock"), sweep);
13617
13660
  if (!swept.ok)
13618
13661
  return swept;
13619
13662
  const temporaryDirs = [
@@ -13621,17 +13664,17 @@ class TaskStore {
13621
13664
  dirs.tasks,
13622
13665
  dirs.blobs,
13623
13666
  dirs.checkout,
13624
- ...names.map((name) => path13.join(dirs.tasks, name))
13667
+ ...names.map((name) => path14.join(dirs.tasks, name))
13625
13668
  ];
13626
13669
  for (const directory of temporaryDirs) {
13627
- if (!fs10.existsSync(directory))
13670
+ if (!fs11.existsSync(directory))
13628
13671
  continue;
13629
- for (const name of fs10.readdirSync(directory)) {
13672
+ for (const name of fs11.readdirSync(directory)) {
13630
13673
  if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
13631
13674
  continue;
13632
- const file = path13.join(directory, name);
13675
+ const file = path14.join(directory, name);
13633
13676
  try {
13634
- const stat = fs10.lstatSync(file);
13677
+ const stat = fs11.lstatSync(file);
13635
13678
  if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
13636
13679
  continue;
13637
13680
  } catch {
@@ -13639,37 +13682,37 @@ class TaskStore {
13639
13682
  }
13640
13683
  report.temporary.removed += 1;
13641
13684
  if (!dryRun)
13642
- fs10.rmSync(file, { force: true });
13685
+ fs11.rmSync(file, { force: true });
13643
13686
  }
13644
13687
  }
13645
13688
  return success(null, null, report);
13646
13689
  }
13647
13690
  paths() {
13648
13691
  const location = this.location();
13649
- const dir = location.ok ? location.data.dir : path13.join(this.root, ".workit");
13650
- const checkout = path13.join(dir, "checkouts", checkoutSlug(this.root));
13692
+ const dir = location.ok ? location.data.dir : path14.join(this.root, ".workit");
13693
+ const checkout = path14.join(dir, "checkouts", checkoutSlug(this.root));
13651
13694
  return {
13652
13695
  dir,
13653
13696
  shared: location.ok ? location.data.shared : false,
13654
- marker: path13.join(dir, "store.json"),
13697
+ marker: path14.join(dir, "store.json"),
13655
13698
  checkout,
13656
- workspace: path13.join(checkout, "workspace.json"),
13657
- checkoutLock: path13.join(checkout, "metadata.lock"),
13658
- tasks: path13.join(dir, "tasks"),
13659
- index: path13.join(dir, "task-index.json"),
13660
- blobs: path13.join(dir, "blobs", "candidates"),
13661
- legacy: path13.join(dir, "legacy", checkoutSlug(this.root), "v2"),
13662
- v2: path13.join(this.root, ".workit")
13699
+ workspace: path14.join(checkout, "workspace.json"),
13700
+ checkoutLock: path14.join(checkout, "metadata.lock"),
13701
+ tasks: path14.join(dir, "tasks"),
13702
+ index: path14.join(dir, "task-index.json"),
13703
+ blobs: path14.join(dir, "blobs", "candidates"),
13704
+ legacy: path14.join(dir, "legacy", checkoutSlug(this.root), "v2"),
13705
+ v2: path14.join(this.root, ".workit")
13663
13706
  };
13664
13707
  }
13665
13708
  eventsPath(taskId) {
13666
- return path13.join(this.paths().tasks, taskId, "events.jsonl");
13709
+ return path14.join(this.paths().tasks, taskId, "events.jsonl");
13667
13710
  }
13668
13711
  snapshotPath(taskId) {
13669
- return path13.join(this.paths().tasks, taskId, "snapshot.json");
13712
+ return path14.join(this.paths().tasks, taskId, "snapshot.json");
13670
13713
  }
13671
13714
  taskLockPath(taskId) {
13672
- return path13.join(this.paths().tasks, taskId, "lock");
13715
+ return path14.join(this.paths().tasks, taskId, "lock");
13673
13716
  }
13674
13717
  ready(write = false) {
13675
13718
  const location = this.location();
@@ -13679,7 +13722,7 @@ class TaskStore {
13679
13722
  return success(null, null, null);
13680
13723
  const dirs = this.paths();
13681
13724
  try {
13682
- const marker = JSON.parse(fs10.readFileSync(dirs.marker, "utf8"));
13725
+ const marker = JSON.parse(fs11.readFileSync(dirs.marker, "utf8"));
13683
13726
  if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
13684
13727
  return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
13685
13728
  } catch {}
@@ -13708,21 +13751,21 @@ class TaskStore {
13708
13751
  return {
13709
13752
  read: (digest) => {
13710
13753
  try {
13711
- return JSON.parse(fs10.readFileSync(path13.join(dir, `${digest}.json`), "utf8"));
13754
+ return JSON.parse(fs11.readFileSync(path14.join(dir, `${digest}.json`), "utf8"));
13712
13755
  } catch (error) {
13713
13756
  throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
13714
13757
  }
13715
13758
  },
13716
13759
  write: (digest, value) => {
13717
- const file = path13.join(dir, `${digest}.json`);
13718
- if (fs10.existsSync(file)) {
13760
+ const file = path14.join(dir, `${digest}.json`);
13761
+ if (fs11.existsSync(file)) {
13719
13762
  const stamp = new Date;
13720
13763
  try {
13721
- fs10.utimesSync(file, stamp, stamp);
13764
+ fs11.utimesSync(file, stamp, stamp);
13722
13765
  return;
13723
13766
  } catch {}
13724
13767
  }
13725
- fs10.mkdirSync(dir, { recursive: true });
13768
+ fs11.mkdirSync(dir, { recursive: true });
13726
13769
  replaceFile(file, JSON.stringify(value), true);
13727
13770
  }
13728
13771
  };
@@ -13804,7 +13847,7 @@ class TaskStore {
13804
13847
  }
13805
13848
  readSnapshot(taskId) {
13806
13849
  try {
13807
- const value = JSON.parse(fs10.readFileSync(this.snapshotPath(taskId), "utf8"));
13850
+ const value = JSON.parse(fs11.readFileSync(this.snapshotPath(taskId), "utf8"));
13808
13851
  if (!isObject6(value) || value.v !== 1 || value.task !== taskId || typeof value.seq !== "number" || typeof value.lastId !== "string" || typeof value.lastStart !== "number" || typeof value.end !== "number" || !isObject6(value.record))
13809
13852
  return null;
13810
13853
  return value;
@@ -13906,8 +13949,8 @@ class TaskStore {
13906
13949
  }
13907
13950
  }
13908
13951
  openTask(record, type, key, legacy = null) {
13909
- const dir = path13.join(this.paths().tasks, record.id);
13910
- fs10.mkdirSync(dir, { recursive: true });
13952
+ const dir = path14.join(this.paths().tasks, record.id);
13953
+ fs11.mkdirSync(dir, { recursive: true });
13911
13954
  const blobs = this.blobStore();
13912
13955
  const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
13913
13956
  const event = this.event(record.id, 1, type, {
@@ -13959,11 +14002,11 @@ class TaskStore {
13959
14002
  } catch {}
13960
14003
  if (!latest || latest.events[0]?.id !== last.id)
13961
14004
  return failure2("busy", "the task log changed during compaction; retry", { taskId });
13962
- const appended = fs10.readFileSync(file).subarray(log.end, latest.end);
14005
+ const appended = fs11.readFileSync(file).subarray(log.end, latest.end);
13963
14006
  const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
13964
14007
  replaceFile(file, bytes.toString("utf8"), true);
13965
14008
  try {
13966
- fs10.rmSync(this.snapshotPath(taskId), { force: true });
14009
+ fs11.rmSync(this.snapshotPath(taskId), { force: true });
13967
14010
  } catch {}
13968
14011
  const reloaded = this.load(taskId);
13969
14012
  if (reloaded.ok && reloaded.data)
@@ -13976,7 +14019,7 @@ class TaskStore {
13976
14019
  }
13977
14020
  countEvents(file) {
13978
14021
  try {
13979
- const bytes = fs10.readFileSync(file);
14022
+ const bytes = fs11.readFileSync(file);
13980
14023
  let count = 0;
13981
14024
  for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
13982
14025
  count += 1;
@@ -13998,8 +14041,8 @@ class TaskStore {
13998
14041
  return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
13999
14042
  }
14000
14043
  withKeyLock(key, operation) {
14001
- const digest = createHash8("sha256").update(key).digest("hex").slice(0, 32);
14002
- return this.withLock(path13.join(this.paths().dir, "keys", `${digest}.lock`), operation);
14044
+ const digest = createHash9("sha256").update(key).digest("hex").slice(0, 32);
14045
+ return this.withLock(path14.join(this.paths().dir, "keys", `${digest}.lock`), operation);
14003
14046
  }
14004
14047
  bindLocked(taskId, key, timestamp) {
14005
14048
  const current = this.readWorkspace();
@@ -14140,7 +14183,7 @@ class TaskStore {
14140
14183
  writeWorkspace(record) {
14141
14184
  const file = this.paths().workspace;
14142
14185
  try {
14143
- fs10.mkdirSync(path13.dirname(file), { recursive: true });
14186
+ fs11.mkdirSync(path14.dirname(file), { recursive: true });
14144
14187
  replaceFile(file, `${canonicalJson(record)}
14145
14188
  `, true);
14146
14189
  return success(null, null, null);
@@ -14150,7 +14193,7 @@ class TaskStore {
14150
14193
  }
14151
14194
  readIndex() {
14152
14195
  try {
14153
- const value = JSON.parse(fs10.readFileSync(this.paths().index, "utf8"));
14196
+ const value = JSON.parse(fs11.readFileSync(this.paths().index, "utf8"));
14154
14197
  if (!isObject6(value) || value.version !== INDEX_VERSION || !isObject6(value.tasks))
14155
14198
  return {};
14156
14199
  const tasks = {};
@@ -14164,7 +14207,7 @@ class TaskStore {
14164
14207
  }
14165
14208
  writeIndex(entries) {
14166
14209
  try {
14167
- fs10.mkdirSync(this.paths().tasks, { recursive: true });
14210
+ fs11.mkdirSync(this.paths().tasks, { recursive: true });
14168
14211
  replaceFile(this.paths().index, JSON.stringify({
14169
14212
  version: INDEX_VERSION,
14170
14213
  tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
@@ -14218,23 +14261,23 @@ class TaskStore {
14218
14261
  return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
14219
14262
  }
14220
14263
  v2Dirs() {
14221
- return [...new Set([this.root, this.origin])].map((dir) => path13.join(dir, ".workit"));
14264
+ return [...new Set([this.root, this.origin])].map((dir) => path14.join(dir, ".workit"));
14222
14265
  }
14223
14266
  pendingMigrations() {
14224
14267
  return this.v2Dirs().filter((dir) => {
14225
- const raw = this.readV2Workspace(path13.join(dir, "workspace.json"));
14268
+ const raw = this.readV2Workspace(path14.join(dir, "workspace.json"));
14226
14269
  return raw !== null && raw !== TOMBSTONE;
14227
14270
  });
14228
14271
  }
14229
14272
  migrateV2(v2) {
14230
14273
  const dirs = this.paths();
14231
- const v2Workspace = path13.join(v2, "workspace.json");
14232
- const v2Tasks = path13.join(v2, "tasks");
14233
- const legacy = path13.join(dirs.dir, "legacy", checkoutSlug(path13.dirname(v2)), "v2");
14274
+ const v2Workspace = path14.join(v2, "workspace.json");
14275
+ const v2Tasks = path14.join(v2, "tasks");
14276
+ const legacy = path14.join(dirs.dir, "legacy", checkoutSlug(path14.dirname(v2)), "v2");
14234
14277
  const raw = this.readV2Workspace(v2Workspace);
14235
14278
  if (raw === null || raw === TOMBSTONE)
14236
14279
  return success(null, null, null);
14237
- return this.withLock(dirs.checkoutLock, () => this.withLock(path13.join(v2, "metadata.lock"), () => {
14280
+ return this.withLock(dirs.checkoutLock, () => this.withLock(path14.join(v2, "metadata.lock"), () => {
14238
14281
  const workspace = this.readV2Workspace(v2Workspace);
14239
14282
  if (workspace === null || workspace === TOMBSTONE)
14240
14283
  return success(null, null, null);
@@ -14243,7 +14286,7 @@ class TaskStore {
14243
14286
  return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
14244
14287
  let files = [];
14245
14288
  try {
14246
- files = fs10.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path13.join(v2Tasks, name)));
14289
+ files = fs11.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path14.join(v2Tasks, name)));
14247
14290
  } catch {}
14248
14291
  const current = this.readWorkspace();
14249
14292
  if (!current.ok)
@@ -14261,22 +14304,22 @@ class TaskStore {
14261
14304
  let skipped = 0;
14262
14305
  const done = new Map;
14263
14306
  try {
14264
- fs10.mkdirSync(path13.join(legacy, "tasks"), { recursive: true });
14307
+ fs11.mkdirSync(path14.join(legacy, "tasks"), { recursive: true });
14265
14308
  for (const name of ["workspace.json", "index.json"])
14266
- if (fs10.existsSync(path13.join(v2, name)))
14267
- fs10.copyFileSync(path13.join(v2, name), path13.join(legacy, name));
14309
+ if (fs11.existsSync(path14.join(v2, name)))
14310
+ fs11.copyFileSync(path14.join(v2, name), path14.join(legacy, name));
14268
14311
  for (const name of files) {
14269
14312
  const id = name.slice(0, -5);
14270
- const file = path13.join(v2Tasks, name);
14271
- const bytes = fs10.readFileSync(file, "utf8");
14313
+ const file = path14.join(v2Tasks, name);
14314
+ const bytes = fs11.readFileSync(file, "utf8");
14272
14315
  const digest = sha256(bytes);
14273
- fs10.writeFileSync(path13.join(legacy, "tasks", name), bytes);
14316
+ fs11.writeFileSync(path14.join(legacy, "tasks", name), bytes);
14274
14317
  const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
14275
14318
  if (!parsed.ok || parsed.data.id !== id)
14276
14319
  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 });
14277
14320
  const record = { ...parsed.data, workspaceId: target.id };
14278
- const origin = { path: path13.join(legacy, "tasks", name), digest };
14279
- if (!fs10.existsSync(this.eventsPath(id))) {
14321
+ const origin = { path: path14.join(legacy, "tasks", name), digest };
14322
+ if (!fs11.existsSync(this.eventsPath(id))) {
14280
14323
  this.openTask(record, "migrated.from_v2", null, origin);
14281
14324
  migrated += 1;
14282
14325
  } else {
@@ -14311,8 +14354,8 @@ class TaskStore {
14311
14354
  try {
14312
14355
  let complete = true;
14313
14356
  for (const [name, digest] of done) {
14314
- const file = path13.join(v2Tasks, name);
14315
- if (sha256(fs10.readFileSync(file, "utf8")) !== digest) {
14357
+ const file = path14.join(v2Tasks, name);
14358
+ if (sha256(fs11.readFileSync(file, "utf8")) !== digest) {
14316
14359
  complete = false;
14317
14360
  continue;
14318
14361
  }
@@ -14322,7 +14365,7 @@ class TaskStore {
14322
14365
  return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
14323
14366
  path: v2
14324
14367
  });
14325
- fs10.rmSync(path13.join(v2, "index.json"), { force: true });
14368
+ fs11.rmSync(path14.join(v2, "index.json"), { force: true });
14326
14369
  this.writeTombstone(v2, target.id);
14327
14370
  } catch (error) {
14328
14371
  return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
@@ -14342,45 +14385,45 @@ class TaskStore {
14342
14385
  }
14343
14386
  adoptLocalStore() {
14344
14387
  const dirs = this.paths();
14345
- const marker = path13.join(dirs.v2, "store.json");
14346
- if (!dirs.shared || !fs10.existsSync(marker))
14388
+ const marker = path14.join(dirs.v2, "store.json");
14389
+ if (!dirs.shared || !fs11.existsSync(marker))
14347
14390
  return success(null, null, null);
14348
14391
  return this.withLock(dirs.checkoutLock, () => {
14349
- if (!fs10.existsSync(marker))
14392
+ if (!fs11.existsSync(marker))
14350
14393
  return success(null, null, null);
14351
14394
  let tasks = 0;
14352
14395
  try {
14353
14396
  const merge = (from, to) => {
14354
- if (!fs10.existsSync(to)) {
14355
- fs10.mkdirSync(path13.dirname(to), { recursive: true });
14397
+ if (!fs11.existsSync(to)) {
14398
+ fs11.mkdirSync(path14.dirname(to), { recursive: true });
14356
14399
  try {
14357
- fs10.renameSync(from, to);
14400
+ fs11.renameSync(from, to);
14358
14401
  } catch {
14359
- fs10.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
14360
- fs10.rmSync(from, { recursive: true, force: true });
14402
+ fs11.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
14403
+ fs11.rmSync(from, { recursive: true, force: true });
14361
14404
  }
14362
14405
  return;
14363
14406
  }
14364
- if (fs10.lstatSync(from).isDirectory() && fs10.lstatSync(to).isDirectory())
14365
- for (const name of fs10.readdirSync(from))
14366
- merge(path13.join(from, name), path13.join(to, name));
14407
+ if (fs11.lstatSync(from).isDirectory() && fs11.lstatSync(to).isDirectory())
14408
+ for (const name of fs11.readdirSync(from))
14409
+ merge(path14.join(from, name), path14.join(to, name));
14367
14410
  };
14368
14411
  for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
14369
- const from = path13.join(dirs.v2, sub);
14370
- if (!fs10.existsSync(from))
14412
+ const from = path14.join(dirs.v2, sub);
14413
+ if (!fs11.existsSync(from))
14371
14414
  continue;
14372
14415
  if (sub === "tasks") {
14373
- for (const name of fs10.readdirSync(from))
14374
- if (validId(name) && !fs10.existsSync(path13.join(dirs.dir, sub, name)))
14416
+ for (const name of fs11.readdirSync(from))
14417
+ if (validId(name) && !fs11.existsSync(path14.join(dirs.dir, sub, name)))
14375
14418
  tasks += 1;
14376
14419
  }
14377
- merge(from, path13.join(dirs.dir, sub));
14420
+ merge(from, path14.join(dirs.dir, sub));
14378
14421
  }
14379
- const ledger = path13.join(dirs.v2, "ledger");
14380
- if (fs10.existsSync(ledger) && !fs10.existsSync(path13.join(dirs.dir, "ledger")))
14381
- merge(ledger, path13.join(dirs.dir, "ledger"));
14382
- fs10.rmSync(path13.join(dirs.v2, "task-index.json"), { force: true });
14383
- fs10.renameSync(marker, path13.join(dirs.v2, "store.moved.json"));
14422
+ const ledger = path14.join(dirs.v2, "ledger");
14423
+ if (fs11.existsSync(ledger) && !fs11.existsSync(path14.join(dirs.dir, "ledger")))
14424
+ merge(ledger, path14.join(dirs.dir, "ledger"));
14425
+ fs11.rmSync(path14.join(dirs.v2, "task-index.json"), { force: true });
14426
+ fs11.renameSync(marker, path14.join(dirs.v2, "store.moved.json"));
14384
14427
  } catch (error) {
14385
14428
  return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
14386
14429
  path: dirs.v2
@@ -14400,7 +14443,7 @@ class TaskStore {
14400
14443
  readV2Workspace(file) {
14401
14444
  let bytes;
14402
14445
  try {
14403
- bytes = fs10.readFileSync(file, "utf8");
14446
+ bytes = fs11.readFileSync(file, "utf8");
14404
14447
  } catch {
14405
14448
  return null;
14406
14449
  }
@@ -14418,7 +14461,7 @@ class TaskStore {
14418
14461
  schemaVersion: SCHEMA_VERSION,
14419
14462
  id: workspaceId ?? newId(),
14420
14463
  revision: newRevision(),
14421
- root: path13.dirname(v2),
14464
+ root: path14.dirname(v2),
14422
14465
  runtime: { createdWith: version, updatedWith: version },
14423
14466
  writer: null,
14424
14467
  store: {
@@ -14429,12 +14472,12 @@ class TaskStore {
14429
14472
  },
14430
14473
  critical: ["store"]
14431
14474
  };
14432
- fs10.mkdirSync(v2, { recursive: true });
14433
- const ignore = path13.join(v2, ".gitignore");
14434
- if (!fs10.existsSync(ignore))
14435
- fs10.writeFileSync(ignore, `*
14475
+ fs11.mkdirSync(v2, { recursive: true });
14476
+ const ignore = path14.join(v2, ".gitignore");
14477
+ if (!fs11.existsSync(ignore))
14478
+ fs11.writeFileSync(ignore, `*
14436
14479
  `);
14437
- replaceFile(path13.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
14480
+ replaceFile(path14.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
14438
14481
  `, true);
14439
14482
  }
14440
14483
  stubFor(taskId) {
@@ -14450,7 +14493,7 @@ class TaskStore {
14450
14493
  }
14451
14494
  isStub(file) {
14452
14495
  try {
14453
- const value = JSON.parse(fs10.readFileSync(file, "utf8"));
14496
+ const value = JSON.parse(fs11.readFileSync(file, "utf8"));
14454
14497
  return isObject6(value) && isObject6(value.store) && value.store.format === "workit-store";
14455
14498
  } catch {
14456
14499
  return false;
@@ -14458,32 +14501,32 @@ class TaskStore {
14458
14501
  }
14459
14502
  writeTombstoneIfMissing() {
14460
14503
  try {
14461
- const v2 = path13.join(this.root, ".workit");
14462
- if (fs10.existsSync(path13.join(v2, "workspace.json")))
14504
+ const v2 = path14.join(this.root, ".workit");
14505
+ if (fs11.existsSync(path14.join(v2, "workspace.json")))
14463
14506
  return;
14464
14507
  let id = null;
14465
14508
  try {
14466
- id = JSON.parse(fs10.readFileSync(this.paths().workspace, "utf8")).id ?? null;
14509
+ id = JSON.parse(fs11.readFileSync(this.paths().workspace, "utf8")).id ?? null;
14467
14510
  } catch {}
14468
14511
  this.writeTombstone(v2, id);
14469
14512
  } catch {}
14470
14513
  }
14471
14514
  initializeStorage() {
14472
14515
  const dirs = this.paths();
14473
- retryTransient(() => fs10.mkdirSync(dirs.checkout, { recursive: true }));
14474
- retryTransient(() => fs10.mkdirSync(dirs.tasks, { recursive: true }));
14475
- if (!fs10.existsSync(dirs.marker))
14516
+ retryTransient(() => fs11.mkdirSync(dirs.checkout, { recursive: true }));
14517
+ retryTransient(() => fs11.mkdirSync(dirs.tasks, { recursive: true }));
14518
+ if (!fs11.existsSync(dirs.marker))
14476
14519
  replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
14477
14520
  `, false);
14478
14521
  if (!dirs.shared) {
14479
- const ignore = path13.join(dirs.dir, ".gitignore");
14522
+ const ignore = path14.join(dirs.dir, ".gitignore");
14480
14523
  let current = null;
14481
14524
  try {
14482
- current = fs10.readFileSync(ignore, "utf8");
14525
+ current = fs11.readFileSync(ignore, "utf8");
14483
14526
  } catch {}
14484
14527
  if (current !== `*
14485
14528
  `)
14486
- retryTransient(() => fs10.writeFileSync(ignore, `*
14529
+ retryTransient(() => fs11.writeFileSync(ignore, `*
14487
14530
  `));
14488
14531
  }
14489
14532
  this.writeTombstoneIfMissing();
@@ -14499,7 +14542,7 @@ class TaskStore {
14499
14542
  shouldReclaim: ({ payload, nowMs }) => {
14500
14543
  let ageMs = null;
14501
14544
  try {
14502
- ageMs = nowMs - fs10.lstatSync(lockPath).mtimeMs;
14545
+ ageMs = nowMs - fs11.lstatSync(lockPath).mtimeMs;
14503
14546
  } catch {}
14504
14547
  return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
14505
14548
  },
@@ -14536,7 +14579,7 @@ class TaskStore {
14536
14579
  withLock(lockPath, operation) {
14537
14580
  try {
14538
14581
  this.initializeStorage();
14539
- fs10.mkdirSync(path13.dirname(lockPath), { recursive: true });
14582
+ fs11.mkdirSync(path14.dirname(lockPath), { recursive: true });
14540
14583
  } catch (error) {
14541
14584
  return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
14542
14585
  path: this.paths().dir
@@ -14576,18 +14619,18 @@ class TaskStore {
14576
14619
  lockDirWritable(lockPath) {
14577
14620
  const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
14578
14621
  try {
14579
- fs10.closeSync(fs10.openSync(probe, "wx", 384));
14622
+ fs11.closeSync(fs11.openSync(probe, "wx", 384));
14580
14623
  } catch {
14581
14624
  return false;
14582
14625
  }
14583
14626
  try {
14584
- fs10.rmSync(probe, { force: true });
14627
+ fs11.rmSync(probe, { force: true });
14585
14628
  } catch {}
14586
14629
  return true;
14587
14630
  }
14588
14631
  lockHolderPresent(lockPath) {
14589
14632
  try {
14590
- fs10.lstatSync(lockPath);
14633
+ fs11.lstatSync(lockPath);
14591
14634
  return true;
14592
14635
  } catch (error) {
14593
14636
  return error?.code !== "ENOENT";
@@ -14599,12 +14642,12 @@ class TaskStore {
14599
14642
  if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
14600
14643
  return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
14601
14644
  path: lockPath,
14602
- guidance: `Check permissions and the read-only attribute on ${path13.dirname(lockPath)}.`
14645
+ guidance: `Check permissions and the read-only attribute on ${path14.dirname(lockPath)}.`
14603
14646
  });
14604
14647
  if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
14605
14648
  let lock = null;
14606
14649
  try {
14607
- lock = parseMetadataLockOrNull(fs10.readFileSync(lockPath, "utf8"));
14650
+ lock = parseMetadataLockOrNull(fs11.readFileSync(lockPath, "utf8"));
14608
14651
  } catch {}
14609
14652
  return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
14610
14653
  outcome: "not_started",
@@ -14623,15 +14666,15 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
14623
14666
  var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
14624
14667
 
14625
14668
  // packages/workit-core/src/core/task-evaluation.ts
14626
- import { createHash as createHash9 } from "node:crypto";
14627
- import * as fs12 from "node:fs";
14669
+ import { createHash as createHash10 } from "node:crypto";
14670
+ import * as fs13 from "node:fs";
14628
14671
  import { spawnSync as spawnSync10 } from "node:child_process";
14629
- import path15 from "node:path";
14672
+ import path16 from "node:path";
14630
14673
 
14631
14674
  // packages/workit-core/src/check-config.ts
14632
14675
  import { spawnSync as spawnSync9 } from "node:child_process";
14633
- import fs11 from "node:fs";
14634
- import path14 from "node:path";
14676
+ import fs12 from "node:fs";
14677
+ import path15 from "node:path";
14635
14678
  var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
14636
14679
  var CHECKS_FILE = "workit.checks.json";
14637
14680
  var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
@@ -14684,10 +14727,10 @@ var git4 = (cwd, args) => {
14684
14727
  return value || null;
14685
14728
  };
14686
14729
  function checkRoot(cwd) {
14687
- return path14.resolve(git4(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
14730
+ return path15.resolve(git4(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
14688
14731
  }
14689
14732
  var packageManager = (root) => {
14690
- const has = (file) => fs11.existsSync(path14.join(root, file));
14733
+ const has = (file) => fs12.existsSync(path15.join(root, file));
14691
14734
  if (has("bun.lock") || has("bun.lockb"))
14692
14735
  return "bun";
14693
14736
  if (has("pnpm-lock.yaml"))
@@ -14707,7 +14750,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
14707
14750
  function detectedChecks(root) {
14708
14751
  const read = (file) => {
14709
14752
  try {
14710
- return fs11.readFileSync(path14.join(root, file), "utf8");
14753
+ return fs12.readFileSync(path15.join(root, file), "utf8");
14711
14754
  } catch {
14712
14755
  return null;
14713
14756
  }
@@ -14746,7 +14789,7 @@ function detectedChecks(root) {
14746
14789
  var GATES = ["testing", "verification"];
14747
14790
  function loadCheckConfig(cwd) {
14748
14791
  const root = checkRoot(cwd);
14749
- const file = path14.join(root, CHECKS_FILE);
14792
+ const file = path15.join(root, CHECKS_FILE);
14750
14793
  const none = (error = null) => ({
14751
14794
  source: error ? CHECKS_FILE : "none",
14752
14795
  root,
@@ -14756,7 +14799,7 @@ function loadCheckConfig(cwd) {
14756
14799
  });
14757
14800
  let text = null;
14758
14801
  try {
14759
- text = fs11.readFileSync(file, "utf8");
14802
+ text = fs12.readFileSync(file, "utf8");
14760
14803
  } catch (error) {
14761
14804
  if (error.code !== "ENOENT")
14762
14805
  return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
@@ -14819,10 +14862,10 @@ function workitAvailable(env = process.env) {
14819
14862
  return workitOnPath;
14820
14863
  const windows = process.platform === "win32";
14821
14864
  const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
14822
- const dirs = (env.PATH ?? env.Path ?? "").split(path14.delimiter).filter(Boolean);
14865
+ const dirs = (env.PATH ?? env.Path ?? "").split(path15.delimiter).filter(Boolean);
14823
14866
  workitOnPath = dirs.some((dir) => exts.some((ext) => {
14824
14867
  try {
14825
- return fs11.statSync(path14.join(dir, `workit${ext}`)).isFile();
14868
+ return fs12.statSync(path15.join(dir, `workit${ext}`)).isFile();
14826
14869
  } catch {
14827
14870
  return false;
14828
14871
  }
@@ -14834,15 +14877,15 @@ function checkCommand(args, version, onPath = workitAvailable()) {
14834
14877
  }
14835
14878
 
14836
14879
  // packages/workit-core/src/core/task-evaluation.ts
14837
- var digestBytes = (value) => createHash9("sha256").update(value).digest("hex");
14838
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path15.sep}`);
14839
- var relative = (root, candidate) => path15.relative(root, candidate).split(path15.sep).join("/") || ".";
14880
+ var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
14881
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`);
14882
+ var relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".";
14840
14883
  var canonicalPath = (value) => {
14841
14884
  if (typeof value !== "string" || !value)
14842
14885
  return null;
14843
14886
  if (value.includes("\\") || value.split("/").includes(".."))
14844
14887
  return null;
14845
- const normalized = path15.posix.normalize(value);
14888
+ const normalized = path16.posix.normalize(value);
14846
14889
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
14847
14890
  return null;
14848
14891
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -14890,21 +14933,21 @@ var gitPaths = (root) => {
14890
14933
  git: !/not a git repository/i.test(stderr)
14891
14934
  };
14892
14935
  }
14893
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path15.sep).join("/")) : [];
14936
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
14894
14937
  const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
14895
14938
  if (stagedDeleted.status !== 0) {
14896
14939
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
14897
14940
  if (!/not a git repository/i.test(stderr))
14898
14941
  return { paths, uncertain: true, git: true };
14899
14942
  } else if (stagedDeleted.stdout) {
14900
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path15.sep).join("/")));
14943
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
14901
14944
  }
14902
14945
  return { paths: [...new Set(paths)], uncertain: false, git: true };
14903
14946
  };
14904
14947
  var walk = (root, directory, output) => {
14905
14948
  let names;
14906
14949
  try {
14907
- names = fs12.readdirSync(directory);
14950
+ names = fs13.readdirSync(directory);
14908
14951
  } catch {
14909
14952
  return true;
14910
14953
  }
@@ -14912,10 +14955,10 @@ var walk = (root, directory, output) => {
14912
14955
  for (const name of names) {
14913
14956
  if (name === ".git" || name === ".workit")
14914
14957
  continue;
14915
- const target = path15.join(directory, name);
14958
+ const target = path16.join(directory, name);
14916
14959
  let stat;
14917
14960
  try {
14918
- stat = fs12.lstatSync(target);
14961
+ stat = fs13.lstatSync(target);
14919
14962
  } catch {
14920
14963
  uncertain = true;
14921
14964
  continue;
@@ -14930,14 +14973,14 @@ var walk = (root, directory, output) => {
14930
14973
  var scopeRoots = (root, scope) => {
14931
14974
  const roots = [];
14932
14975
  for (const item of scope.paths.length ? scope.paths : ["."]) {
14933
- const target = path15.resolve(root, item);
14976
+ const target = path16.resolve(root, item);
14934
14977
  if (!inside(root, target))
14935
14978
  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` }] });
14936
14979
  let ancestor = target;
14937
- while (!fs12.existsSync(ancestor) && ancestor !== root)
14938
- ancestor = path15.dirname(ancestor);
14980
+ while (!fs13.existsSync(ancestor) && ancestor !== root)
14981
+ ancestor = path16.dirname(ancestor);
14939
14982
  try {
14940
- if (!inside(root, fs12.realpathSync(ancestor)))
14983
+ if (!inside(root, fs13.realpathSync(ancestor)))
14941
14984
  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` }] });
14942
14985
  } catch {
14943
14986
  return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
@@ -14949,7 +14992,7 @@ var scopeRoots = (root, scope) => {
14949
14992
  function captureCandidate(root, scope, environment = []) {
14950
14993
  let checkout;
14951
14994
  try {
14952
- checkout = fs12.realpathSync(root);
14995
+ checkout = fs13.realpathSync(root);
14953
14996
  } catch {
14954
14997
  return failure2("invalid_input", "candidate root does not exist");
14955
14998
  }
@@ -14967,11 +15010,11 @@ function captureCandidate(root, scope, environment = []) {
14967
15010
  if (item === ".git" || item === ".workit")
14968
15011
  continue;
14969
15012
  names.add(item);
14970
- if (!fs12.existsSync(target))
15013
+ if (!fs13.existsSync(target))
14971
15014
  continue;
14972
15015
  let stat;
14973
15016
  try {
14974
- stat = fs12.lstatSync(target);
15017
+ stat = fs13.lstatSync(target);
14975
15018
  } catch {
14976
15019
  uncertain = true;
14977
15020
  continue;
@@ -14987,17 +15030,17 @@ function captureCandidate(root, scope, environment = []) {
14987
15030
  for (const item of [...names].toSorted()) {
14988
15031
  if (!scopeMatches(item, normalizedScope))
14989
15032
  continue;
14990
- const target = path15.join(checkout, item);
15033
+ const target = path16.join(checkout, item);
14991
15034
  try {
14992
- const stat = fs12.lstatSync(target);
15035
+ const stat = fs13.lstatSync(target);
14993
15036
  if (stat.isSymbolicLink()) {
14994
- const link = fs12.readlinkSync(target);
15037
+ const link = fs13.readlinkSync(target);
14995
15038
  files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
14996
15039
  } else if (stat.isFile()) {
14997
15040
  files.push({
14998
15041
  path: item,
14999
15042
  kind: "file",
15000
- digest: digestBytes(fs12.readFileSync(target)),
15043
+ digest: digestBytes(fs13.readFileSync(target)),
15001
15044
  executable: (stat.mode & 73) !== 0
15002
15045
  });
15003
15046
  } else if (!stat.isDirectory()) {
@@ -15162,7 +15205,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
15162
15205
  var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15163
15206
  let root;
15164
15207
  try {
15165
- root = fs12.realpathSync(checkoutRoot);
15208
+ root = fs13.realpathSync(checkoutRoot);
15166
15209
  } catch {
15167
15210
  return failure2("permission_denied", "checkout root is unavailable");
15168
15211
  }
@@ -15171,23 +15214,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15171
15214
  continue;
15172
15215
  if (!reference.digest)
15173
15216
  return failure2("invalid_input", "document references require a byte digest");
15174
- const target = path15.resolve(root, reference.path);
15175
- if (target !== root && !target.startsWith(`${root}${path15.sep}`))
15217
+ const target = path16.resolve(root, reference.path);
15218
+ if (target !== root && !target.startsWith(`${root}${path16.sep}`))
15176
15219
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
15177
15220
  fields: [
15178
15221
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
15179
15222
  ]
15180
15223
  });
15181
15224
  try {
15182
- const below = path15.relative(root, target);
15225
+ const below = path16.relative(root, target);
15183
15226
  let current = root;
15184
- for (const segment of below.split(path15.sep)) {
15227
+ for (const segment of below.split(path16.sep)) {
15185
15228
  if (!segment)
15186
15229
  continue;
15187
- current = path15.join(current, segment);
15188
- const stat = fs12.lstatSync(current);
15189
- const real = fs12.realpathSync(current);
15190
- if (real !== root && !real.startsWith(`${root}${path15.sep}`))
15230
+ current = path16.join(current, segment);
15231
+ const stat = fs13.lstatSync(current);
15232
+ const real = fs13.realpathSync(current);
15233
+ if (real !== root && !real.startsWith(`${root}${path16.sep}`))
15191
15234
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
15192
15235
  fields: [
15193
15236
  {
@@ -15203,7 +15246,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15203
15246
  if (current !== target && !stat.isDirectory())
15204
15247
  return failure2("invalid_input", "document reference ancestor is not a directory");
15205
15248
  }
15206
- if (digestBytes(fs12.readFileSync(target)) !== reference.digest)
15249
+ if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
15207
15250
  return failure2("permission_denied", "decision document bytes have changed");
15208
15251
  } catch {
15209
15252
  return failure2("invalid_input", "decision document is unavailable");
@@ -15572,7 +15615,8 @@ Autonomy contract:
15572
15615
  satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
15573
15616
  - Author is not verifier: your own verdict is \`--self\`, shown as
15574
15617
  self-reviewed, never verified. High risk, \`verification: independent\` and
15575
- merge need a verifier with its own session (\`WORKIT_SESSION_ID=<lead>-v<n>\`).
15618
+ merge need a verifier with its own host-derived session (a subagent's
15619
+ \`<lead>:<child>\`, set by the host); never hand-mint one such as \`<lead>-v1\`.
15576
15620
  - A local-commit endpoint does not imply PR readiness, and a local commit alone
15577
15621
  is not evidence of a requested remote push. Before reporting delivery,
15578
15622
  reconcile every requested item and observe it (\`workit verify-delivery\`).
@@ -17400,9 +17444,9 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
17400
17444
  };
17401
17445
  });
17402
17446
  // packages/workit-core/src/hooks/context.ts
17403
- import { createHash as createHash10 } from "node:crypto";
17404
- import fs13 from "node:fs";
17405
- import path16 from "node:path";
17447
+ import { createHash as createHash11 } from "node:crypto";
17448
+ import fs14 from "node:fs";
17449
+ import path17 from "node:path";
17406
17450
  var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
17407
17451
  var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
17408
17452
  var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
@@ -17487,7 +17531,7 @@ function entryCompactContext(store, entry, session, context) {
17487
17531
  return null;
17488
17532
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
17489
17533
  const observed = task?.data.evidence.some(isObservedCheck) ?? false;
17490
- const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path16.resolve(store.root, ref.path)] : []));
17534
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path17.resolve(store.root, ref.path)] : []));
17491
17535
  const observedFiles = filesKey(files);
17492
17536
  const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
17493
17537
  if (!compact.ok || !compact.data)
@@ -17556,7 +17600,7 @@ var turnContextText = (input, descriptor) => {
17556
17600
  }
17557
17601
  };
17558
17602
  var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
17559
- var digestOf = (text) => createHash10("sha256").update(text).digest("hex");
17603
+ var digestOf = (text) => createHash11("sha256").update(text).digest("hex");
17560
17604
  var turnMarker = (input) => {
17561
17605
  if (!input.session.id)
17562
17606
  return null;
@@ -17565,32 +17609,32 @@ var turnMarker = (input) => {
17565
17609
  if (location instanceof Error)
17566
17610
  return null;
17567
17611
  const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
17568
- return path16.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
17612
+ return path17.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
17569
17613
  } catch {
17570
17614
  return null;
17571
17615
  }
17572
17616
  };
17573
17617
  var writeTurnDigest = (file, digest) => {
17574
17618
  try {
17575
- fs13.mkdirSync(path16.dirname(file), { recursive: true });
17576
- fs13.writeFileSync(file, `${JSON.stringify({ digest })}
17619
+ fs14.mkdirSync(path17.dirname(file), { recursive: true });
17620
+ fs14.writeFileSync(file, `${JSON.stringify({ digest })}
17577
17621
  `);
17578
17622
  } catch {}
17579
17623
  };
17580
17624
  var clearTurnDigest = (file) => {
17581
17625
  try {
17582
- fs13.rmSync(file, { force: true });
17626
+ fs14.rmSync(file, { force: true });
17583
17627
  } catch {}
17584
17628
  };
17585
17629
  var pruneTurnDigests = (dir, now) => {
17586
17630
  try {
17587
- for (const name of fs13.readdirSync(dir)) {
17631
+ for (const name of fs14.readdirSync(dir)) {
17588
17632
  if (!name.startsWith("turn-"))
17589
17633
  continue;
17590
- const file = path16.join(dir, name);
17634
+ const file = path17.join(dir, name);
17591
17635
  try {
17592
- if (now - fs13.statSync(file).mtimeMs > TURN_TTL_MS)
17593
- fs13.rmSync(file, { force: true });
17636
+ if (now - fs14.statSync(file).mtimeMs > TURN_TTL_MS)
17637
+ fs14.rmSync(file, { force: true });
17594
17638
  } catch {}
17595
17639
  }
17596
17640
  } catch {}
@@ -17606,7 +17650,7 @@ var changedTurnContext = (input, text) => {
17606
17650
  return text;
17607
17651
  const digest = digestOf(text);
17608
17652
  try {
17609
- const previous = JSON.parse(fs13.readFileSync(file, "utf8"));
17653
+ const previous = JSON.parse(fs14.readFileSync(file, "utf8"));
17610
17654
  if (previous.digest === digest)
17611
17655
  return null;
17612
17656
  } catch {}
@@ -17617,14 +17661,26 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
17617
17661
  const file = turnMarker(input);
17618
17662
  if (!file)
17619
17663
  return;
17620
- pruneTurnDigests(path16.dirname(file), Date.now());
17664
+ pruneTurnDigests(path17.dirname(file), Date.now());
17621
17665
  if (text)
17622
17666
  writeTurnDigest(file, digestOf(text));
17623
17667
  else
17624
17668
  clearTurnDigest(file);
17625
17669
  };
17670
+ // packages/workit-core/src/core/route-intent.ts
17671
+ import { spawnSync as spawnSync12 } from "node:child_process";
17672
+ import { existsSync as existsSync6 } from "node:fs";
17673
+ import path20 from "node:path";
17674
+
17675
+ // packages/workit-core/src/hooks/raw-git.ts
17676
+ import { spawnSync as spawnSync11 } from "node:child_process";
17677
+ import { createHash as createHash14 } from "node:crypto";
17678
+ import fs16 from "node:fs";
17679
+ import os4 from "node:os";
17680
+ import path19 from "node:path";
17681
+
17626
17682
  // packages/workit-core/src/core/branch.ts
17627
- import { createHash as createHash11 } from "node:crypto";
17683
+ import { createHash as createHash12 } from "node:crypto";
17628
17684
  var withReleaseTracks = (workspaceRoot, policy) => {
17629
17685
  const read = workspaceReleaseTracks(workspaceRoot);
17630
17686
  if (read.error)
@@ -17660,10 +17716,72 @@ function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
17660
17716
  provenance: { commitPolicy: selected.source }
17661
17717
  };
17662
17718
  }
17719
+ var KIND_SYNONYMS = {
17720
+ fix: "bugfix",
17721
+ fixes: "bugfix",
17722
+ bug: "bugfix",
17723
+ bugs: "bugfix",
17724
+ bugfixes: "bugfix",
17725
+ feat: "feature",
17726
+ features: "feature",
17727
+ chore: "feature",
17728
+ docs: "feature",
17729
+ doc: "feature",
17730
+ refactor: "feature",
17731
+ test: "feature",
17732
+ tests: "feature",
17733
+ ci: "feature",
17734
+ build: "feature",
17735
+ perf: "feature",
17736
+ style: "feature",
17737
+ deps: "feature",
17738
+ task: "feature",
17739
+ enhancement: "feature",
17740
+ hot: "hotfix",
17741
+ hotfixes: "hotfix",
17742
+ releases: "release",
17743
+ rel: "release"
17744
+ };
17745
+ var allowedKinds = (allowed) => [
17746
+ ...new Set(allowed.map((pattern) => /^\^([\w.-]+)\\?\/\.\*\$$/u.exec(pattern.source)?.[1]?.toLowerCase()).filter((kind) => Boolean(kind)))
17747
+ ];
17748
+ var editDistance = (a, b) => {
17749
+ const row = Array.from({ length: b.length + 1 }, (_, index) => index);
17750
+ for (let i = 1;i <= a.length; i++) {
17751
+ let previous = row[0];
17752
+ row[0] = i;
17753
+ for (let j = 1;j <= b.length; j++) {
17754
+ const saved = row[j];
17755
+ row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1));
17756
+ previous = saved;
17757
+ }
17758
+ }
17759
+ return row[b.length];
17760
+ };
17761
+ var suggestBranchName = (name, kinds) => {
17762
+ if (!kinds.length)
17763
+ return null;
17764
+ const slash = name.indexOf("/");
17765
+ const prefix = slash > 0 ? name.slice(0, slash).toLowerCase() : "";
17766
+ const rest = (slash > 0 ? name.slice(slash + 1) : name) || "name";
17767
+ const synonym = KIND_SYNONYMS[prefix];
17768
+ const near = kinds.map((kind) => ({ kind, distance: editDistance(prefix, kind) })).filter((entry) => prefix && entry.distance <= 2).toSorted((a, b) => a.distance - b.distance)[0]?.kind;
17769
+ const kind = (synonym && kinds.includes(synonym) ? synonym : null) ?? near ?? (kinds.includes("feature") ? "feature" : kinds[0]);
17770
+ return `${kind}/${rest}`;
17771
+ };
17772
+ var kindList = (kinds) => kinds.map((kind) => `${kind}/`).join(", ");
17773
+ var aliasNote = (kinds) => {
17774
+ const notes = [
17775
+ ["fix", "bugfix"],
17776
+ ["chore", "feature"]
17777
+ ].filter(([, kind]) => kinds.includes(kind)).map(([alias, kind]) => `${alias}/ is ${kind}/`);
17778
+ return notes.length ? ` (${notes.join(", ")})` : "";
17779
+ };
17663
17780
  var validateBranchNamePolicy = (name, policy, source) => {
17664
17781
  const allowed = policy.allowed.map((pattern) => `/${pattern.source}/${pattern.flags}`);
17782
+ const kinds = allowedKinds(policy.allowed);
17665
17783
  if (policy.protected.has(name.toLowerCase())) {
17666
- const correction = `choose a non-protected branch matching one of: ${allowed.join(", ")}`;
17784
+ const correction = kinds.length ? `choose a non-protected branch of an allowed kind (${kindList(kinds)}), e.g. ${kinds[0]}/<slug>: workit git branch --kind ${kinds[0]} --slug <slug>` : `choose a non-protected branch matching one of: ${allowed.join(", ")}`;
17667
17785
  return {
17668
17786
  ok: false,
17669
17787
  rule: "protected_ref",
@@ -17674,7 +17792,8 @@ var validateBranchNamePolicy = (name, policy, source) => {
17674
17792
  };
17675
17793
  }
17676
17794
  if (!policy.allowed.some((pattern) => pattern.test(name))) {
17677
- const correction = `choose a branch matching one of: ${allowed.join(", ")}`;
17795
+ const suggestion = suggestBranchName(name, kinds);
17796
+ const correction = suggestion && policy.allowed.some((pattern) => pattern.test(suggestion)) ? `use ${JSON.stringify(suggestion)}; the allowed kinds are ${kindList(kinds)} (workit git branch ${suggestion})` : `choose a branch matching one of: ${allowed.join(", ")}`;
17678
17797
  return {
17679
17798
  ok: false,
17680
17799
  rule: "allowed_pattern",
@@ -17692,7 +17811,7 @@ var branchPolicySnapshotFor = (workspaceRoot) => {
17692
17811
  const source = resolved.provenance.branchPolicy;
17693
17812
  const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
17694
17813
  const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
17695
- const fingerprint = createHash11("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
17814
+ const fingerprint = createHash12("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
17696
17815
  return {
17697
17816
  branchPolicy,
17698
17817
  namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
@@ -17717,31 +17836,87 @@ var validateBranchNameFor = (workspaceRoot, name) => {
17717
17836
  }
17718
17837
  };
17719
17838
  var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
17720
-
17721
- // packages/workit-core/src/core/route-intent.ts
17722
- var shellBranchTarget = (command) => {
17723
- if (!command.trim() || /[\r\n;&|<>`'"$()\\*?{}~!#]/u.test(command) || command.includes("[") || command.includes("]"))
17724
- return null;
17725
- const args = command.trim().split(/\s+/u);
17726
- if (args[0] !== "git")
17839
+ var gitFlowOf = (root, integration) => {
17840
+ try {
17841
+ if (resolveBranchPolicyFor(root).preset !== "gitflow")
17842
+ return null;
17843
+ const selected = resolveRuntimeWorkspacePolicy(root, "branch", process.env.WORKFLOW_PROFILE?.trim() || undefined).policy;
17844
+ const names = branchNames(root);
17845
+ const production = productionBranchOf(names);
17846
+ const develop = selected?.developBranch?.trim() || (integration && integration.toLowerCase() !== production ? integration : "develop");
17847
+ return { develop, production, developExists: names.has(develop) };
17848
+ } catch {
17727
17849
  return null;
17728
- const option = args[2];
17729
- const target = args[1] === "switch" && ["-c", "--create", "-C", "--force-create"].includes(option ?? "") && (args.length === 4 || args.length === 5) ? args[3] : args[1] === "checkout" && ["-b", "-B"].includes(option ?? "") && (args.length === 4 || args.length === 5) ? args[3] : args[1] === "branch" && (args.length === 3 || args.length === 4) ? args[2] : null;
17730
- return target && !target.startsWith("-") ? target : null;
17850
+ }
17851
+ };
17852
+ var releaseBranchesOf = (cwd) => {
17853
+ try {
17854
+ const resolved = resolveBranchPolicyFor(cwd);
17855
+ const flow = resolved.preset === "gitflow" ? gitFlowOf(cwd) : null;
17856
+ return {
17857
+ preset: resolved.preset,
17858
+ branches: flow ? [flow.production, flow.develop] : [],
17859
+ production: flow ? flow.production : resolved.defaultTargetBranch
17860
+ };
17861
+ } catch {
17862
+ return { preset: "unknown", branches: [], production: null };
17863
+ }
17731
17864
  };
17732
- var shellBranchPolicyViolation = (root, command) => {
17733
- const target = shellBranchTarget(command);
17734
- return target === null ? null : validateBranchNameFor(root, target);
17865
+ var releaseBranch = (cwd, branch) => releaseBranchesOf(cwd).branches.some((name) => name.toLowerCase() === branch.toLowerCase());
17866
+ var branchGuidance = (cwd) => {
17867
+ try {
17868
+ const patterns = branchPolicySnapshotFor(cwd).namePolicy.allowed;
17869
+ const kinds = allowedKinds(patterns);
17870
+ const kindShaped = new Set(kinds.map((kind) => branchGlobPattern(`${kind}/*`).source));
17871
+ const anyName = patterns.some((pattern) => pattern.source === "^.*$");
17872
+ const others = patterns.filter((pattern) => !kindShaped.has(pattern.source)).map((pattern) => globOf(pattern));
17873
+ const allowed = anyName ? "Any non-protected branch name is allowed here." : `${kinds.length ? `Allowed branch kinds here: ${kindList(kinds)}${aliasNote(kinds)}` : "Allowed branch names here:"}${others.length ? `${kinds.length ? "; also" : ""} names matching ${others.join(", ")}` : ""}; anything else is denied.`;
17874
+ const resolved = vcsConfig("resolve", cwd);
17875
+ const integration = resolved.ok === false ? null : String(resolved.defaultTargetBranch ?? "") || null;
17876
+ const flow = resolved.releaseTrack?.track ? null : gitFlowOf(cwd, integration);
17877
+ if (flow?.developExists)
17878
+ return `${allowed} git-flow: feature/, bugfix/ and release/ start from origin/${flow.develop}, hotfix/ from origin/${flow.production}; feature/ and bugfix/ land on ${flow.develop}, release/ and hotfix/ on ${flow.production}.`;
17879
+ const names = branchNames(cwd);
17880
+ const configured = String(resolved.baseBranch ?? integration ?? "") || null;
17881
+ const trunk = productionBranchOf(names);
17882
+ const base = configured && names.has(configured) ? `Branch from origin/${configured}.` : names.has(trunk) ? `Branch from origin/${trunk}${configured ? ` (the default target ${configured} does not exist here)` : ""}.` : "No base branch exists here yet; pass the base explicitly.";
17883
+ return `${allowed} ${base}`;
17884
+ } catch {
17885
+ return null;
17886
+ }
17735
17887
  };
17888
+ var globOf = (pattern) => pattern.source.replace(/^\^|\$$/gu, "").replaceAll(".*", "*").replace(/\\(.)/gu, "$1");
17736
17889
 
17737
- // packages/workit-core/src/hooks/policy.ts
17738
- var shellPolicy = (cwd, command) => {
17739
- const policy = shellBranchPolicyViolation(cwd, command);
17740
- return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
17890
+ // packages/workit-core/src/hooks/session-marker.ts
17891
+ import { createHash as createHash13 } from "node:crypto";
17892
+ import fs15 from "node:fs";
17893
+ import path18 from "node:path";
17894
+ var sessionMarkerFile = (input, session, store) => {
17895
+ let dir = store;
17896
+ if (dir === undefined) {
17897
+ const location = resolveStore(input.cwd);
17898
+ if (location instanceof Error || !location.shared)
17899
+ return null;
17900
+ dir = location.dir;
17901
+ }
17902
+ const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
17903
+ return path18.join(dir, "hooks", `skills-${name}.json`);
17904
+ };
17905
+ var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
17906
+ var readSessionMarker = (file) => {
17907
+ try {
17908
+ const value = JSON.parse(fs15.readFileSync(file, "utf8"));
17909
+ return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
17910
+ } catch {
17911
+ return { nudged: [], loaded: [], raw: [] };
17912
+ }
17913
+ };
17914
+ var writeSessionMarker = (file, marker) => {
17915
+ try {
17916
+ fs15.mkdirSync(path18.dirname(file), { recursive: true });
17917
+ fs15.writeFileSync(file, JSON.stringify(marker));
17918
+ } catch {}
17741
17919
  };
17742
- // packages/workit-core/src/hooks/write-gate.ts
17743
- import { realpathSync as realpathSync5 } from "node:fs";
17744
- import path17 from "node:path";
17745
17920
 
17746
17921
  // packages/workit-core/src/hooks/shell-words.ts
17747
17922
  var closingQuote = (command, start, dialect) => {
@@ -17866,252 +18041,38 @@ var segmentsOf = (command, dialect = "posix") => {
17866
18041
  return segments;
17867
18042
  };
17868
18043
 
17869
- // packages/workit-core/src/hooks/write-gate.ts
18044
+ // packages/workit-core/src/hooks/raw-git.ts
17870
18045
  var NONE2 = { kind: "none" };
17871
- var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
17872
- var canonical2 = (file) => {
17873
- let rest = "";
17874
- for (let dir = path17.resolve(file);; dir = path17.dirname(dir)) {
17875
- try {
17876
- return path17.join(realpathSync5(dir), rest);
17877
- } catch {
17878
- if (path17.dirname(dir) === dir)
17879
- return path17.resolve(file);
17880
- rest = rest ? path17.join(path17.basename(dir), rest) : path17.basename(dir);
17881
- }
17882
- }
18046
+ var SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
18047
+ var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
18048
+ var SCRIPT_FLAG = /^-[a-zA-Z]*c[a-zA-Z]*$/;
18049
+ var CD = new Set(["cd", "chdir", "set-location", "sl"]);
18050
+ var PUSHD = new Set(["pushd", "push-location"]);
18051
+ var POPD = new Set(["popd", "pop-location"]);
18052
+ var GIT_VALUE_OPTIONS = new Set([
18053
+ "-c",
18054
+ "--git-dir",
18055
+ "--work-tree",
18056
+ "--namespace",
18057
+ "--exec-path",
18058
+ "--config-env",
18059
+ "--super-prefix"
18060
+ ]);
18061
+ var isAbsolute = (value) => path19.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
18062
+ var joinDir = (base, next) => {
18063
+ if (base === undefined || /[$`*?]/.test(next) || next === "-")
18064
+ return;
18065
+ const expanded = next === "~" || next.startsWith("~/") ? path19.join(os4.homedir(), next.slice(1)) : next;
18066
+ return base === null || isAbsolute(expanded) ? expanded : path19.join(base, expanded);
17883
18067
  };
17884
- var exempt = (cwd, root, file, plans) => {
17885
- const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
17886
- const relative = path17.relative(fold(canonical2(root)), fold(canonical2(path17.resolve(cwd, file))));
17887
- if (relative.startsWith("..") || path17.isAbsolute(relative))
17888
- return true;
17889
- const posix = relative.split(path17.sep).join("/");
17890
- const segments = posix.split("/");
17891
- return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
18068
+ var cdTarget = (words) => {
18069
+ const args = words.slice(1);
18070
+ const flag = args.findIndex((word) => /^-(?:literal)?path$/i.test(word));
18071
+ if (flag >= 0)
18072
+ return args[flag + 1] ?? null;
18073
+ return args.find((word) => word === "-" || !word.startsWith("-")) ?? null;
17892
18074
  };
17893
- var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
17894
- var operands = (words) => words.filter((item) => !item.startsWith("-"));
17895
- var editsOf = (input) => {
17896
- let words = input;
17897
- while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
17898
- words = words.slice(1);
17899
- const [verb, ...args] = words;
17900
- const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
17901
- if (!name || name === "workit" || name === "npx" && args.some((item) => /workit/.test(item)))
17902
- return [];
17903
- switch (name) {
17904
- case "touch":
17905
- case "mkdir":
17906
- case "rm":
17907
- case "rmdir":
17908
- case "tee":
17909
- case "mv":
17910
- case "truncate":
17911
- return operands(args);
17912
- case "cp":
17913
- case "ln":
17914
- case "install": {
17915
- const target = args.indexOf("-t");
17916
- if (target >= 0)
17917
- return args[target + 1] ? [args[target + 1]] : null;
17918
- const last = operands(args).at(-1);
17919
- return last ? [last] : null;
17920
- }
17921
- case "dd":
17922
- return args.filter((item) => item.startsWith("of=")).map((item) => item.slice(3));
17923
- case "sed":
17924
- case "perl": {
17925
- if (!args.some((item) => /^-[a-zA-Z]*i|^--in-place/.test(item)))
17926
- return [];
17927
- const scripted = args.some((item) => item === "-e" || item === "-f");
17928
- const files = [];
17929
- for (let index = 0;index < args.length; index++) {
17930
- if (args[index] === "-e" || args[index] === "-f")
17931
- index++;
17932
- else if (!args[index].startsWith("-"))
17933
- files.push(args[index]);
17934
- }
17935
- return scripted ? files : files.slice(1);
17936
- }
17937
- case "patch":
17938
- return null;
17939
- case "set-content":
17940
- case "add-content":
17941
- case "out-file":
17942
- case "new-item":
17943
- case "ni":
17944
- case "remove-item":
17945
- case "del":
17946
- case "copy-item":
17947
- case "move-item": {
17948
- const flag = args.findIndex((item) => /^-(?:path|filepath|literalpath|destination)$/i.test(item));
17949
- if (flag >= 0)
17950
- return args[flag + 1] ? [args[flag + 1]] : null;
17951
- const files = operands(args);
17952
- if (name === "move-item")
17953
- return files.length ? files : null;
17954
- const file = name === "copy-item" ? files.at(-1) : files[0];
17955
- return file ? [file] : null;
17956
- }
17957
- case "git": {
17958
- const sub = operands(args)[0];
17959
- if (sub === "apply")
17960
- return null;
17961
- if (sub === "restore" || sub === "mv" || sub === "rm")
17962
- return operands(args).slice(1);
17963
- if (sub === "checkout" && args.includes("--"))
17964
- return args.slice(args.indexOf("--") + 1);
17965
- return [];
17966
- }
17967
- default:
17968
- return [];
17969
- }
17970
- };
17971
- var shellWrites = (command) => {
17972
- const targets = [];
17973
- let unknown = false;
17974
- for (const segment of segmentsOf(command)) {
17975
- targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
17976
- const edits = editsOf(segment.words);
17977
- if (edits === null)
17978
- unknown = true;
17979
- else
17980
- targets.push(...edits);
17981
- }
17982
- if (!unknown && targets.length === 0)
17983
- return { writes: false, targets: null };
17984
- return { writes: true, targets: unknown ? null : targets };
17985
- };
17986
- var cache2 = new Map;
17987
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path17.resolve(root, file)) ?? "-").join("\x00");
17988
- var gateFor = (cwd) => {
17989
- const store = new TaskStore(cwd);
17990
- const listed = store.listTaskIndex();
17991
- if (!listed.ok)
17992
- return null;
17993
- const key = store.currentKey();
17994
- if (!key.ok)
17995
- return null;
17996
- const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
17997
- if (!entry)
17998
- return { blockers: [], root: store.root, plans: new Set };
17999
- const ledger = ledgerPath(store.root);
18000
- const cacheKey = `${entry.id}\x00${entry.revision}\x00${ledger.ok && fileSignature(ledger.value) || "-"}`;
18001
- const hit = cache2.get(store.root);
18002
- if (hit && hit.key === cacheKey && hit.planKey === planKeyOf(store.root, hit.plans))
18003
- return hit;
18004
- const task = store.readTask(entry.id);
18005
- if (!task.ok)
18006
- return null;
18007
- const workspace = store.readWorkspace();
18008
- const plans = new Set((latestJudgment(task.data)?.refs ?? []).flatMap((ref) => ref.kind === "file" ? [ref.path] : []));
18009
- const blockers = writeBlockers(task.data, store.root).filter((blocker) => {
18010
- const requirement = task.data.policy?.requirements.find((item) => item.ruleId === blocker.ruleId && item.before === "write");
18011
- return !(requirement?.acceptanceAllowed && workspace.ok && workspace.data && applicableDecision(task.data, workspace.data, requirement, store.root).length > 0);
18012
- });
18013
- const gate = {
18014
- blockers,
18015
- root: store.root,
18016
- plans,
18017
- key: cacheKey,
18018
- planKey: planKeyOf(store.root, plans)
18019
- };
18020
- cache2.set(store.root, gate);
18021
- if (cache2.size > 32)
18022
- cache2.delete(cache2.keys().next().value);
18023
- return gate;
18024
- };
18025
- function writeGate(cwd, paths) {
18026
- try {
18027
- const gate = gateFor(cwd);
18028
- if (!gate || gate.blockers.length === 0)
18029
- return NONE2;
18030
- if (paths?.length && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
18031
- return NONE2;
18032
- const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
18033
- return {
18034
- kind: "deny",
18035
- reason: `workit before-write gate: ${reasons}. Markdown, docs/, plans/ and files outside the checkout stay writable.`,
18036
- unblock: gate.blockers[0].reason
18037
- };
18038
- } catch {
18039
- return NONE2;
18040
- }
18041
- }
18042
- // packages/workit-core/src/hooks/stop.ts
18043
- import { spawnSync as spawnSync12 } from "node:child_process";
18044
-
18045
- // packages/workit-core/src/hooks/raw-git.ts
18046
- import { spawnSync as spawnSync11 } from "node:child_process";
18047
- import { createHash as createHash13 } from "node:crypto";
18048
- import fs15 from "node:fs";
18049
- import os4 from "node:os";
18050
- import path19 from "node:path";
18051
-
18052
- // packages/workit-core/src/hooks/session-marker.ts
18053
- import { createHash as createHash12 } from "node:crypto";
18054
- import fs14 from "node:fs";
18055
- import path18 from "node:path";
18056
- var sessionMarkerFile = (input, session, store) => {
18057
- let dir = store;
18058
- if (dir === undefined) {
18059
- const location = resolveStore(input.cwd);
18060
- if (location instanceof Error || !location.shared)
18061
- return null;
18062
- dir = location.dir;
18063
- }
18064
- const name = createHash12("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
18065
- return path18.join(dir, "hooks", `skills-${name}.json`);
18066
- };
18067
- var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
18068
- var readSessionMarker = (file) => {
18069
- try {
18070
- const value = JSON.parse(fs14.readFileSync(file, "utf8"));
18071
- return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
18072
- } catch {
18073
- return { nudged: [], loaded: [], raw: [] };
18074
- }
18075
- };
18076
- var writeSessionMarker = (file, marker) => {
18077
- try {
18078
- fs14.mkdirSync(path18.dirname(file), { recursive: true });
18079
- fs14.writeFileSync(file, JSON.stringify(marker));
18080
- } catch {}
18081
- };
18082
-
18083
- // packages/workit-core/src/hooks/raw-git.ts
18084
- var NONE3 = { kind: "none" };
18085
- var SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
18086
- var PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
18087
- var SCRIPT_FLAG = /^-[a-zA-Z]*c[a-zA-Z]*$/;
18088
- var CD = new Set(["cd", "chdir", "set-location", "sl"]);
18089
- var PUSHD = new Set(["pushd", "push-location"]);
18090
- var POPD = new Set(["popd", "pop-location"]);
18091
- var GIT_VALUE_OPTIONS = new Set([
18092
- "-c",
18093
- "--git-dir",
18094
- "--work-tree",
18095
- "--namespace",
18096
- "--exec-path",
18097
- "--config-env",
18098
- "--super-prefix"
18099
- ]);
18100
- var isAbsolute = (value) => path19.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
18101
- var joinDir = (base, next) => {
18102
- if (base === undefined || /[$`*?]/.test(next) || next === "-")
18103
- return;
18104
- const expanded = next === "~" || next.startsWith("~/") ? path19.join(os4.homedir(), next.slice(1)) : next;
18105
- return base === null || isAbsolute(expanded) ? expanded : path19.join(base, expanded);
18106
- };
18107
- var cdTarget = (words) => {
18108
- const args = words.slice(1);
18109
- const flag = args.findIndex((word) => /^-(?:literal)?path$/i.test(word));
18110
- if (flag >= 0)
18111
- return args[flag + 1] ?? null;
18112
- return args.find((word) => word === "-" || !word.startsWith("-")) ?? null;
18113
- };
18114
- var stripPrefixes = (input) => {
18075
+ var stripPrefixes = (input) => {
18115
18076
  let words = input;
18116
18077
  for (;; ) {
18117
18078
  const first = words[0];
@@ -18119,7 +18080,7 @@ var stripPrefixes = (input) => {
18119
18080
  return words;
18120
18081
  if (/^\w+=/.test(first))
18121
18082
  words = words.slice(1);
18122
- else if (PREFIXES3.has(first)) {
18083
+ else if (PREFIXES2.has(first)) {
18123
18084
  words = words.slice(1);
18124
18085
  while (words[0]?.startsWith("-"))
18125
18086
  words = words.slice(words[0] === "-u" ? 2 : 1);
@@ -18234,6 +18195,10 @@ var pushAction = (args) => {
18234
18195
  let force = false;
18235
18196
  let lease = false;
18236
18197
  let wholesale = false;
18198
+ let allTags = false;
18199
+ let deletes = false;
18200
+ let allBranches = false;
18201
+ let mirror = false;
18237
18202
  let remote = null;
18238
18203
  const positionals = [];
18239
18204
  for (let index = 1;index < args.length; index++) {
@@ -18254,14 +18219,22 @@ var pushAction = (args) => {
18254
18219
  force = true;
18255
18220
  else if (arg.startsWith("--force-with-lease") || arg === "--force-if-includes")
18256
18221
  lease = true;
18257
- else if (["--all", "--mirror", "--branches", "--tags"].includes(arg))
18258
- wholesale = true;
18222
+ else if (arg === "--delete" || arg === "-d")
18223
+ deletes = true;
18224
+ else if (arg === "--tags")
18225
+ allTags = wholesale = true;
18226
+ else if (arg === "--all" || arg === "--branches")
18227
+ allBranches = wholesale = true;
18228
+ else if (arg === "--mirror")
18229
+ mirror = wholesale = true;
18259
18230
  else if (!arg.startsWith("-"))
18260
18231
  positionals.push(arg);
18261
18232
  }
18262
18233
  const specs = remote === null ? positionals.slice(1) : positionals;
18263
18234
  remote ??= positionals[0] ?? null;
18264
18235
  const targets = [];
18236
+ const tags = [];
18237
+ const deleted = [];
18265
18238
  for (const raw of specs) {
18266
18239
  if (raw.startsWith("+"))
18267
18240
  force = true;
@@ -18270,16 +18243,29 @@ var pushAction = (args) => {
18270
18243
  const destination = colon >= 0 ? spec.slice(colon + 1) : spec;
18271
18244
  if (!destination)
18272
18245
  continue;
18273
- if (destination === "HEAD")
18274
- targets.push(null);
18275
- else if (destination.startsWith("refs/heads/"))
18276
- targets.push(destination.slice(11));
18277
- else if (!destination.startsWith("refs/"))
18278
- targets.push(destination);
18246
+ const name = destination === "HEAD" ? null : destination.startsWith("refs/heads/") ? destination.slice(11) : destination.startsWith("refs/") ? undefined : destination;
18247
+ if (destination.startsWith("refs/tags/"))
18248
+ tags.push(destination.slice(10));
18249
+ if (name === undefined)
18250
+ continue;
18251
+ targets.push(name);
18252
+ if (colon === 0 && name !== null)
18253
+ deleted.push(name);
18279
18254
  }
18280
18255
  if (specs.length === 0 && !wholesale)
18281
18256
  targets.push(null);
18282
- return { kind: "push", targets, blindForce: force && !lease, remote };
18257
+ return {
18258
+ kind: "push",
18259
+ targets,
18260
+ blindForce: force && !lease,
18261
+ remote,
18262
+ ...tags.length ? { tags } : {},
18263
+ ...allTags ? { allTags: true } : {},
18264
+ ...deletes ? { deletes: true } : {},
18265
+ ...deleted.length ? { deleted } : {},
18266
+ ...allBranches ? { allBranches: true } : {},
18267
+ ...mirror ? { mirror: true } : {}
18268
+ };
18283
18269
  };
18284
18270
  var VALUE_OPTIONS = {
18285
18271
  git: new Set([
@@ -18355,7 +18341,7 @@ var repoAt = (dir) => {
18355
18341
  return { location, dir, branch: resolveTaskKey(dir, location).branch };
18356
18342
  };
18357
18343
  var isWorkitWorkspace = (repo) => {
18358
- if (fs15.existsSync(repo.location.dir))
18344
+ if (fs16.existsSync(repo.location.dir))
18359
18345
  return true;
18360
18346
  try {
18361
18347
  return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
@@ -18386,7 +18372,7 @@ var headSha2 = (repo) => {
18386
18372
  if (!git || git.reftable)
18387
18373
  return null;
18388
18374
  try {
18389
- const head = fs15.readFileSync(path19.join(git.gitDir, "HEAD"), "utf8").trim();
18375
+ const head = fs16.readFileSync(path19.join(git.gitDir, "HEAD"), "utf8").trim();
18390
18376
  if (/^[0-9a-f]{40,64}$/.test(head))
18391
18377
  return head;
18392
18378
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
@@ -18394,20 +18380,72 @@ var headSha2 = (repo) => {
18394
18380
  return null;
18395
18381
  const common = path19.dirname(repo.location.dir);
18396
18382
  try {
18397
- return fs15.readFileSync(path19.join(common, ref), "utf8").trim() || null;
18383
+ return fs16.readFileSync(path19.join(common, ref), "utf8").trim() || null;
18398
18384
  } catch {}
18399
- const packed = fs15.readFileSync(path19.join(common, "packed-refs"), "utf8");
18385
+ const packed = fs16.readFileSync(path19.join(common, "packed-refs"), "utf8");
18400
18386
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
18401
18387
  } catch {
18402
18388
  return null;
18403
18389
  }
18404
18390
  };
18405
18391
  var quote = (text) => `\`${text}\``;
18406
- var refusal = (action, branch) => {
18392
+ var refusal = (action, branch, dir) => {
18407
18393
  if (action.kind === "merge")
18408
18394
  return `workit: raw ${quote(action.forge === "gh" ? "gh pr merge" : "glab mr merge")} bypasses the merge grant and the verdict gate. Run ${quote("workit pr merge")} instead (it checks both; ${quote("workit help pr")}).`;
18395
+ const plainPush = action.kind === "push" && !action.blindForce && !action.deletes;
18396
+ if (dir && branch && plainPush && releaseBranch(dir, branch))
18397
+ return `workit: raw ${quote("git push")} onto ${branch}, a protected release branch, bypasses workit's protected-branch check. To finish a release or hotfix, push it with ${quote(`workit git push --release ${branch} [--release v<version>]`)}: it pushes ${branch} (fast-forward only) and the release tag, verifies the remote and records it, under the ${quote("release")} grant (if refused, ask the user to run ${quote("workit grant set <workspace> release=true")}). Feature work goes through ${quote("workit git push")} and ${quote("workit pr merge")}.`;
18409
18398
  return `workit: raw ${quote("git push")}${action.kind === "push" && action.blindForce ? " --force" : ""} onto ${branch}, a protected branch or the default target, bypasses workit's protected-branch check. Push your feature branch with ${quote("workit git push")} (${quote("workit git push --force-with-lease")} to rewrite it) and land it with ${quote("workit pr merge")}.`;
18410
18399
  };
18400
+ var refFacts = (repo, remote) => {
18401
+ const facts = { heads: new Set, tags: new Set, remoteHeads: new Set };
18402
+ const listed = spawnSync11("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/tags", `refs/remotes/${remote}`], { cwd: repo.dir, encoding: "utf8", timeout: 5000 });
18403
+ const remotePrefix = `refs/remotes/${remote}/`;
18404
+ for (const ref of (listed.stdout ?? "").split(`
18405
+ `)) {
18406
+ if (ref.startsWith("refs/heads/"))
18407
+ facts.heads.add(ref.slice(11));
18408
+ else if (ref.startsWith("refs/tags/"))
18409
+ facts.tags.add(ref.slice(10));
18410
+ else if (ref.startsWith(remotePrefix))
18411
+ facts.remoteHeads.add(ref.slice(remotePrefix.length));
18412
+ }
18413
+ return facts;
18414
+ };
18415
+ var isTag = (facts, name) => facts.tags.has(name) && !facts.heads.has(name);
18416
+ var tagRefusal = (action, facts) => {
18417
+ const tags = [
18418
+ ...action.tags ?? [],
18419
+ ...action.targets.filter((name) => name !== null && isTag(facts, name))
18420
+ ];
18421
+ if (!tags.length && !action.allTags)
18422
+ return null;
18423
+ const named = tags.length ? tags.join(" --release ") : "v<version>";
18424
+ return `workit: raw ${quote(`git push${action.allTags ? " --tags" : ""}`)} of ${tags.length ? `tag ${tags.join(", ")}` : "tags"} bypasses the release grant. Push a release tag with ${quote(`workit git push --release ${named}`)} (v<version> tags whose commit the production branch already has; a tag never moves), under the ${quote("release")} grant (if refused, ask the user to run ${quote("workit grant set <workspace> release=true")}).`;
18425
+ };
18426
+ var remoteHas = (repo, remote, branch) => {
18427
+ const listed = spawnSync11("git", ["ls-remote", "--heads", remote, `refs/heads/${branch}`], {
18428
+ cwd: repo.dir,
18429
+ encoding: "utf8",
18430
+ timeout: 3000,
18431
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
18432
+ });
18433
+ return listed.status !== 0 || (listed.stdout ?? "").trim() !== "";
18434
+ };
18435
+ var offPolicyPush = (action, repo, facts, remote) => {
18436
+ const names = action.allBranches ? [...facts.heads] : action.targets.map((target) => target ?? repo.branch);
18437
+ for (const branch of names) {
18438
+ if (!branch || action.deleted?.includes(branch) || isTag(facts, branch))
18439
+ continue;
18440
+ if (facts.remoteHeads.has(branch))
18441
+ continue;
18442
+ const check = validateBranchNameFor(repo.dir, branch);
18443
+ if (check.ok || check.rule !== "allowed_pattern" || remoteHas(repo, remote, branch))
18444
+ continue;
18445
+ return `workit: raw ${quote("git push")} onto ${branch}: ${check.error}`;
18446
+ }
18447
+ return null;
18448
+ };
18411
18449
  var nudgeFor = (action, host, session) => {
18412
18450
  switch (action.kind) {
18413
18451
  case "commit": {
@@ -18470,7 +18508,7 @@ var unseenNudges = (input, nudges, store) => {
18470
18508
  function rawGitPre(input, command, options = {}) {
18471
18509
  try {
18472
18510
  if (!mentionsRawTool(command))
18473
- return NONE3;
18511
+ return NONE2;
18474
18512
  const dialect = dialectOf(input);
18475
18513
  const nudges = new Map;
18476
18514
  let store = null;
@@ -18478,32 +18516,43 @@ function rawGitPre(input, command, options = {}) {
18478
18516
  store ??= repo.location.dir;
18479
18517
  if (action.kind === "merge")
18480
18518
  return deny(refusal(action, null));
18481
- if (action.kind === "push")
18519
+ if (action.kind === "push" && toPushRemote(repo, action.remote)) {
18520
+ if (action.mirror)
18521
+ return deny(`workit: raw ${quote("git push --mirror")} rewrites and deletes every remote ref, protected branches and tags included. Push one branch with ${quote("workit git push")}.`);
18482
18522
  for (const target of action.targets) {
18483
18523
  const branch = target ?? repo.branch;
18484
- if (branch && guardedBranch(repo.dir, branch) && toPushRemote(repo, action.remote))
18485
- return deny(refusal(action, branch));
18524
+ if (branch && guardedBranch(repo.dir, branch))
18525
+ return deny(refusal(action, branch, repo.dir));
18486
18526
  }
18527
+ const remote = action.remote ?? pushRemoteName(repo.dir, repo.branch) ?? "origin";
18528
+ const facts = refFacts(repo, remote);
18529
+ const tagDeny = tagRefusal(action, facts);
18530
+ if (tagDeny)
18531
+ return deny(tagDeny);
18532
+ const nameDeny = action.deletes ? null : offPolicyPush(action, repo, facts, remote);
18533
+ if (nameDeny)
18534
+ return deny(nameDeny);
18535
+ }
18487
18536
  const nudge = nudgeFor(action, input.host, input.session.id);
18488
18537
  if (nudge && !nudges.has(nudgeKind(action)))
18489
18538
  nudges.set(nudgeKind(action), nudge);
18490
18539
  }
18491
18540
  if (options.nudge === false || !store || nudges.size === 0)
18492
- return NONE3;
18541
+ return NONE2;
18493
18542
  if (workitInvocations(command, dialect).length > 0)
18494
- return NONE3;
18543
+ return NONE2;
18495
18544
  const shown = unseenNudges(input, nudges, store);
18496
18545
  return shown.length ? { kind: "context", text: shown.join(`
18497
- `) } : NONE3;
18546
+ `) } : NONE2;
18498
18547
  } catch {
18499
- return NONE3;
18548
+ return NONE2;
18500
18549
  }
18501
18550
  }
18502
18551
  var deny = (reason) => ({ kind: "deny", reason, unblock: null });
18503
18552
  var NEXT_COMMAND = "next";
18504
18553
  var MARKER_TTL_SECONDS = 3600;
18505
- var markerFile = (repo, session, key) => path19.join(repo.location.dir, "hooks", `commit-${createHash13("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
18506
- var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash13("sha256").update(command).digest("hex")}`;
18554
+ var markerFile = (repo, session, key) => path19.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
18555
+ var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
18507
18556
  function noteRawCommit(input, command, key) {
18508
18557
  try {
18509
18558
  if (!input.session.id || !/\bcommit\b/.test(command))
@@ -18513,8 +18562,8 @@ function noteRawCommit(input, command, key) {
18513
18562
  if (action.kind !== "commit")
18514
18563
  continue;
18515
18564
  const file = markerFile(repo, input.session.id, key);
18516
- fs15.mkdirSync(path19.dirname(file), { recursive: true });
18517
- fs15.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
18565
+ fs16.mkdirSync(path19.dirname(file), { recursive: true });
18566
+ fs16.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
18518
18567
  `);
18519
18568
  }
18520
18569
  } catch {}
@@ -18537,11 +18586,11 @@ var headCommit = (dir) => {
18537
18586
  var settle2 = (input, repo, file) => {
18538
18587
  let marker;
18539
18588
  try {
18540
- marker = JSON.parse(fs15.readFileSync(file, "utf8"));
18589
+ marker = JSON.parse(fs16.readFileSync(file, "utf8"));
18541
18590
  } catch {
18542
18591
  return;
18543
18592
  }
18544
- fs15.rmSync(file, { force: true });
18593
+ fs16.rmSync(file, { force: true });
18545
18594
  const session = input.session.id;
18546
18595
  const now = Date.now() / 1000;
18547
18596
  if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
@@ -18590,12 +18639,297 @@ function settlePendingCommit(input) {
18590
18639
  if (!repo)
18591
18640
  return;
18592
18641
  const file = markerFile(repo, input.session.id, NEXT_COMMAND);
18593
- if (fs15.existsSync(file))
18642
+ if (fs16.existsSync(file))
18594
18643
  settle2(input, repo, file);
18595
18644
  } catch {}
18596
18645
  }
18597
18646
 
18647
+ // packages/workit-core/src/core/route-intent.ts
18648
+ var UNKNOWABLE = /[$`*?[\]{}~]/u;
18649
+ var SWITCH_CREATE = new Set(["-c", "--create", "-C", "--force-create", "--orphan"]);
18650
+ var CHECKOUT_CREATE = new Set(["-b", "-B", "--orphan"]);
18651
+ var WORKTREE_CREATE = new Set(["-b", "-B"]);
18652
+ var BRANCH_NO_CREATE = new Set(["-d", "-D", "--delete", "-l", "--list", "-a", "--all", "-r", "--remotes", "--show-current"].concat(["-v", "-vv", "--verbose", "--contains", "--no-contains", "--merged", "--no-merged"]).concat(["--points-at", "--sort", "--format", "--column", "--no-column", "-u"]).concat(["--set-upstream-to", "--unset-upstream", "--edit-description", "-i", "--ignore-case"]));
18653
+ var BRANCH_RENAME = new Set(["-m", "-M", "--move", "-c", "-C", "--copy"]);
18654
+ var flagValue = (args, flags) => {
18655
+ for (let index = 1;index < args.length; index++) {
18656
+ const arg = args[index];
18657
+ if (arg === "--")
18658
+ return null;
18659
+ if (flags.has(arg))
18660
+ return args[index + 1] ?? null;
18661
+ const eq = arg.indexOf("=");
18662
+ if (arg.startsWith("--") && eq > 0 && flags.has(arg.slice(0, eq)))
18663
+ return arg.slice(eq + 1);
18664
+ if (!arg.startsWith("--") && arg.length > 2 && flags.has(arg.slice(0, 2)))
18665
+ return arg.slice(2);
18666
+ }
18667
+ return null;
18668
+ };
18669
+ var WORKTREE_VALUES = new Set(["--reason"]);
18670
+ var worktreeTarget = (args) => {
18671
+ const named = flagValue(args, WORKTREE_CREATE);
18672
+ if (named !== null)
18673
+ return named;
18674
+ const positionals = [];
18675
+ for (let index = 1;index < args.length; index++) {
18676
+ const arg = args[index];
18677
+ if (arg === "--detach" || arg === "-d")
18678
+ return null;
18679
+ if (WORKTREE_VALUES.has(arg))
18680
+ index++;
18681
+ else if (!arg.startsWith("-"))
18682
+ positionals.push(arg);
18683
+ }
18684
+ if (positionals.length !== 1)
18685
+ return null;
18686
+ const base = positionals[0].split(/[\\/]/u).findLast(Boolean) ?? "";
18687
+ return base && base !== "." && base !== ".." ? base : null;
18688
+ };
18689
+ var branchCommandTarget = (args) => {
18690
+ let rename = false;
18691
+ const positionals = [];
18692
+ for (let index = 1;index < args.length; index++) {
18693
+ const arg = args[index];
18694
+ if (arg === "--") {
18695
+ positionals.push(...args.slice(index + 1));
18696
+ break;
18697
+ }
18698
+ const flag = arg.split("=", 1)[0];
18699
+ if (BRANCH_NO_CREATE.has(flag))
18700
+ return null;
18701
+ if (BRANCH_RENAME.has(flag))
18702
+ rename = true;
18703
+ else if (!arg.startsWith("-"))
18704
+ positionals.push(arg);
18705
+ }
18706
+ if (positionals.length < 1 || positionals.length > 2)
18707
+ return null;
18708
+ return rename ? positionals.at(-1) ?? null : positionals[0];
18709
+ };
18710
+ var gitBranchCreation = (args) => {
18711
+ const [sub, second] = args;
18712
+ const target = sub === "switch" ? flagValue(args, SWITCH_CREATE) : sub === "checkout" ? flagValue(args, CHECKOUT_CREATE) : sub === "branch" ? branchCommandTarget(args) : sub === "worktree" && second === "add" ? worktreeTarget(args.slice(1)) : null;
18713
+ return target && !target.startsWith("-") && !UNKNOWABLE.test(target) ? target : null;
18714
+ };
18715
+ var shellBranchTargets = (command, dialect = "posix") => {
18716
+ if (!/\bgit\b/u.test(command))
18717
+ return [];
18718
+ const found = [];
18719
+ for (const invocation of rawInvocations(command, dialect)) {
18720
+ if (invocation.tool !== "git")
18721
+ continue;
18722
+ const branch = gitBranchCreation(invocation.args);
18723
+ if (branch)
18724
+ found.push({ branch, dir: invocation.dir });
18725
+ }
18726
+ return found;
18727
+ };
18728
+ var localBranchExists = (dir, branch) => spawnSync12("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
18729
+ cwd: dir,
18730
+ stdio: "ignore",
18731
+ timeout: 5000
18732
+ }).status === 0;
18733
+ var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
18734
+ let judged = false;
18735
+ for (const target of shellBranchTargets(command, dialect)) {
18736
+ if (target.dir === undefined)
18737
+ continue;
18738
+ const dir = target.dir === null ? root : path20.resolve(root, target.dir);
18739
+ if (target.dir !== null && (!existsSync6(dir) || !inWorkitWorkspace(dir)))
18740
+ continue;
18741
+ if (localBranchExists(dir, target.branch))
18742
+ continue;
18743
+ judged = true;
18744
+ const check = validateBranchNameFor(dir, target.branch);
18745
+ if (!check.ok)
18746
+ return check;
18747
+ }
18748
+ return judged ? { ok: true } : null;
18749
+ };
18750
+
18751
+ // packages/workit-core/src/hooks/policy.ts
18752
+ var shellPolicy = (cwd, command, dialect = "posix") => {
18753
+ const policy = shellBranchPolicyViolation(cwd, command, dialect);
18754
+ return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
18755
+ };
18756
+ // packages/workit-core/src/hooks/write-gate.ts
18757
+ import { realpathSync as realpathSync5 } from "node:fs";
18758
+ import path21 from "node:path";
18759
+ var NONE3 = { kind: "none" };
18760
+ var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
18761
+ var canonical2 = (file) => {
18762
+ let rest = "";
18763
+ for (let dir = path21.resolve(file);; dir = path21.dirname(dir)) {
18764
+ try {
18765
+ return path21.join(realpathSync5(dir), rest);
18766
+ } catch {
18767
+ if (path21.dirname(dir) === dir)
18768
+ return path21.resolve(file);
18769
+ rest = rest ? path21.join(path21.basename(dir), rest) : path21.basename(dir);
18770
+ }
18771
+ }
18772
+ };
18773
+ var exempt = (cwd, root, file, plans) => {
18774
+ const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
18775
+ const relative = path21.relative(fold(canonical2(root)), fold(canonical2(path21.resolve(cwd, file))));
18776
+ if (relative.startsWith("..") || path21.isAbsolute(relative))
18777
+ return true;
18778
+ const posix = relative.split(path21.sep).join("/");
18779
+ const segments = posix.split("/");
18780
+ return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
18781
+ };
18782
+ var PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
18783
+ var operands = (words) => words.filter((item) => !item.startsWith("-"));
18784
+ var editsOf = (input) => {
18785
+ let words = input;
18786
+ while (words.length && (PREFIXES3.has(words[0]) || /^\w+=/.test(words[0])))
18787
+ words = words.slice(1);
18788
+ const [verb, ...args] = words;
18789
+ const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
18790
+ if (!name || name === "workit" || name === "npx" && args.some((item) => /workit/.test(item)))
18791
+ return [];
18792
+ switch (name) {
18793
+ case "touch":
18794
+ case "mkdir":
18795
+ case "rm":
18796
+ case "rmdir":
18797
+ case "tee":
18798
+ case "mv":
18799
+ case "truncate":
18800
+ return operands(args);
18801
+ case "cp":
18802
+ case "ln":
18803
+ case "install": {
18804
+ const target = args.indexOf("-t");
18805
+ if (target >= 0)
18806
+ return args[target + 1] ? [args[target + 1]] : null;
18807
+ const last = operands(args).at(-1);
18808
+ return last ? [last] : null;
18809
+ }
18810
+ case "dd":
18811
+ return args.filter((item) => item.startsWith("of=")).map((item) => item.slice(3));
18812
+ case "sed":
18813
+ case "perl": {
18814
+ if (!args.some((item) => /^-[a-zA-Z]*i|^--in-place/.test(item)))
18815
+ return [];
18816
+ const scripted = args.some((item) => item === "-e" || item === "-f");
18817
+ const files = [];
18818
+ for (let index = 0;index < args.length; index++) {
18819
+ if (args[index] === "-e" || args[index] === "-f")
18820
+ index++;
18821
+ else if (!args[index].startsWith("-"))
18822
+ files.push(args[index]);
18823
+ }
18824
+ return scripted ? files : files.slice(1);
18825
+ }
18826
+ case "patch":
18827
+ return null;
18828
+ case "set-content":
18829
+ case "add-content":
18830
+ case "out-file":
18831
+ case "new-item":
18832
+ case "ni":
18833
+ case "remove-item":
18834
+ case "del":
18835
+ case "copy-item":
18836
+ case "move-item": {
18837
+ const flag = args.findIndex((item) => /^-(?:path|filepath|literalpath|destination)$/i.test(item));
18838
+ if (flag >= 0)
18839
+ return args[flag + 1] ? [args[flag + 1]] : null;
18840
+ const files = operands(args);
18841
+ if (name === "move-item")
18842
+ return files.length ? files : null;
18843
+ const file = name === "copy-item" ? files.at(-1) : files[0];
18844
+ return file ? [file] : null;
18845
+ }
18846
+ case "git": {
18847
+ const sub = operands(args)[0];
18848
+ if (sub === "apply")
18849
+ return null;
18850
+ if (sub === "restore" || sub === "mv" || sub === "rm")
18851
+ return operands(args).slice(1);
18852
+ if (sub === "checkout" && args.includes("--"))
18853
+ return args.slice(args.indexOf("--") + 1);
18854
+ return [];
18855
+ }
18856
+ default:
18857
+ return [];
18858
+ }
18859
+ };
18860
+ var shellWrites = (command) => {
18861
+ const targets = [];
18862
+ let unknown = false;
18863
+ for (const segment of segmentsOf(command)) {
18864
+ targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
18865
+ const edits = editsOf(segment.words);
18866
+ if (edits === null)
18867
+ unknown = true;
18868
+ else
18869
+ targets.push(...edits);
18870
+ }
18871
+ if (!unknown && targets.length === 0)
18872
+ return { writes: false, targets: null };
18873
+ return { writes: true, targets: unknown ? null : targets };
18874
+ };
18875
+ var cache2 = new Map;
18876
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path21.resolve(root, file)) ?? "-").join("\x00");
18877
+ var gateFor = (cwd) => {
18878
+ const store = new TaskStore(cwd);
18879
+ const listed = store.listTaskIndex();
18880
+ if (!listed.ok)
18881
+ return null;
18882
+ const key = store.currentKey();
18883
+ if (!key.ok)
18884
+ return null;
18885
+ const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
18886
+ if (!entry)
18887
+ return { blockers: [], root: store.root, plans: new Set };
18888
+ const ledger = ledgerPath(store.root);
18889
+ const cacheKey = `${entry.id}\x00${entry.revision}\x00${ledger.ok && fileSignature(ledger.value) || "-"}`;
18890
+ const hit = cache2.get(store.root);
18891
+ if (hit && hit.key === cacheKey && hit.planKey === planKeyOf(store.root, hit.plans))
18892
+ return hit;
18893
+ const task = store.readTask(entry.id);
18894
+ if (!task.ok)
18895
+ return null;
18896
+ const workspace = store.readWorkspace();
18897
+ const plans = new Set((latestJudgment(task.data)?.refs ?? []).flatMap((ref) => ref.kind === "file" ? [ref.path] : []));
18898
+ const blockers = writeBlockers(task.data, store.root).filter((blocker) => {
18899
+ const requirement = task.data.policy?.requirements.find((item) => item.ruleId === blocker.ruleId && item.before === "write");
18900
+ return !(requirement?.acceptanceAllowed && workspace.ok && workspace.data && applicableDecision(task.data, workspace.data, requirement, store.root).length > 0);
18901
+ });
18902
+ const gate = {
18903
+ blockers,
18904
+ root: store.root,
18905
+ plans,
18906
+ key: cacheKey,
18907
+ planKey: planKeyOf(store.root, plans)
18908
+ };
18909
+ cache2.set(store.root, gate);
18910
+ if (cache2.size > 32)
18911
+ cache2.delete(cache2.keys().next().value);
18912
+ return gate;
18913
+ };
18914
+ function writeGate(cwd, paths) {
18915
+ try {
18916
+ const gate = gateFor(cwd);
18917
+ if (!gate || gate.blockers.length === 0)
18918
+ return NONE3;
18919
+ if (paths?.length && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
18920
+ return NONE3;
18921
+ const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
18922
+ return {
18923
+ kind: "deny",
18924
+ reason: `workit before-write gate: ${reasons}. Markdown, docs/, plans/ and files outside the checkout stay writable.`,
18925
+ unblock: gate.blockers[0].reason
18926
+ };
18927
+ } catch {
18928
+ return NONE3;
18929
+ }
18930
+ }
18598
18931
  // packages/workit-core/src/hooks/stop.ts
18932
+ import { spawnSync as spawnSync13 } from "node:child_process";
18599
18933
  var NONE4 = { kind: "none" };
18600
18934
  var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
18601
18935
  var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
@@ -18616,7 +18950,7 @@ var asksUser = (message) => {
18616
18950
  };
18617
18951
  var short = (sha) => (sha ?? "?").slice(0, 12);
18618
18952
  var gitList = (cwd, args) => {
18619
- const run = spawnSync12("git", args, {
18953
+ const run = spawnSync13("git", args, {
18620
18954
  cwd,
18621
18955
  encoding: "utf8",
18622
18956
  timeout: 3000,
@@ -18979,13 +19313,13 @@ ${line}` };
18979
19313
  return decision;
18980
19314
  };
18981
19315
  // packages/workit-core/src/hooks/hosts/fields.ts
18982
- import { existsSync as existsSync6, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
18983
- import path20 from "node:path";
19316
+ import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
19317
+ import path22 from "node:path";
18984
19318
  var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
18985
19319
  var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
18986
19320
  var optionalText = (value) => typeof value === "string" ? value : null;
18987
19321
  var existingDirectory = (value) => {
18988
- if (!nonEmpty2(value) || !path20.isAbsolute(value) || !existsSync6(value))
19322
+ if (!nonEmpty2(value) || !path22.isAbsolute(value) || !existsSync7(value))
18989
19323
  return null;
18990
19324
  try {
18991
19325
  return statSync2(value).isDirectory() ? realpathSync6(value) : null;
@@ -19074,17 +19408,20 @@ var subagentStartText = (input, descriptor, event) => {
19074
19408
  const role = workitRole(descriptor, event.agentType);
19075
19409
  const who = `${descriptor.label} subagent ${event.agentId} (${event.agentType})`;
19076
19410
  const isolated = descriptor.subagents.worktreeIsolation === "native" && (role === "implementer" || CLAUDE_WORKTREE.test(input.cwd));
19411
+ const guide = role === "implementer" ? branchGuidance(input.cwd) : null;
19077
19412
  if (role === "implementer" && isolated)
19078
- return `Workit observed ${who} working in its own git worktree: it may edit and commit there, within its brief's scope. ${ISOLATED_GIT} Use a policy-compliant branch (e.g. feature/<slug>); branch policy hooks still deny protected or non-compliant branches. Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
19413
+ return `Workit observed ${who} working in its own git worktree: it may edit and commit there, within its brief's scope. ${ISOLATED_GIT} Plain git is expected here, and the branch name is still policy-checked: a raw \`git switch -c\`, \`git checkout -b\`, \`git branch\` or \`git worktree add -b\` onto a protected or non-compliant name is denied, so use a policy-compliant branch.${guide ? ` ${guide}` : " (e.g. feature/<slug>)"} Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
19079
19414
  if (isolated)
19080
19415
  return `${ISOLATED_GIT}
19081
19416
  ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
19082
19417
  if (role === "implementer")
19083
- return `Workit observed ${who} as an implementer: it edits and commits only in its own git worktree (the lead makes one with \`workit fanout worktree create <slice>\`), never in the lead's checkout, within its brief's scope. Before the first commit, switch to a policy-compliant branch with \`workit git branch <branch> --base <base>\` (e.g. feature/<slug>); branch policy hooks still deny protected or non-compliant branches. Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
19418
+ return `Workit observed ${who} as an implementer: it edits and commits only in its own git worktree (the lead makes one with \`workit fanout worktree create <slice>\`), never in the lead's checkout, within its brief's scope. Before the first commit, switch to a policy-compliant branch with \`workit git branch <branch> --base <base>\` (or \`--kind <kind> --slug <slug>\`, which picks the base); branch policy hooks still deny protected or non-compliant branches, raw git included.${guide ? ` ${guide}` : " (e.g. feature/<slug>)"} Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
19084
19419
  const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
19085
19420
  if (role !== "judge" || !event.agentId)
19086
19421
  return readOnly;
19087
19422
  const own = subagentSession(session, event.agentId);
19423
+ if (session)
19424
+ recordIssuedSession(input.cwd || process.cwd(), session, own);
19088
19425
  return `${readOnly} Its own Workit session is ${own}: record verdicts with \`workit ledger verdict <result> --session ${own} ...\` so the ledger tells it apart from the author.`;
19089
19426
  };
19090
19427
  function handleHook(input, deps) {
@@ -19118,7 +19455,7 @@ function handleHook(input, deps) {
19118
19455
  if (skillRead)
19119
19456
  recordSkillLoad(input, skillRead, "read");
19120
19457
  const canDeny = usable2(descriptor.shellPolicy.deny);
19121
- const policy = canDeny ? shellPolicy(input.cwd, event.command) : NONE5;
19458
+ const policy = canDeny ? shellPolicy(input.cwd, event.command, event.dialect) : NONE5;
19122
19459
  if (policy.kind !== "none")
19123
19460
  return policy;
19124
19461
  const postTool = usable2(descriptor.events["shell.post"].support);
@@ -19716,12 +20053,12 @@ var codexAdapter = {
19716
20053
  };
19717
20054
  },
19718
20055
  render: render2,
19719
- addendum: (input) => `<workit-codex-mutations>Codex MCP is read-only: unattested callers cannot mutate. Run workit verbs with the workit CLI on the shell (node_modules/.bin/workit, or npx -y @brainervirus/workit-cli): check, git branch|commit|push, pr, ci, stack, ledger, handoff; task-family mutations take --json${input.session.id ? ` --actor ${input.session.id}` : ""}. workit acts as this thread (CODEX_THREAD_ID${input.session.id ? `=${input.session.id}` : ""}) unless WORKIT_SESSION_ID is set. A verifier or reviewer records workit ledger verdict under its own session, never the author's: a workit-verifier or workit-reviewer agent uses the --session its SubagentStart context names; any other uses one the lead assigns (WORKIT_HOST=codex_cli WORKIT_SESSION_ID=<lead>-v<n>). Merge and release need a workspace grant (workit grant show); a question answer is not host permission.</workit-codex-mutations>`
20056
+ addendum: (input) => `<workit-codex-mutations>Codex MCP is read-only: unattested callers cannot mutate. Run workit verbs with the workit CLI on the shell (node_modules/.bin/workit, or npx -y @brainervirus/workit-cli): check, git branch|commit|push, pr, ci, stack, ledger, handoff; task-family mutations take --json${input.session.id ? ` --actor ${input.session.id}` : ""}. workit acts as this thread (CODEX_THREAD_ID${input.session.id ? `=${input.session.id}` : ""}) unless WORKIT_SESSION_ID is set. A verifier or reviewer records workit ledger verdict under its own session, never the author's: a workit-verifier or workit-reviewer agent uses the --session its SubagentStart context names; any other passes --as verifier (an id derived from its own thread). A hand-minted id such as <lead>-v1 is refused as forged_session. Merge and release need a workspace grant (workit grant show); a question answer is not host permission.</workit-codex-mutations>`
19720
20057
  };
19721
20058
 
19722
20059
  // packages/workit-core/src/hooks/hosts/cursor.ts
19723
- import fs16 from "node:fs";
19724
- import path21 from "node:path";
20060
+ import fs17 from "node:fs";
20061
+ import path23 from "node:path";
19725
20062
  var undocumented = { support: "undocumented", native: null };
19726
20063
  var none = { support: "none", native: null };
19727
20064
  var CURSOR_DESCRIPTOR = {
@@ -19918,17 +20255,17 @@ var protocolEvent2 = (input) => {
19918
20255
  };
19919
20256
  var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
19920
20257
  var lastAssistantText = (file) => {
19921
- if (!file || !path21.isAbsolute(file))
20258
+ if (!file || !path23.isAbsolute(file))
19922
20259
  return null;
19923
20260
  try {
19924
- const size = fs16.statSync(file).size;
20261
+ const size = fs17.statSync(file).size;
19925
20262
  const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
19926
20263
  const buffer = Buffer.alloc(size - start);
19927
- const fd = fs16.openSync(file, "r");
20264
+ const fd = fs17.openSync(file, "r");
19928
20265
  try {
19929
- fs16.readSync(fd, buffer, 0, buffer.length, start);
20266
+ fs17.readSync(fd, buffer, 0, buffer.length, start);
19930
20267
  } finally {
19931
- fs16.closeSync(fd);
20268
+ fs17.closeSync(fd);
19932
20269
  }
19933
20270
  const lines = buffer.toString("utf8").split(`
19934
20271
  `);
@@ -19955,7 +20292,7 @@ var lastAssistantText = (file) => {
19955
20292
  return null;
19956
20293
  }
19957
20294
  };
19958
- var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path21.sep) ? root : `${root}${path21.sep}`);
20295
+ var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path23.sep) ? root : `${root}${path23.sep}`);
19959
20296
  var hookCwd = (input) => {
19960
20297
  const cwd = existingDirectory(input.cwd);
19961
20298
  if (cwd && input.hook_event_name === "beforeShellExecution")