@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.
@@ -8834,7 +8834,7 @@ var PREFIXES, branchNames = (workspaceRoot) => {
8834
8834
  return new Set;
8835
8835
  return new Set((r.stdout ?? "").split(`
8836
8836
  `).filter(Boolean).map((ref) => ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : ref.slice("refs/remotes/origin/".length)));
8837
- }, detectBranchPolicy = (workspaceRoot) => {
8837
+ }, productionBranchOf = (names) => names.has("main") ? "main" : names.has("master") ? "master" : "main", detectBranchPolicy = (workspaceRoot) => {
8838
8838
  const names = branchNames(workspaceRoot);
8839
8839
  const develop = names.has("develop");
8840
8840
  const main = names.has("main");
@@ -9832,37 +9832,6 @@ var init_vcs_config = __esm(() => {
9832
9832
  init_release_tracks();
9833
9833
  });
9834
9834
 
9835
- // packages/workit-core/src/host-session.ts
9836
- function hostSessionFromEnv(env) {
9837
- const value = (key) => env[key]?.trim() || null;
9838
- let inner = null;
9839
- for (const [key, host] of HOST_SESSION_VARS) {
9840
- const session = value(key);
9841
- if (session) {
9842
- inner = { host: host(env), session };
9843
- break;
9844
- }
9845
- }
9846
- const declared = value("WORKIT_HOST");
9847
- if (env.WORKIT_SESSION_ID !== undefined) {
9848
- const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
9849
- if (!inherited)
9850
- return { host: declared, session: value("WORKIT_SESSION_ID") };
9851
- }
9852
- return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
9853
- }
9854
- var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null;
9855
- var init_host_session = __esm(() => {
9856
- HOST_SESSION_VARS = [
9857
- ["OPENCODE_SESSION_ID", () => "opencode"],
9858
- ["PI_SESSION_ID", () => "pi"],
9859
- [
9860
- "CODEX_THREAD_ID",
9861
- (env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
9862
- ]
9863
- ];
9864
- });
9865
-
9866
9835
  // packages/workit-core/src/store/paths.ts
9867
9836
  import { spawnSync as spawnSync6 } from "node:child_process";
9868
9837
  import { createHash as createHash5 } from "node:crypto";
@@ -10139,11 +10108,69 @@ var init_paths = __esm(() => {
10139
10108
  ];
10140
10109
  });
10141
10110
 
10142
- // packages/workit-core/src/ledger.ts
10143
- import { spawnSync as spawnSync7 } from "node:child_process";
10144
- import { createHash as createHash6, randomBytes } from "node:crypto";
10111
+ // packages/workit-core/src/host-session.ts
10112
+ import { createHash as createHash6 } from "node:crypto";
10145
10113
  import fs4 from "node:fs";
10146
10114
  import path6 from "node:path";
10115
+ function hostSessionFromEnv(env) {
10116
+ const value = (key) => env[key]?.trim() || null;
10117
+ let inner = null;
10118
+ for (const [key, host] of HOST_SESSION_VARS) {
10119
+ const session = value(key);
10120
+ if (session) {
10121
+ inner = { host: host(env), session };
10122
+ break;
10123
+ }
10124
+ }
10125
+ const declared = value("WORKIT_HOST");
10126
+ if (env.WORKIT_SESSION_ID !== undefined) {
10127
+ const inherited = inner !== null && declared !== null && family(declared) !== family(inner.host);
10128
+ if (!inherited)
10129
+ return { host: declared, session: value("WORKIT_SESSION_ID") };
10130
+ }
10131
+ return inner ? { host: inner.host, session: inner.session } : { host: declared, session: null };
10132
+ }
10133
+ function recordIssuedSession(cwd, lead, issued) {
10134
+ try {
10135
+ const dir = issuedDir(cwd, lead);
10136
+ if (!dir)
10137
+ return;
10138
+ fs4.mkdirSync(dir, { recursive: true });
10139
+ const own = hash(issued);
10140
+ fs4.writeFileSync(path6.join(dir, own), ISSUED_SESSIONS_MARKER);
10141
+ const entries = fs4.readdirSync(dir);
10142
+ if (entries.length <= ISSUED_PER_LEAD)
10143
+ return;
10144
+ 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);
10145
+ for (const { name } of byAge.slice(0, entries.length - ISSUED_PER_LEAD))
10146
+ fs4.rmSync(path6.join(dir, name), { force: true });
10147
+ } catch {}
10148
+ }
10149
+ var HOST_SESSION_VARS, family = (host) => host?.replace(/_(?:cli|desktop)$/, "") ?? null, ISSUED_PER_LEAD = 64, ISSUED_SESSIONS_MARKER = "workit-issued-sessions/1", hash = (value) => createHash6("sha256").update(value).digest("hex").slice(0, 32), issuedRoot = (cwd) => {
10150
+ const location = resolveStore(cwd);
10151
+ return location instanceof Error ? null : path6.join(location.dir, "hooks", "issued");
10152
+ }, issuedDir = (cwd, lead) => {
10153
+ const root = issuedRoot(cwd);
10154
+ return root === null ? null : path6.join(root, hash(lead));
10155
+ }, OWN_CHILD_SHELL;
10156
+ var init_host_session = __esm(() => {
10157
+ init_paths();
10158
+ HOST_SESSION_VARS = [
10159
+ ["OPENCODE_SESSION_ID", () => "opencode"],
10160
+ ["PI_SESSION_ID", () => "pi"],
10161
+ [
10162
+ "CODEX_THREAD_ID",
10163
+ (env) => env.CODEX_INTERNAL_ORIGINATOR_OVERRIDE === "Codex Desktop" || env.CODEX_ELECTRON_RESOURCES_PATH ? "codex_desktop" : "codex_cli"
10164
+ ]
10165
+ ];
10166
+ OWN_CHILD_SHELL = new Set(["opencode", "pi"]);
10167
+ });
10168
+
10169
+ // packages/workit-core/src/ledger.ts
10170
+ import { spawnSync as spawnSync7 } from "node:child_process";
10171
+ import { createHash as createHash7, randomBytes } from "node:crypto";
10172
+ import fs5 from "node:fs";
10173
+ import path7 from "node:path";
10147
10174
  function resolveCommit(cwd, ref) {
10148
10175
  if (!safeRef(ref))
10149
10176
  return null;
@@ -10197,7 +10224,7 @@ function diffHash(cwd, base, head) {
10197
10224
  ], GIT_TIMEOUTS.worktree);
10198
10225
  if (!diff.ok || !diff.stdout)
10199
10226
  return null;
10200
- const hash = createHash6("sha256");
10227
+ const hash = createHash7("sha256");
10201
10228
  for (const line of diff.stdout.split(`
10202
10229
  `)) {
10203
10230
  if (line.startsWith("index "))
@@ -10209,14 +10236,14 @@ function diffHash(cwd, base, head) {
10209
10236
  return `sha256:${hash.digest("hex")}`;
10210
10237
  }
10211
10238
  function storeRoot(cwd) {
10212
- const location = resolveStore(path6.resolve(cwd));
10239
+ const location = resolveStore(path7.resolve(cwd));
10213
10240
  if (location instanceof Error)
10214
10241
  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");
10215
10242
  return { ok: true, value: { root: location.dir, shared: location.shared } };
10216
10243
  }
10217
10244
  function ledgerPath(cwd) {
10218
10245
  const root = storeRoot(cwd);
10219
- return root.ok ? { ok: true, value: path6.join(root.value.root, "ledger", "ledger.jsonl") } : root;
10246
+ return root.ok ? { ok: true, value: path7.join(root.value.root, "ledger", "ledger.jsonl") } : root;
10220
10247
  }
10221
10248
  function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
10222
10249
  const lock = `${file}.lock`;
@@ -10242,13 +10269,13 @@ function withLedgerLock(file, fn, waitMs = LOCK_WAIT_MS) {
10242
10269
  }
10243
10270
  function appendLine(file, build, options) {
10244
10271
  const write = () => {
10245
- const fd = fs4.openSync(file, "a+");
10272
+ const fd = fs5.openSync(file, "a+");
10246
10273
  try {
10247
- const size = fs4.fstatSync(fd).size;
10274
+ const size = fs5.fstatSync(fd).size;
10248
10275
  let fence = "";
10249
10276
  if (size > 0) {
10250
10277
  const last = Buffer.alloc(1);
10251
- fs4.readSync(fd, last, 0, 1, size - 1);
10278
+ fs5.readSync(fd, last, 0, 1, size - 1);
10252
10279
  if (last[0] !== 10)
10253
10280
  fence = `
10254
10281
  `;
@@ -10257,17 +10284,17 @@ function appendLine(file, build, options) {
10257
10284
  if (!line.ok)
10258
10285
  return line;
10259
10286
  const bytes = Buffer.from(fence + line.value);
10260
- const written = fs4.writeSync(fd, bytes);
10287
+ const written = fs5.writeSync(fd, bytes);
10261
10288
  if (written !== bytes.length)
10262
10289
  return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
10263
10290
  return { ok: true, value: undefined };
10264
10291
  } finally {
10265
- fs4.closeSync(fd);
10292
+ fs5.closeSync(fd);
10266
10293
  }
10267
10294
  };
10268
10295
  try {
10269
- fs4.mkdirSync(path6.dirname(file), { recursive: true });
10270
- if (!options.forceLock && !onNetworkFs(path6.dirname(file)))
10296
+ fs5.mkdirSync(path7.dirname(file), { recursive: true });
10297
+ if (!options.forceLock && !onNetworkFs(path7.dirname(file)))
10271
10298
  return write();
10272
10299
  const locked = withLedgerLock(file, write);
10273
10300
  return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
@@ -10321,7 +10348,7 @@ function readLedger(cwd, options = {}) {
10321
10348
  return file;
10322
10349
  let text = "";
10323
10350
  try {
10324
- text = fs4.readFileSync(file.value, "utf8");
10351
+ text = fs5.readFileSync(file.value, "utf8");
10325
10352
  } catch (error) {
10326
10353
  if (error.code === "ENOENT")
10327
10354
  return {
@@ -10676,28 +10703,28 @@ var LEDGER_VERSION = 1, MAX_LINE_BYTES = 4096, PASSING, FAILING, STRONG_RESULTS,
10676
10703
  if (process.platform !== "linux")
10677
10704
  return false;
10678
10705
  try {
10679
- return isNetworkFsType(fs4.statfsSync(dir).type);
10706
+ return isNetworkFsType(fs5.statfsSync(dir).type);
10680
10707
  } catch {
10681
10708
  return false;
10682
10709
  }
10683
10710
  }, LOCK_WAIT_MS = 5000, LOCK_STALE_MS = 30000, LOCK_OWNER = "owner", lockToken = (lock) => {
10684
10711
  try {
10685
- return fs4.readFileSync(path6.join(lock, LOCK_OWNER), "utf8");
10712
+ return fs5.readFileSync(path7.join(lock, LOCK_OWNER), "utf8");
10686
10713
  } catch {
10687
10714
  return null;
10688
10715
  }
10689
10716
  }, lockAgeMs = (lock) => {
10690
- for (const target of [path6.join(lock, LOCK_OWNER), lock]) {
10717
+ for (const target of [path7.join(lock, LOCK_OWNER), lock]) {
10691
10718
  try {
10692
- return Date.now() - fs4.statSync(target).mtimeMs;
10719
+ return Date.now() - fs5.statSync(target).mtimeMs;
10693
10720
  } catch {}
10694
10721
  }
10695
10722
  return null;
10696
- }, ledgerLock, sha2562 = (text) => createHash6("sha256").update(text).digest("hex"), chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
10723
+ }, ledgerLock, sha2562 = (text) => createHash7("sha256").update(text).digest("hex"), chainHash = (prevHash, body) => sha2562(`${prevHash ?? ""}
10697
10724
  ${body}`), TAIL_BYTES, lastLine = (fd, size) => {
10698
10725
  for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
10699
10726
  const buffer = Buffer.alloc(span);
10700
- fs4.readSync(fd, buffer, 0, span, size - span);
10727
+ fs5.readSync(fd, buffer, 0, span, size - span);
10701
10728
  const end = buffer.lastIndexOf(10);
10702
10729
  if (end < 0) {
10703
10730
  if (span === size)
@@ -10749,11 +10776,18 @@ ${body}`), TAIL_BYTES, lastLine = (fd, size) => {
10749
10776
  return false;
10750
10777
  return scopeOf(own)({ branch: theirs, at: target.at });
10751
10778
  };
10779
+ }, derivesFrom = (derived, bare) => !bare.includes(":") && derived.endsWith(`:${bare}`), authoredBy = (authors, session) => {
10780
+ if (authors.has(session))
10781
+ return true;
10782
+ for (const author of authors)
10783
+ if (derivesFrom(author, session) || derivesFrom(session, author))
10784
+ return true;
10785
+ return false;
10752
10786
  }, independenceReasons = (row, authors) => {
10753
10787
  const reasons = [];
10754
10788
  if (!row.actor.session)
10755
10789
  reasons.push("no_session");
10756
- else if (authors.has(row.actor.session))
10790
+ else if (authoredBy(authors, row.actor.session))
10757
10791
  reasons.push("author_session");
10758
10792
  if (row.self === true && !reasons.includes("no_session"))
10759
10793
  reasons.push("self");
@@ -10783,14 +10817,14 @@ var init_ledger = __esm(() => {
10783
10817
  ledgerLock = {
10784
10818
  acquire(lock) {
10785
10819
  try {
10786
- fs4.mkdirSync(lock);
10820
+ fs5.mkdirSync(lock);
10787
10821
  } catch (error) {
10788
10822
  if (error.code === "EEXIST")
10789
10823
  return null;
10790
10824
  throw error;
10791
10825
  }
10792
10826
  const token = `${process.pid}-${randomBytes(8).toString("hex")}`;
10793
- fs4.writeFileSync(path6.join(lock, LOCK_OWNER), token);
10827
+ fs5.writeFileSync(path7.join(lock, LOCK_OWNER), token);
10794
10828
  return token;
10795
10829
  },
10796
10830
  release(lock, token) {
@@ -10798,35 +10832,35 @@ var init_ledger = __esm(() => {
10798
10832
  return false;
10799
10833
  const tomb = `${lock}.release-${randomBytes(6).toString("hex")}`;
10800
10834
  try {
10801
- fs4.renameSync(lock, tomb);
10835
+ fs5.renameSync(lock, tomb);
10802
10836
  } catch {
10803
10837
  return false;
10804
10838
  }
10805
10839
  if (lockToken(tomb) !== token) {
10806
10840
  try {
10807
- fs4.renameSync(tomb, lock);
10841
+ fs5.renameSync(tomb, lock);
10808
10842
  } catch {}
10809
10843
  return false;
10810
10844
  }
10811
- fs4.rmSync(tomb, { recursive: true, force: true });
10845
+ fs5.rmSync(tomb, { recursive: true, force: true });
10812
10846
  return true;
10813
10847
  },
10814
10848
  token: lockToken,
10815
10849
  reap(lock, observed) {
10816
10850
  const tomb = `${lock}.reap-${randomBytes(6).toString("hex")}`;
10817
10851
  try {
10818
- fs4.renameSync(lock, tomb);
10852
+ fs5.renameSync(lock, tomb);
10819
10853
  } catch {
10820
10854
  return false;
10821
10855
  }
10822
10856
  if (lockToken(tomb) === observed) {
10823
- fs4.rmSync(tomb, { recursive: true, force: true });
10857
+ fs5.rmSync(tomb, { recursive: true, force: true });
10824
10858
  return true;
10825
10859
  }
10826
10860
  try {
10827
- fs4.renameSync(tomb, lock);
10861
+ fs5.renameSync(tomb, lock);
10828
10862
  } catch {
10829
- fs4.rmSync(tomb, { recursive: true, force: true });
10863
+ fs5.rmSync(tomb, { recursive: true, force: true });
10830
10864
  }
10831
10865
  return false;
10832
10866
  }
@@ -10843,7 +10877,7 @@ var init_ledger = __esm(() => {
10843
10877
 
10844
10878
  // packages/workit-core/src/core/policy/derive.ts
10845
10879
  import { existsSync as existsSync2 } from "node:fs";
10846
- import path7 from "node:path";
10880
+ import path8 from "node:path";
10847
10881
  function deriveRequirements(judgment, scope, constraints, verification = "self") {
10848
10882
  const out = [];
10849
10883
  const base = { scope, dependentAction: null };
@@ -10995,7 +11029,7 @@ var RULES, derived2 = (spec) => ({
10995
11029
  decisionIds: []
10996
11030
  };
10997
11031
  }, planRecorded = (task, root) => {
10998
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync2(path7.resolve(root, ref.path)));
11032
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync2(path8.resolve(root, ref.path)));
10999
11033
  if (cited)
11000
11034
  return {
11001
11035
  met: true,
@@ -11592,7 +11626,7 @@ var init_safe_write = () => {};
11592
11626
 
11593
11627
  // packages/workit-core/src/autonomy.ts
11594
11628
  import os3 from "node:os";
11595
- import path8 from "node:path";
11629
+ import path9 from "node:path";
11596
11630
  function endpointOf(entry) {
11597
11631
  const raw = entry?.defaultEndpoint;
11598
11632
  if (raw === undefined)
@@ -11689,9 +11723,9 @@ var accountHome = () => {
11689
11723
  } catch {
11690
11724
  return os3.homedir();
11691
11725
  }
11692
- }, grantsHome, grantsDir = () => path8.join(grantsHome.resolve(), ".config", "workit"), grantsOverride = () => {
11693
- const configured = path8.resolve(resolveConfigDir());
11694
- 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`;
11726
+ }, grantsHome, grantsDir = () => path9.join(grantsHome.resolve(), ".config", "workit"), grantsOverride = () => {
11727
+ const configured = path9.resolve(resolveConfigDir());
11728
+ 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`;
11695
11729
  }, GRANT_KINDS, DEFAULT_GRANTS, DEFAULT_ENDPOINTS, DEFAULT_ENDPOINT = "commit", isDefaultEndpoint = (value) => typeof value === "string" && DEFAULT_ENDPOINTS.includes(value), DEFAULT_VERIFICATION = "self", verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self", isGrantValue = (value) => value === true || value === false || value === "verified", normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value, fromAutoApprove = (value) => {
11696
11730
  if (value === true)
11697
11731
  return { push: true, pr: true, merge: "verified" };
@@ -11782,7 +11816,7 @@ function normalizeMaxBytes(value, options = {}) {
11782
11816
  }
11783
11817
 
11784
11818
  // node_modules/@openclaw/fs-safe/dist/bounded-read.js
11785
- import fs5 from "node:fs";
11819
+ import fs6 from "node:fs";
11786
11820
  function createScratchBuffer(maxBytes) {
11787
11821
  const initialReadBytes = Number.isFinite(maxBytes) ? Math.min(READ_CHUNK_BYTES, maxBytes + 1) : READ_CHUNK_BYTES;
11788
11822
  return Buffer.allocUnsafe(Math.max(1, initialReadBytes));
@@ -11805,7 +11839,7 @@ function readFileDescriptorBoundedSync(fd, maxBytes) {
11805
11839
  let total = 0;
11806
11840
  while (true) {
11807
11841
  const length = nextReadLength(total, maxBytes, scratch.length);
11808
- const bytesRead = fs5.readSync(fd, scratch, 0, length, null);
11842
+ const bytesRead = fs6.readSync(fd, scratch, 0, length, null);
11809
11843
  if (bytesRead === 0) {
11810
11844
  return Buffer.concat(chunks, total);
11811
11845
  }
@@ -11819,9 +11853,9 @@ var init_bounded_read = __esm(() => {
11819
11853
  });
11820
11854
 
11821
11855
  // node_modules/@openclaw/fs-safe/dist/read-open-flags.js
11822
- import fs6 from "node:fs";
11856
+ import fs7 from "node:fs";
11823
11857
  function resolveReadOpenFlags(options) {
11824
- const constants = options?.constants ?? fs6.constants;
11858
+ const constants = options?.constants ?? fs7.constants;
11825
11859
  const noFollow = process.platform !== "win32" && options?.followSymlinks !== true && typeof constants.O_NOFOLLOW === "number" ? constants.O_NOFOLLOW : 0;
11826
11860
  const nonBlocking = process.platform !== "win32" && typeof constants.O_NONBLOCK === "number" ? constants.O_NONBLOCK : 0;
11827
11861
  return constants.O_RDONLY | noFollow | nonBlocking;
@@ -11831,7 +11865,7 @@ var init_read_open_flags = () => {};
11831
11865
  // node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
11832
11866
  import { randomBytes as randomBytes2 } from "node:crypto";
11833
11867
  import fsSync from "node:fs";
11834
- import path9 from "node:path";
11868
+ import path10 from "node:path";
11835
11869
  function parseSidecarLockSnapshot(snapshot, parser) {
11836
11870
  return snapshot && { ...snapshot, payload: parseSidecarLockPayload(snapshot.raw, parser) };
11837
11871
  }
@@ -11859,13 +11893,13 @@ ${ownershipToken}
11859
11893
  return { raw, ownershipToken };
11860
11894
  }
11861
11895
  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)) {
11896
+ const resolved = path10.resolve(lockPath);
11897
+ const lexicalRelative = path10.relative(lockRoot.rootDir, resolved);
11898
+ const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path10.sep}`) && !path10.isAbsolute(lexicalRelative) ? lexicalRelative : path10.relative(lockRoot.rootReal, resolved);
11899
+ if (!relative || relative === ".." || relative.startsWith(`..${path10.sep}`) || path10.isAbsolute(relative)) {
11866
11900
  throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
11867
11901
  }
11868
- return relative.split(path9.sep).join(path9.posix.sep);
11902
+ return relative.split(path10.sep).join(path10.posix.sep);
11869
11903
  }
11870
11904
  function parseSidecarLockPayload(raw, parser) {
11871
11905
  if (parser) {
@@ -12034,8 +12068,8 @@ function sleepSync(ms) {
12034
12068
  }
12035
12069
 
12036
12070
  // node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
12037
- import fs7 from "node:fs";
12038
- import path10 from "node:path";
12071
+ import fs8 from "node:fs";
12072
+ import path11 from "node:path";
12039
12073
  function getSyncHeldLocks() {
12040
12074
  const globalWithState = globalThis;
12041
12075
  if (!globalWithState[SYNC_HELD_LOCKS_KEY]) {
@@ -12052,7 +12086,7 @@ function releaseAllSyncHeldLocks() {
12052
12086
  }
12053
12087
  try {
12054
12088
  if (held.fd !== undefined)
12055
- fs7.closeSync(held.fd);
12089
+ fs8.closeSync(held.fd);
12056
12090
  } catch {}
12057
12091
  try {
12058
12092
  removeSidecarLockIfUnchangedSync(held.lockPath, held.snapshot);
@@ -12087,7 +12121,7 @@ function releaseSyncHeldLock(held) {
12087
12121
  if (held.fd !== undefined) {
12088
12122
  const fd = held.fd;
12089
12123
  held.fd = undefined;
12090
- fs7.closeSync(fd);
12124
+ fs8.closeSync(fd);
12091
12125
  }
12092
12126
  removeSidecarLockIfUnchangedSync(held.lockPath, held.snapshot);
12093
12127
  heldLocks.delete(held.normalizedTargetPath);
@@ -12110,29 +12144,29 @@ function createSyncHeldLockHandle(held) {
12110
12144
  };
12111
12145
  }
12112
12146
  function canonicalLockParentSync(parent) {
12113
- return process.platform === "win32" ? fs7.realpathSync.native(parent) : fs7.realpathSync(parent);
12147
+ return process.platform === "win32" ? fs8.realpathSync.native(parent) : fs8.realpathSync(parent);
12114
12148
  }
12115
12149
  function normalizeTargetPath(targetPath) {
12116
- const resolved = path10.resolve(targetPath);
12117
- fs7.mkdirSync(path10.dirname(resolved), { recursive: true });
12150
+ const resolved = path11.resolve(targetPath);
12151
+ fs8.mkdirSync(path11.dirname(resolved), { recursive: true });
12118
12152
  try {
12119
- return path10.join(canonicalLockParentSync(path10.dirname(resolved)), path10.basename(resolved));
12153
+ return path11.join(canonicalLockParentSync(path11.dirname(resolved)), path11.basename(resolved));
12120
12154
  } catch {
12121
12155
  return resolved;
12122
12156
  }
12123
12157
  }
12124
12158
  function boundedLockPath(lockPath, lockRoot) {
12125
- const resolved = path10.resolve(lockPath);
12159
+ const resolved = path11.resolve(lockPath);
12126
12160
  if (!lockRoot)
12127
12161
  return resolved;
12128
12162
  relativeSidecarLockPath(lockRoot, resolved);
12129
- const parent = path10.dirname(resolved);
12163
+ const parent = path11.dirname(resolved);
12130
12164
  const parentReal = canonicalLockParentSync(parent);
12131
- const parentRelative = path10.relative(lockRoot.rootReal, parentReal);
12132
- if (parentRelative === ".." || parentRelative.startsWith(`..${path10.sep}`) || path10.isAbsolute(parentRelative)) {
12165
+ const parentRelative = path11.relative(lockRoot.rootReal, parentReal);
12166
+ if (parentRelative === ".." || parentRelative.startsWith(`..${path11.sep}`) || path11.isAbsolute(parentRelative)) {
12133
12167
  throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
12134
12168
  }
12135
- return path10.join(parentReal, path10.basename(resolved));
12169
+ return path11.join(parentReal, path11.basename(resolved));
12136
12170
  }
12137
12171
  function defaultShouldReclaim(snapshot, staleMs, nowMs) {
12138
12172
  const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
@@ -12142,7 +12176,7 @@ function defaultShouldReclaim(snapshot, staleMs, nowMs) {
12142
12176
  }
12143
12177
  function reclaimGuardExists(reclaimGuardPath) {
12144
12178
  try {
12145
- fs7.lstatSync(reclaimGuardPath);
12179
+ fs8.lstatSync(reclaimGuardPath);
12146
12180
  return true;
12147
12181
  } catch (error) {
12148
12182
  if (error.code === "ENOENT")
@@ -12206,19 +12240,19 @@ function acquireFileLockSync(targetPath, options) {
12206
12240
  const { raw, ownershipToken } = serializeSidecarLockPayload(payload);
12207
12241
  let lockFileCreateDenied = false;
12208
12242
  try {
12209
- const noFollow = process.platform !== "win32" && typeof fs7.constants.O_NOFOLLOW === "number" ? fs7.constants.O_NOFOLLOW : 0;
12243
+ const noFollow = process.platform !== "win32" && typeof fs8.constants.O_NOFOLLOW === "number" ? fs8.constants.O_NOFOLLOW : 0;
12210
12244
  try {
12211
- fd = fs7.openSync(lockPath, fs7.constants.O_WRONLY | fs7.constants.O_CREAT | fs7.constants.O_EXCL | noFollow, 384);
12245
+ fd = fs8.openSync(lockPath, fs8.constants.O_WRONLY | fs8.constants.O_CREAT | fs8.constants.O_EXCL | noFollow, 384);
12212
12246
  } catch (error) {
12213
12247
  lockFileCreateDenied = isTransientLockFileDenial(error, lockPath);
12214
12248
  throw error;
12215
12249
  }
12216
- fs7.writeFileSync(fd, raw, "utf8");
12217
- fs7.fsyncSync(fd);
12250
+ fs8.writeFileSync(fd, raw, "utf8");
12251
+ fs8.fsyncSync(fd);
12218
12252
  const snapshot = {
12219
12253
  raw,
12220
12254
  payload,
12221
- stat: fs7.fstatSync(fd),
12255
+ stat: fs8.fstatSync(fd),
12222
12256
  ownershipToken
12223
12257
  };
12224
12258
  const createdHeld = {
@@ -12253,8 +12287,8 @@ function acquireFileLockSync(targetPath, options) {
12253
12287
  return returnedHandle;
12254
12288
  } catch (error) {
12255
12289
  if (fd !== undefined) {
12256
- const failed = { payload: null, stat: fs7.fstatSync(fd) };
12257
- fs7.closeSync(fd);
12290
+ const failed = { payload: null, stat: fs8.fstatSync(fd) };
12291
+ fs8.closeSync(fd);
12258
12292
  fd = undefined;
12259
12293
  removeSidecarLockIfUnchangedSync(lockPath, failed);
12260
12294
  }
@@ -12303,7 +12337,7 @@ function acquireFileLockSync(targetPath, options) {
12303
12337
  })) {
12304
12338
  let ownsReclaimGuard = false;
12305
12339
  try {
12306
- fs7.mkdirSync(reclaimGuardPath);
12340
+ fs8.mkdirSync(reclaimGuardPath);
12307
12341
  ownsReclaimGuard = true;
12308
12342
  if (removeSidecarLockIfUnchangedSync(lockPath, snapshot))
12309
12343
  continue;
@@ -12316,7 +12350,7 @@ function acquireFileLockSync(targetPath, options) {
12316
12350
  } finally {
12317
12351
  if (ownsReclaimGuard) {
12318
12352
  try {
12319
- fs7.rmdirSync(reclaimGuardPath);
12353
+ fs8.rmdirSync(reclaimGuardPath);
12320
12354
  } catch {}
12321
12355
  }
12322
12356
  }
@@ -12349,13 +12383,13 @@ var init_file_lock = __esm(() => {
12349
12383
 
12350
12384
  // packages/workit-core/src/core/package-root.ts
12351
12385
  import { existsSync as existsSync3 } from "node:fs";
12352
- import path11 from "node:path";
12386
+ import path12 from "node:path";
12353
12387
  import { fileURLToPath } from "node:url";
12354
12388
  var packageRoot = () => {
12355
- let dir = path11.dirname(fileURLToPath(import.meta.url));
12389
+ let dir = path12.dirname(fileURLToPath(import.meta.url));
12356
12390
  while (true) {
12357
- const parent = path11.dirname(dir);
12358
- if (existsSync3(path11.join(dir, "package.json")) || parent === dir)
12391
+ const parent = path12.dirname(dir);
12392
+ if (existsSync3(path12.join(dir, "package.json")) || parent === dir)
12359
12393
  return dir;
12360
12394
  dir = parent;
12361
12395
  }
@@ -12364,7 +12398,7 @@ var init_package_root = () => {};
12364
12398
 
12365
12399
  // packages/workit-core/src/core/store-lock.ts
12366
12400
  import { spawnSync as spawnSync8 } from "node:child_process";
12367
- import fs8 from "node:fs";
12401
+ import fs9 from "node:fs";
12368
12402
  import { hostname } from "node:os";
12369
12403
  var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, RECLAIM_GUARD_TTL_MS = 30000, defaultLockTimeoutMs = 250, defaultLockTimeout = () => defaultLockTimeoutMs, parseMetadataLock = (raw) => {
12370
12404
  let value;
@@ -12391,7 +12425,7 @@ var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, REC
12391
12425
  }, processStartOf = (pid) => {
12392
12426
  if (process.platform === "linux") {
12393
12427
  try {
12394
- return parseProcStatStart(fs8.readFileSync(`/proc/${pid}/stat`, "utf8"));
12428
+ return parseProcStatStart(fs9.readFileSync(`/proc/${pid}/stat`, "utf8"));
12395
12429
  } catch {
12396
12430
  return null;
12397
12431
  }
@@ -12411,7 +12445,7 @@ var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, REC
12411
12445
  return null;
12412
12446
  }, readTrimmed = (file) => {
12413
12447
  try {
12414
- return fs8.readFileSync(file, "utf8").trim() || null;
12448
+ return fs9.readFileSync(file, "utf8").trim() || null;
12415
12449
  } catch {
12416
12450
  return null;
12417
12451
  }
@@ -12420,7 +12454,7 @@ var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, REC
12420
12454
  return cachedLockHost;
12421
12455
  let pidns = null;
12422
12456
  try {
12423
- pidns = /\[(\d+)\]/.exec(fs8.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
12457
+ pidns = /\[(\d+)\]/.exec(fs9.readlinkSync("/proc/self/ns/pid"))?.[1] ?? null;
12424
12458
  } catch {}
12425
12459
  const boot = readTrimmed("/proc/sys/kernel/random/boot_id");
12426
12460
  cachedLockHost = pidns || boot ? `${hostname()}#${pidns ?? "?"}:${boot ?? "?"}` : hostname();
@@ -12462,7 +12496,7 @@ var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, REC
12462
12496
  return { state: "live", reason: `held by running pid ${pid}` };
12463
12497
  }, ageOf = (file, nowMs) => {
12464
12498
  try {
12465
- return nowMs - fs8.lstatSync(file).mtimeMs;
12499
+ return nowMs - fs9.lstatSync(file).mtimeMs;
12466
12500
  } catch {
12467
12501
  return null;
12468
12502
  }
@@ -12472,7 +12506,7 @@ var metadataLockSchema, FOREIGN_LOCK_TTL_MS, UNREADABLE_LOCK_TTL_MS = 30000, REC
12472
12506
  if (age === null || age <= RECLAIM_GUARD_TTL_MS)
12473
12507
  return false;
12474
12508
  try {
12475
- fs8.rmdirSync(guard);
12509
+ fs9.rmdirSync(guard);
12476
12510
  return true;
12477
12511
  } catch {
12478
12512
  return false;
@@ -12495,12 +12529,12 @@ var init_store_lock = __esm(() => {
12495
12529
 
12496
12530
  // packages/workit-core/src/store/event-log.ts
12497
12531
  import { randomUUID as randomUUID2 } from "node:crypto";
12498
- import fs9 from "node:fs";
12499
- import path12 from "node:path";
12532
+ import fs10 from "node:fs";
12533
+ import path13 from "node:path";
12500
12534
  function readLog(file, from = 0, expectSeq = null) {
12501
12535
  let fd;
12502
12536
  try {
12503
- fd = fs9.openSync(file, "r");
12537
+ fd = fs10.openSync(file, "r");
12504
12538
  } catch (error) {
12505
12539
  if (error.code === "ENOENT")
12506
12540
  return null;
@@ -12509,13 +12543,13 @@ function readLog(file, from = 0, expectSeq = null) {
12509
12543
  let bytes;
12510
12544
  let size;
12511
12545
  try {
12512
- size = fs9.fstatSync(fd).size;
12546
+ size = fs10.fstatSync(fd).size;
12513
12547
  if (from > size)
12514
12548
  throw new LogDamage(`${file}: shorter than its snapshot`);
12515
12549
  bytes = Buffer.alloc(size - from);
12516
12550
  let read = 0;
12517
12551
  while (read < bytes.length) {
12518
- const got = fs9.readSync(fd, bytes, read, bytes.length - read, from + read);
12552
+ const got = fs10.readSync(fd, bytes, read, bytes.length - read, from + read);
12519
12553
  if (got === 0)
12520
12554
  break;
12521
12555
  read += got;
@@ -12523,7 +12557,7 @@ function readLog(file, from = 0, expectSeq = null) {
12523
12557
  bytes = bytes.subarray(0, read);
12524
12558
  size = from + read;
12525
12559
  } finally {
12526
- fs9.closeSync(fd);
12560
+ fs10.closeSync(fd);
12527
12561
  }
12528
12562
  const events = [];
12529
12563
  let start = 0;
@@ -12549,46 +12583,46 @@ function readLog(file, from = 0, expectSeq = null) {
12549
12583
  function appendEvent(file, event, validEnd) {
12550
12584
  const line = Buffer.from(`${JSON.stringify(event)}
12551
12585
  `, "utf8");
12552
- const created = !fs9.existsSync(file);
12553
- if (!created && fs9.statSync(file).size !== validEnd) {
12554
- const cut = fs9.openSync(file, "r+");
12586
+ const created = !fs10.existsSync(file);
12587
+ if (!created && fs10.statSync(file).size !== validEnd) {
12588
+ const cut = fs10.openSync(file, "r+");
12555
12589
  try {
12556
- fs9.ftruncateSync(cut, validEnd);
12557
- fs9.fsyncSync(cut);
12590
+ fs10.ftruncateSync(cut, validEnd);
12591
+ fs10.fsyncSync(cut);
12558
12592
  } finally {
12559
- fs9.closeSync(cut);
12593
+ fs10.closeSync(cut);
12560
12594
  }
12561
12595
  }
12562
- const fd = fs9.openSync(file, "a", 384);
12596
+ const fd = fs10.openSync(file, "a", 384);
12563
12597
  try {
12564
12598
  let written = 0;
12565
12599
  while (written < line.length)
12566
- written += fs9.writeSync(fd, line, written, line.length - written);
12567
- fs9.fsyncSync(fd);
12600
+ written += fs10.writeSync(fd, line, written, line.length - written);
12601
+ fs10.fsyncSync(fd);
12568
12602
  } finally {
12569
- fs9.closeSync(fd);
12603
+ fs10.closeSync(fd);
12570
12604
  }
12571
12605
  if (created)
12572
- fsyncDirectory(path12.dirname(file));
12606
+ fsyncDirectory(path13.dirname(file));
12573
12607
  return validEnd;
12574
12608
  }
12575
12609
  function replaceFile(file, bytes, durable) {
12576
12610
  const temporary = `${file}.${process.pid}.${randomUUID2()}.tmp`;
12577
12611
  try {
12578
- const fd = fs9.openSync(temporary, "wx", 384);
12612
+ const fd = fs10.openSync(temporary, "wx", 384);
12579
12613
  try {
12580
- fs9.writeSync(fd, bytes);
12614
+ fs10.writeSync(fd, bytes);
12581
12615
  if (durable)
12582
- fs9.fsyncSync(fd);
12616
+ fs10.fsyncSync(fd);
12583
12617
  } finally {
12584
- fs9.closeSync(fd);
12618
+ fs10.closeSync(fd);
12585
12619
  }
12586
12620
  renameWithRetry(temporary, file);
12587
12621
  if (durable)
12588
- fsyncDirectory(path12.dirname(file));
12622
+ fsyncDirectory(path13.dirname(file));
12589
12623
  } catch (error) {
12590
12624
  try {
12591
- fs9.unlinkSync(temporary);
12625
+ fs10.unlinkSync(temporary);
12592
12626
  } catch {}
12593
12627
  throw error;
12594
12628
  }
@@ -12609,11 +12643,11 @@ var EVENT_VERSION = 1, LogDamage, isObject4 = (value) => typeof value === "objec
12609
12643
  return value;
12610
12644
  }, fsyncDirectory = (dir) => {
12611
12645
  try {
12612
- const fd = fs9.openSync(dir, "r");
12646
+ const fd = fs10.openSync(dir, "r");
12613
12647
  try {
12614
- fs9.fsyncSync(fd);
12648
+ fs10.fsyncSync(fd);
12615
12649
  } finally {
12616
- fs9.closeSync(fd);
12650
+ fs10.closeSync(fd);
12617
12651
  }
12618
12652
  } catch (error) {
12619
12653
  if (process.platform !== "win32")
@@ -12636,7 +12670,7 @@ var EVENT_VERSION = 1, LogDamage, isObject4 = (value) => typeof value === "objec
12636
12670
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 5 * (attempt + 1));
12637
12671
  }
12638
12672
  }
12639
- }, renameWithRetry = (from, to) => retryTransient(() => fs9.renameSync(from, to));
12673
+ }, renameWithRetry = (from, to) => retryTransient(() => fs10.renameSync(from, to));
12640
12674
  var init_event_log = __esm(() => {
12641
12675
  LogDamage = class LogDamage extends Error {
12642
12676
  upgrade;
@@ -12755,7 +12789,7 @@ var isPlainObject2 = (value) => typeof value === "object" && value !== null && !
12755
12789
  });
12756
12790
 
12757
12791
  // packages/workit-core/src/store/reduce.ts
12758
- import { createHash as createHash7 } from "node:crypto";
12792
+ import { createHash as createHash8 } from "node:crypto";
12759
12793
  function reduce(state, event, blobs) {
12760
12794
  const data = event.data;
12761
12795
  const known = KNOWN_TYPES.has(event.type);
@@ -12822,7 +12856,7 @@ function eventType(before, after) {
12822
12856
  return "task.noted";
12823
12857
  return "task.updated";
12824
12858
  }
12825
- var OPENING_TYPES, KNOWN_TYPES, isObject5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isKey = (value) => isObject5(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string"), BLOB_REF = "$blob", 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, blobDigest = (value) => createHash7("sha256").update(JSON.stringify(value)).digest("hex"), toBlob = (value, blobs) => {
12859
+ var OPENING_TYPES, KNOWN_TYPES, isObject5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isKey = (value) => isObject5(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string"), BLOB_REF = "$blob", 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, blobDigest = (value) => createHash8("sha256").update(JSON.stringify(value)).digest("hex"), toBlob = (value, blobs) => {
12826
12860
  if (!isObject5(value))
12827
12861
  return value;
12828
12862
  const digest = blobDigest(value);
@@ -12893,9 +12927,9 @@ var init_notes = __esm(() => {
12893
12927
  });
12894
12928
 
12895
12929
  // packages/workit-core/src/core/task-store.ts
12896
- import { createHash as createHash8, randomUUID as randomUUID3 } from "node:crypto";
12897
- import * as fs10 from "node:fs";
12898
- import path13 from "node:path";
12930
+ import { createHash as createHash9, randomUUID as randomUUID3 } from "node:crypto";
12931
+ import * as fs11 from "node:fs";
12932
+ import path14 from "node:path";
12899
12933
 
12900
12934
  class TaskStore {
12901
12935
  root;
@@ -12907,8 +12941,8 @@ class TaskStore {
12907
12941
  migrationChecked = false;
12908
12942
  compactDue = new Set;
12909
12943
  constructor(root, options = {}) {
12910
- const resolved = path13.resolve(root);
12911
- this.origin = fs10.existsSync(resolved) ? fs10.realpathSync(resolved) : resolved;
12944
+ const resolved = path14.resolve(root);
12945
+ this.origin = fs11.existsSync(resolved) ? fs11.realpathSync(resolved) : resolved;
12912
12946
  this.located = resolveStore(this.origin);
12913
12947
  this.root = checkoutRootOf(this.origin, this.located);
12914
12948
  this.lockTimeoutMs = options.lockTimeoutMs ?? defaultLockTimeout();
@@ -12916,7 +12950,7 @@ class TaskStore {
12916
12950
  this.migrateOnRead = options.migrateOnRead;
12917
12951
  }
12918
12952
  location() {
12919
- if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs10.existsSync(path13.join(this.root, ".git"))) {
12953
+ if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs11.existsSync(path14.join(this.root, ".git"))) {
12920
12954
  this.located = undefined;
12921
12955
  this.migrationChecked = false;
12922
12956
  }
@@ -12980,7 +13014,7 @@ class TaskStore {
12980
13014
  const tasksDir = this.paths().tasks;
12981
13015
  let names;
12982
13016
  try {
12983
- names = fs10.existsSync(tasksDir) ? fs10.readdirSync(tasksDir).filter((name) => validId(name)) : [];
13017
+ names = fs11.existsSync(tasksDir) ? fs11.readdirSync(tasksDir).filter((name) => validId(name)) : [];
12984
13018
  } catch (error) {
12985
13019
  return failure2("storage_error", `unable to list tasks: ${String(error)}`, {
12986
13020
  path: tasksDir
@@ -13021,7 +13055,7 @@ class TaskStore {
13021
13055
  const file = this.paths().workspace;
13022
13056
  let bytes;
13023
13057
  try {
13024
- bytes = retryTransient(() => fs10.readFileSync(file, "utf8"));
13058
+ bytes = retryTransient(() => fs11.readFileSync(file, "utf8"));
13025
13059
  } catch (error) {
13026
13060
  if (error.code === "ENOENT")
13027
13061
  return success(null, null, null);
@@ -13072,7 +13106,7 @@ class TaskStore {
13072
13106
  const moved = this.bindLocked(again.data.id, key, input.now);
13073
13107
  return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
13074
13108
  }
13075
- const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path13.basename(this.root) || this.root;
13109
+ const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path14.basename(this.root) || this.root;
13076
13110
  const intent = {
13077
13111
  objective: input.objective?.trim() || `Work on ${label}`,
13078
13112
  scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
@@ -13132,14 +13166,14 @@ class TaskStore {
13132
13166
  const branch = key.ok ? key.data.branch : null;
13133
13167
  if (!branch || !location.ok)
13134
13168
  return success(null, null, null);
13135
- const common = path13.dirname(location.data.dir);
13136
- const heads = path13.join(common, "logs", "refs", "heads");
13137
- const headLog = location.data.git ? path13.join(location.data.git.gitDir, "logs", "HEAD") : "";
13169
+ const common = path14.dirname(location.data.dir);
13170
+ const heads = path14.join(common, "logs", "refs", "heads");
13171
+ const headLog = location.data.git ? path14.join(location.data.git.gitDir, "logs", "HEAD") : "";
13138
13172
  const signatures = [
13139
- path13.join(heads, branch),
13173
+ path14.join(heads, branch),
13140
13174
  headLog,
13141
13175
  heads,
13142
- path13.join(common, "packed-refs")
13176
+ path14.join(common, "packed-refs")
13143
13177
  ].map((file) => fileSignature(file) ?? "-");
13144
13178
  const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
13145
13179
  let resolved = origins.get(slot);
@@ -13151,7 +13185,7 @@ class TaskStore {
13151
13185
  aliases.push(rename.from);
13152
13186
  const start = branchStart(location.data, branch, aliases);
13153
13187
  const slash = start?.indexOf("/") ?? -1;
13154
- 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)}"]`));
13188
+ 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)}"]`));
13155
13189
  const name = start && (remote ? start.slice(slash + 1) : start);
13156
13190
  resolved = {
13157
13191
  signature: signatures.join("|"),
@@ -13389,13 +13423,13 @@ class TaskStore {
13389
13423
  if (!ready.ok)
13390
13424
  return ready;
13391
13425
  const dirs = this.paths();
13392
- const recovery = path13.join(this.root, ".workit", "recovery");
13393
- if (fs10.existsSync(recovery)) {
13426
+ const recovery = path14.join(this.root, ".workit", "recovery");
13427
+ if (fs11.existsSync(recovery)) {
13394
13428
  let files = 0;
13395
13429
  let bytes = 0;
13396
13430
  try {
13397
- for (const name of fs10.readdirSync(recovery)) {
13398
- const stat = fs10.lstatSync(path13.join(recovery, name));
13431
+ for (const name of fs11.readdirSync(recovery)) {
13432
+ const stat = fs11.lstatSync(path14.join(recovery, name));
13399
13433
  if (!stat.isFile())
13400
13434
  continue;
13401
13435
  files += 1;
@@ -13404,17 +13438,17 @@ class TaskStore {
13404
13438
  } catch {}
13405
13439
  const remove = options.pruneRecovery === true && !dryRun;
13406
13440
  if (remove)
13407
- fs10.rmSync(recovery, { recursive: true, force: true });
13441
+ fs11.rmSync(recovery, { recursive: true, force: true });
13408
13442
  report.legacyRecovery = { path: recovery, files, bytes, removed: remove };
13409
13443
  }
13410
- if (!fs10.existsSync(dirs.tasks))
13444
+ if (!fs11.existsSync(dirs.tasks))
13411
13445
  return success(null, null, report);
13412
- const names = fs10.readdirSync(dirs.tasks).filter((name) => validId(name));
13446
+ const names = fs11.readdirSync(dirs.tasks).filter((name) => validId(name));
13413
13447
  for (const name of names) {
13414
13448
  const file = this.eventsPath(name);
13415
13449
  let size = 0;
13416
13450
  try {
13417
- size = fs10.statSync(file).size;
13451
+ size = fs11.statSync(file).size;
13418
13452
  } catch {
13419
13453
  continue;
13420
13454
  }
@@ -13442,16 +13476,16 @@ class TaskStore {
13442
13476
  const references = new Set;
13443
13477
  for (const name of names)
13444
13478
  try {
13445
- for (const match of fs10.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
13479
+ for (const match of fs11.readFileSync(this.eventsPath(name), "utf8").matchAll(/"\$blob":"([0-9a-f]{64})"/g))
13446
13480
  references.add(match[1]);
13447
13481
  } catch {}
13448
- if (fs10.existsSync(dirs.blobs))
13449
- for (const name of fs10.readdirSync(dirs.blobs)) {
13482
+ if (fs11.existsSync(dirs.blobs))
13483
+ for (const name of fs11.readdirSync(dirs.blobs)) {
13450
13484
  const digest = name.replace(/\.json$/, "");
13451
- const file = path13.join(dirs.blobs, name);
13485
+ const file = path14.join(dirs.blobs, name);
13452
13486
  let stat;
13453
13487
  try {
13454
- stat = fs10.lstatSync(file);
13488
+ stat = fs11.lstatSync(file);
13455
13489
  } catch {
13456
13490
  continue;
13457
13491
  }
@@ -13462,11 +13496,11 @@ class TaskStore {
13462
13496
  report.blobs.removed += 1;
13463
13497
  report.blobs.removedBytes += stat.size;
13464
13498
  if (!dryRun)
13465
- fs10.rmSync(file, { force: true });
13499
+ fs11.rmSync(file, { force: true });
13466
13500
  }
13467
13501
  return success(null, null, null);
13468
13502
  };
13469
- const swept = dryRun ? sweep() : this.withLock(path13.join(dirs.dir, "gc.lock"), sweep);
13503
+ const swept = dryRun ? sweep() : this.withLock(path14.join(dirs.dir, "gc.lock"), sweep);
13470
13504
  if (!swept.ok)
13471
13505
  return swept;
13472
13506
  const temporaryDirs = [
@@ -13474,17 +13508,17 @@ class TaskStore {
13474
13508
  dirs.tasks,
13475
13509
  dirs.blobs,
13476
13510
  dirs.checkout,
13477
- ...names.map((name) => path13.join(dirs.tasks, name))
13511
+ ...names.map((name) => path14.join(dirs.tasks, name))
13478
13512
  ];
13479
13513
  for (const directory of temporaryDirs) {
13480
- if (!fs10.existsSync(directory))
13514
+ if (!fs11.existsSync(directory))
13481
13515
  continue;
13482
- for (const name of fs10.readdirSync(directory)) {
13516
+ for (const name of fs11.readdirSync(directory)) {
13483
13517
  if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
13484
13518
  continue;
13485
- const file = path13.join(directory, name);
13519
+ const file = path14.join(directory, name);
13486
13520
  try {
13487
- const stat = fs10.lstatSync(file);
13521
+ const stat = fs11.lstatSync(file);
13488
13522
  if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
13489
13523
  continue;
13490
13524
  } catch {
@@ -13492,37 +13526,37 @@ class TaskStore {
13492
13526
  }
13493
13527
  report.temporary.removed += 1;
13494
13528
  if (!dryRun)
13495
- fs10.rmSync(file, { force: true });
13529
+ fs11.rmSync(file, { force: true });
13496
13530
  }
13497
13531
  }
13498
13532
  return success(null, null, report);
13499
13533
  }
13500
13534
  paths() {
13501
13535
  const location = this.location();
13502
- const dir = location.ok ? location.data.dir : path13.join(this.root, ".workit");
13503
- const checkout = path13.join(dir, "checkouts", checkoutSlug(this.root));
13536
+ const dir = location.ok ? location.data.dir : path14.join(this.root, ".workit");
13537
+ const checkout = path14.join(dir, "checkouts", checkoutSlug(this.root));
13504
13538
  return {
13505
13539
  dir,
13506
13540
  shared: location.ok ? location.data.shared : false,
13507
- marker: path13.join(dir, "store.json"),
13541
+ marker: path14.join(dir, "store.json"),
13508
13542
  checkout,
13509
- workspace: path13.join(checkout, "workspace.json"),
13510
- checkoutLock: path13.join(checkout, "metadata.lock"),
13511
- tasks: path13.join(dir, "tasks"),
13512
- index: path13.join(dir, "task-index.json"),
13513
- blobs: path13.join(dir, "blobs", "candidates"),
13514
- legacy: path13.join(dir, "legacy", checkoutSlug(this.root), "v2"),
13515
- v2: path13.join(this.root, ".workit")
13543
+ workspace: path14.join(checkout, "workspace.json"),
13544
+ checkoutLock: path14.join(checkout, "metadata.lock"),
13545
+ tasks: path14.join(dir, "tasks"),
13546
+ index: path14.join(dir, "task-index.json"),
13547
+ blobs: path14.join(dir, "blobs", "candidates"),
13548
+ legacy: path14.join(dir, "legacy", checkoutSlug(this.root), "v2"),
13549
+ v2: path14.join(this.root, ".workit")
13516
13550
  };
13517
13551
  }
13518
13552
  eventsPath(taskId) {
13519
- return path13.join(this.paths().tasks, taskId, "events.jsonl");
13553
+ return path14.join(this.paths().tasks, taskId, "events.jsonl");
13520
13554
  }
13521
13555
  snapshotPath(taskId) {
13522
- return path13.join(this.paths().tasks, taskId, "snapshot.json");
13556
+ return path14.join(this.paths().tasks, taskId, "snapshot.json");
13523
13557
  }
13524
13558
  taskLockPath(taskId) {
13525
- return path13.join(this.paths().tasks, taskId, "lock");
13559
+ return path14.join(this.paths().tasks, taskId, "lock");
13526
13560
  }
13527
13561
  ready(write = false) {
13528
13562
  const location = this.location();
@@ -13532,7 +13566,7 @@ class TaskStore {
13532
13566
  return success(null, null, null);
13533
13567
  const dirs = this.paths();
13534
13568
  try {
13535
- const marker = JSON.parse(fs10.readFileSync(dirs.marker, "utf8"));
13569
+ const marker = JSON.parse(fs11.readFileSync(dirs.marker, "utf8"));
13536
13570
  if (typeof marker.version === "number" && marker.version > STORE_FORMAT)
13537
13571
  return failure2("recovery_required", `the workit store at ${dirs.dir} has format ${marker.version}; upgrade Workit to use it`, { path: dirs.marker });
13538
13572
  } catch {}
@@ -13561,21 +13595,21 @@ class TaskStore {
13561
13595
  return {
13562
13596
  read: (digest) => {
13563
13597
  try {
13564
- return JSON.parse(fs10.readFileSync(path13.join(dir, `${digest}.json`), "utf8"));
13598
+ return JSON.parse(fs11.readFileSync(path14.join(dir, `${digest}.json`), "utf8"));
13565
13599
  } catch (error) {
13566
13600
  throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
13567
13601
  }
13568
13602
  },
13569
13603
  write: (digest, value) => {
13570
- const file = path13.join(dir, `${digest}.json`);
13571
- if (fs10.existsSync(file)) {
13604
+ const file = path14.join(dir, `${digest}.json`);
13605
+ if (fs11.existsSync(file)) {
13572
13606
  const stamp = new Date;
13573
13607
  try {
13574
- fs10.utimesSync(file, stamp, stamp);
13608
+ fs11.utimesSync(file, stamp, stamp);
13575
13609
  return;
13576
13610
  } catch {}
13577
13611
  }
13578
- fs10.mkdirSync(dir, { recursive: true });
13612
+ fs11.mkdirSync(dir, { recursive: true });
13579
13613
  replaceFile(file, JSON.stringify(value), true);
13580
13614
  }
13581
13615
  };
@@ -13657,7 +13691,7 @@ class TaskStore {
13657
13691
  }
13658
13692
  readSnapshot(taskId) {
13659
13693
  try {
13660
- const value = JSON.parse(fs10.readFileSync(this.snapshotPath(taskId), "utf8"));
13694
+ const value = JSON.parse(fs11.readFileSync(this.snapshotPath(taskId), "utf8"));
13661
13695
  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))
13662
13696
  return null;
13663
13697
  return value;
@@ -13759,8 +13793,8 @@ class TaskStore {
13759
13793
  }
13760
13794
  }
13761
13795
  openTask(record, type, key, legacy = null) {
13762
- const dir = path13.join(this.paths().tasks, record.id);
13763
- fs10.mkdirSync(dir, { recursive: true });
13796
+ const dir = path14.join(this.paths().tasks, record.id);
13797
+ fs11.mkdirSync(dir, { recursive: true });
13764
13798
  const blobs = this.blobStore();
13765
13799
  const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
13766
13800
  const event = this.event(record.id, 1, type, {
@@ -13812,11 +13846,11 @@ class TaskStore {
13812
13846
  } catch {}
13813
13847
  if (!latest || latest.events[0]?.id !== last.id)
13814
13848
  return failure2("busy", "the task log changed during compaction; retry", { taskId });
13815
- const appended = fs10.readFileSync(file).subarray(log.end, latest.end);
13849
+ const appended = fs11.readFileSync(file).subarray(log.end, latest.end);
13816
13850
  const bytes = Buffer.concat([Buffer.from(prefix, "utf8"), appended]);
13817
13851
  replaceFile(file, bytes.toString("utf8"), true);
13818
13852
  try {
13819
- fs10.rmSync(this.snapshotPath(taskId), { force: true });
13853
+ fs11.rmSync(this.snapshotPath(taskId), { force: true });
13820
13854
  } catch {}
13821
13855
  const reloaded = this.load(taskId);
13822
13856
  if (reloaded.ok && reloaded.data)
@@ -13829,7 +13863,7 @@ class TaskStore {
13829
13863
  }
13830
13864
  countEvents(file) {
13831
13865
  try {
13832
- const bytes = fs10.readFileSync(file);
13866
+ const bytes = fs11.readFileSync(file);
13833
13867
  let count = 0;
13834
13868
  for (let index = bytes.indexOf(10);index >= 0; index = bytes.indexOf(10, index + 1))
13835
13869
  count += 1;
@@ -13851,8 +13885,8 @@ class TaskStore {
13851
13885
  return bound.ok ? success(null, null, bound.data[0] ?? null) : bound;
13852
13886
  }
13853
13887
  withKeyLock(key, operation) {
13854
- const digest = createHash8("sha256").update(key).digest("hex").slice(0, 32);
13855
- return this.withLock(path13.join(this.paths().dir, "keys", `${digest}.lock`), operation);
13888
+ const digest = createHash9("sha256").update(key).digest("hex").slice(0, 32);
13889
+ return this.withLock(path14.join(this.paths().dir, "keys", `${digest}.lock`), operation);
13856
13890
  }
13857
13891
  bindLocked(taskId, key, timestamp) {
13858
13892
  const current = this.readWorkspace();
@@ -13993,7 +14027,7 @@ class TaskStore {
13993
14027
  writeWorkspace(record) {
13994
14028
  const file = this.paths().workspace;
13995
14029
  try {
13996
- fs10.mkdirSync(path13.dirname(file), { recursive: true });
14030
+ fs11.mkdirSync(path14.dirname(file), { recursive: true });
13997
14031
  replaceFile(file, `${canonicalJson(record)}
13998
14032
  `, true);
13999
14033
  return success(null, null, null);
@@ -14003,7 +14037,7 @@ class TaskStore {
14003
14037
  }
14004
14038
  readIndex() {
14005
14039
  try {
14006
- const value = JSON.parse(fs10.readFileSync(this.paths().index, "utf8"));
14040
+ const value = JSON.parse(fs11.readFileSync(this.paths().index, "utf8"));
14007
14041
  if (!isObject6(value) || value.version !== INDEX_VERSION || !isObject6(value.tasks))
14008
14042
  return {};
14009
14043
  const tasks = {};
@@ -14017,7 +14051,7 @@ class TaskStore {
14017
14051
  }
14018
14052
  writeIndex(entries) {
14019
14053
  try {
14020
- fs10.mkdirSync(this.paths().tasks, { recursive: true });
14054
+ fs11.mkdirSync(this.paths().tasks, { recursive: true });
14021
14055
  replaceFile(this.paths().index, JSON.stringify({
14022
14056
  version: INDEX_VERSION,
14023
14057
  tasks: Object.fromEntries(entries.map((entry) => [entry.id, entry]))
@@ -14071,23 +14105,23 @@ class TaskStore {
14071
14105
  return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
14072
14106
  }
14073
14107
  v2Dirs() {
14074
- return [...new Set([this.root, this.origin])].map((dir) => path13.join(dir, ".workit"));
14108
+ return [...new Set([this.root, this.origin])].map((dir) => path14.join(dir, ".workit"));
14075
14109
  }
14076
14110
  pendingMigrations() {
14077
14111
  return this.v2Dirs().filter((dir) => {
14078
- const raw = this.readV2Workspace(path13.join(dir, "workspace.json"));
14112
+ const raw = this.readV2Workspace(path14.join(dir, "workspace.json"));
14079
14113
  return raw !== null && raw !== TOMBSTONE;
14080
14114
  });
14081
14115
  }
14082
14116
  migrateV2(v2) {
14083
14117
  const dirs = this.paths();
14084
- const v2Workspace = path13.join(v2, "workspace.json");
14085
- const v2Tasks = path13.join(v2, "tasks");
14086
- const legacy = path13.join(dirs.dir, "legacy", checkoutSlug(path13.dirname(v2)), "v2");
14118
+ const v2Workspace = path14.join(v2, "workspace.json");
14119
+ const v2Tasks = path14.join(v2, "tasks");
14120
+ const legacy = path14.join(dirs.dir, "legacy", checkoutSlug(path14.dirname(v2)), "v2");
14087
14121
  const raw = this.readV2Workspace(v2Workspace);
14088
14122
  if (raw === null || raw === TOMBSTONE)
14089
14123
  return success(null, null, null);
14090
- return this.withLock(dirs.checkoutLock, () => this.withLock(path13.join(v2, "metadata.lock"), () => {
14124
+ return this.withLock(dirs.checkoutLock, () => this.withLock(path14.join(v2, "metadata.lock"), () => {
14091
14125
  const workspace = this.readV2Workspace(v2Workspace);
14092
14126
  if (workspace === null || workspace === TOMBSTONE)
14093
14127
  return success(null, null, null);
@@ -14096,7 +14130,7 @@ class TaskStore {
14096
14130
  return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
14097
14131
  let files = [];
14098
14132
  try {
14099
- files = fs10.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path13.join(v2Tasks, name)));
14133
+ files = fs11.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path14.join(v2Tasks, name)));
14100
14134
  } catch {}
14101
14135
  const current = this.readWorkspace();
14102
14136
  if (!current.ok)
@@ -14114,22 +14148,22 @@ class TaskStore {
14114
14148
  let skipped = 0;
14115
14149
  const done = new Map;
14116
14150
  try {
14117
- fs10.mkdirSync(path13.join(legacy, "tasks"), { recursive: true });
14151
+ fs11.mkdirSync(path14.join(legacy, "tasks"), { recursive: true });
14118
14152
  for (const name of ["workspace.json", "index.json"])
14119
- if (fs10.existsSync(path13.join(v2, name)))
14120
- fs10.copyFileSync(path13.join(v2, name), path13.join(legacy, name));
14153
+ if (fs11.existsSync(path14.join(v2, name)))
14154
+ fs11.copyFileSync(path14.join(v2, name), path14.join(legacy, name));
14121
14155
  for (const name of files) {
14122
14156
  const id = name.slice(0, -5);
14123
- const file = path13.join(v2Tasks, name);
14124
- const bytes = fs10.readFileSync(file, "utf8");
14157
+ const file = path14.join(v2Tasks, name);
14158
+ const bytes = fs11.readFileSync(file, "utf8");
14125
14159
  const digest = sha256(bytes);
14126
- fs10.writeFileSync(path13.join(legacy, "tasks", name), bytes);
14160
+ fs11.writeFileSync(path14.join(legacy, "tasks", name), bytes);
14127
14161
  const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
14128
14162
  if (!parsed.ok || parsed.data.id !== id)
14129
14163
  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 });
14130
14164
  const record = { ...parsed.data, workspaceId: target.id };
14131
- const origin = { path: path13.join(legacy, "tasks", name), digest };
14132
- if (!fs10.existsSync(this.eventsPath(id))) {
14165
+ const origin = { path: path14.join(legacy, "tasks", name), digest };
14166
+ if (!fs11.existsSync(this.eventsPath(id))) {
14133
14167
  this.openTask(record, "migrated.from_v2", null, origin);
14134
14168
  migrated += 1;
14135
14169
  } else {
@@ -14164,8 +14198,8 @@ class TaskStore {
14164
14198
  try {
14165
14199
  let complete = true;
14166
14200
  for (const [name, digest] of done) {
14167
- const file = path13.join(v2Tasks, name);
14168
- if (sha256(fs10.readFileSync(file, "utf8")) !== digest) {
14201
+ const file = path14.join(v2Tasks, name);
14202
+ if (sha256(fs11.readFileSync(file, "utf8")) !== digest) {
14169
14203
  complete = false;
14170
14204
  continue;
14171
14205
  }
@@ -14175,7 +14209,7 @@ class TaskStore {
14175
14209
  return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
14176
14210
  path: v2
14177
14211
  });
14178
- fs10.rmSync(path13.join(v2, "index.json"), { force: true });
14212
+ fs11.rmSync(path14.join(v2, "index.json"), { force: true });
14179
14213
  this.writeTombstone(v2, target.id);
14180
14214
  } catch (error) {
14181
14215
  return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
@@ -14195,45 +14229,45 @@ class TaskStore {
14195
14229
  }
14196
14230
  adoptLocalStore() {
14197
14231
  const dirs = this.paths();
14198
- const marker = path13.join(dirs.v2, "store.json");
14199
- if (!dirs.shared || !fs10.existsSync(marker))
14232
+ const marker = path14.join(dirs.v2, "store.json");
14233
+ if (!dirs.shared || !fs11.existsSync(marker))
14200
14234
  return success(null, null, null);
14201
14235
  return this.withLock(dirs.checkoutLock, () => {
14202
- if (!fs10.existsSync(marker))
14236
+ if (!fs11.existsSync(marker))
14203
14237
  return success(null, null, null);
14204
14238
  let tasks = 0;
14205
14239
  try {
14206
14240
  const merge = (from, to) => {
14207
- if (!fs10.existsSync(to)) {
14208
- fs10.mkdirSync(path13.dirname(to), { recursive: true });
14241
+ if (!fs11.existsSync(to)) {
14242
+ fs11.mkdirSync(path14.dirname(to), { recursive: true });
14209
14243
  try {
14210
- fs10.renameSync(from, to);
14244
+ fs11.renameSync(from, to);
14211
14245
  } catch {
14212
- fs10.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
14213
- fs10.rmSync(from, { recursive: true, force: true });
14246
+ fs11.cpSync(from, to, { recursive: true, errorOnExist: true, force: false });
14247
+ fs11.rmSync(from, { recursive: true, force: true });
14214
14248
  }
14215
14249
  return;
14216
14250
  }
14217
- if (fs10.lstatSync(from).isDirectory() && fs10.lstatSync(to).isDirectory())
14218
- for (const name of fs10.readdirSync(from))
14219
- merge(path13.join(from, name), path13.join(to, name));
14251
+ if (fs11.lstatSync(from).isDirectory() && fs11.lstatSync(to).isDirectory())
14252
+ for (const name of fs11.readdirSync(from))
14253
+ merge(path14.join(from, name), path14.join(to, name));
14220
14254
  };
14221
14255
  for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
14222
- const from = path13.join(dirs.v2, sub);
14223
- if (!fs10.existsSync(from))
14256
+ const from = path14.join(dirs.v2, sub);
14257
+ if (!fs11.existsSync(from))
14224
14258
  continue;
14225
14259
  if (sub === "tasks") {
14226
- for (const name of fs10.readdirSync(from))
14227
- if (validId(name) && !fs10.existsSync(path13.join(dirs.dir, sub, name)))
14260
+ for (const name of fs11.readdirSync(from))
14261
+ if (validId(name) && !fs11.existsSync(path14.join(dirs.dir, sub, name)))
14228
14262
  tasks += 1;
14229
14263
  }
14230
- merge(from, path13.join(dirs.dir, sub));
14264
+ merge(from, path14.join(dirs.dir, sub));
14231
14265
  }
14232
- const ledger = path13.join(dirs.v2, "ledger");
14233
- if (fs10.existsSync(ledger) && !fs10.existsSync(path13.join(dirs.dir, "ledger")))
14234
- merge(ledger, path13.join(dirs.dir, "ledger"));
14235
- fs10.rmSync(path13.join(dirs.v2, "task-index.json"), { force: true });
14236
- fs10.renameSync(marker, path13.join(dirs.v2, "store.moved.json"));
14266
+ const ledger = path14.join(dirs.v2, "ledger");
14267
+ if (fs11.existsSync(ledger) && !fs11.existsSync(path14.join(dirs.dir, "ledger")))
14268
+ merge(ledger, path14.join(dirs.dir, "ledger"));
14269
+ fs11.rmSync(path14.join(dirs.v2, "task-index.json"), { force: true });
14270
+ fs11.renameSync(marker, path14.join(dirs.v2, "store.moved.json"));
14237
14271
  } catch (error) {
14238
14272
  return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
14239
14273
  path: dirs.v2
@@ -14253,7 +14287,7 @@ class TaskStore {
14253
14287
  readV2Workspace(file) {
14254
14288
  let bytes;
14255
14289
  try {
14256
- bytes = fs10.readFileSync(file, "utf8");
14290
+ bytes = fs11.readFileSync(file, "utf8");
14257
14291
  } catch {
14258
14292
  return null;
14259
14293
  }
@@ -14271,7 +14305,7 @@ class TaskStore {
14271
14305
  schemaVersion: SCHEMA_VERSION,
14272
14306
  id: workspaceId ?? newId(),
14273
14307
  revision: newRevision(),
14274
- root: path13.dirname(v2),
14308
+ root: path14.dirname(v2),
14275
14309
  runtime: { createdWith: version, updatedWith: version },
14276
14310
  writer: null,
14277
14311
  store: {
@@ -14282,12 +14316,12 @@ class TaskStore {
14282
14316
  },
14283
14317
  critical: ["store"]
14284
14318
  };
14285
- fs10.mkdirSync(v2, { recursive: true });
14286
- const ignore = path13.join(v2, ".gitignore");
14287
- if (!fs10.existsSync(ignore))
14288
- fs10.writeFileSync(ignore, `*
14319
+ fs11.mkdirSync(v2, { recursive: true });
14320
+ const ignore = path14.join(v2, ".gitignore");
14321
+ if (!fs11.existsSync(ignore))
14322
+ fs11.writeFileSync(ignore, `*
14289
14323
  `);
14290
- replaceFile(path13.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
14324
+ replaceFile(path14.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
14291
14325
  `, true);
14292
14326
  }
14293
14327
  stubFor(taskId) {
@@ -14303,7 +14337,7 @@ class TaskStore {
14303
14337
  }
14304
14338
  isStub(file) {
14305
14339
  try {
14306
- const value = JSON.parse(fs10.readFileSync(file, "utf8"));
14340
+ const value = JSON.parse(fs11.readFileSync(file, "utf8"));
14307
14341
  return isObject6(value) && isObject6(value.store) && value.store.format === "workit-store";
14308
14342
  } catch {
14309
14343
  return false;
@@ -14311,32 +14345,32 @@ class TaskStore {
14311
14345
  }
14312
14346
  writeTombstoneIfMissing() {
14313
14347
  try {
14314
- const v2 = path13.join(this.root, ".workit");
14315
- if (fs10.existsSync(path13.join(v2, "workspace.json")))
14348
+ const v2 = path14.join(this.root, ".workit");
14349
+ if (fs11.existsSync(path14.join(v2, "workspace.json")))
14316
14350
  return;
14317
14351
  let id = null;
14318
14352
  try {
14319
- id = JSON.parse(fs10.readFileSync(this.paths().workspace, "utf8")).id ?? null;
14353
+ id = JSON.parse(fs11.readFileSync(this.paths().workspace, "utf8")).id ?? null;
14320
14354
  } catch {}
14321
14355
  this.writeTombstone(v2, id);
14322
14356
  } catch {}
14323
14357
  }
14324
14358
  initializeStorage() {
14325
14359
  const dirs = this.paths();
14326
- retryTransient(() => fs10.mkdirSync(dirs.checkout, { recursive: true }));
14327
- retryTransient(() => fs10.mkdirSync(dirs.tasks, { recursive: true }));
14328
- if (!fs10.existsSync(dirs.marker))
14360
+ retryTransient(() => fs11.mkdirSync(dirs.checkout, { recursive: true }));
14361
+ retryTransient(() => fs11.mkdirSync(dirs.tasks, { recursive: true }));
14362
+ if (!fs11.existsSync(dirs.marker))
14329
14363
  replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
14330
14364
  `, false);
14331
14365
  if (!dirs.shared) {
14332
- const ignore = path13.join(dirs.dir, ".gitignore");
14366
+ const ignore = path14.join(dirs.dir, ".gitignore");
14333
14367
  let current = null;
14334
14368
  try {
14335
- current = fs10.readFileSync(ignore, "utf8");
14369
+ current = fs11.readFileSync(ignore, "utf8");
14336
14370
  } catch {}
14337
14371
  if (current !== `*
14338
14372
  `)
14339
- retryTransient(() => fs10.writeFileSync(ignore, `*
14373
+ retryTransient(() => fs11.writeFileSync(ignore, `*
14340
14374
  `));
14341
14375
  }
14342
14376
  this.writeTombstoneIfMissing();
@@ -14352,7 +14386,7 @@ class TaskStore {
14352
14386
  shouldReclaim: ({ payload, nowMs }) => {
14353
14387
  let ageMs = null;
14354
14388
  try {
14355
- ageMs = nowMs - fs10.lstatSync(lockPath).mtimeMs;
14389
+ ageMs = nowMs - fs11.lstatSync(lockPath).mtimeMs;
14356
14390
  } catch {}
14357
14391
  return classifyLockOwner(payload, ageMs, localLockHost(), TASK_STORE_LOCK).state === "stale";
14358
14392
  },
@@ -14389,7 +14423,7 @@ class TaskStore {
14389
14423
  withLock(lockPath, operation) {
14390
14424
  try {
14391
14425
  this.initializeStorage();
14392
- fs10.mkdirSync(path13.dirname(lockPath), { recursive: true });
14426
+ fs11.mkdirSync(path14.dirname(lockPath), { recursive: true });
14393
14427
  } catch (error) {
14394
14428
  return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
14395
14429
  path: this.paths().dir
@@ -14429,18 +14463,18 @@ class TaskStore {
14429
14463
  lockDirWritable(lockPath) {
14430
14464
  const probe = `${lockPath}.${process.pid}.${randomUUID3()}.probe`;
14431
14465
  try {
14432
- fs10.closeSync(fs10.openSync(probe, "wx", 384));
14466
+ fs11.closeSync(fs11.openSync(probe, "wx", 384));
14433
14467
  } catch {
14434
14468
  return false;
14435
14469
  }
14436
14470
  try {
14437
- fs10.rmSync(probe, { force: true });
14471
+ fs11.rmSync(probe, { force: true });
14438
14472
  } catch {}
14439
14473
  return true;
14440
14474
  }
14441
14475
  lockHolderPresent(lockPath) {
14442
14476
  try {
14443
- fs10.lstatSync(lockPath);
14477
+ fs11.lstatSync(lockPath);
14444
14478
  return true;
14445
14479
  } catch (error) {
14446
14480
  return error?.code !== "ENOENT";
@@ -14452,12 +14486,12 @@ class TaskStore {
14452
14486
  if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
14453
14487
  return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
14454
14488
  path: lockPath,
14455
- guidance: `Check permissions and the read-only attribute on ${path13.dirname(lockPath)}.`
14489
+ guidance: `Check permissions and the read-only attribute on ${path14.dirname(lockPath)}.`
14456
14490
  });
14457
14491
  if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
14458
14492
  let lock = null;
14459
14493
  try {
14460
- lock = parseMetadataLockOrNull(fs10.readFileSync(lockPath, "utf8"));
14494
+ lock = parseMetadataLockOrNull(fs11.readFileSync(lockPath, "utf8"));
14461
14495
  } catch {}
14462
14496
  return failure2("busy", `workit lock is held by another Workit call${lock ? ` (pid ${lock.pid} on ${lock.host})` : ""}; retry shortly`, {
14463
14497
  outcome: "not_started",
@@ -14498,7 +14532,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
14498
14532
  };
14499
14533
  }, fileSignature = (file) => {
14500
14534
  try {
14501
- const stat = fs10.statSync(file, { bigint: true });
14535
+ const stat = fs11.statSync(file, { bigint: true });
14502
14536
  return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
14503
14537
  } catch {
14504
14538
  return null;
@@ -14510,7 +14544,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
14510
14544
  }, now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"), oldestFirst = (left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id), cachedRuntimeVersion = null, runtimeVersion = () => {
14511
14545
  if (cachedRuntimeVersion === null) {
14512
14546
  try {
14513
- const pkg = JSON.parse(fs10.readFileSync(path13.join(packageRoot(), "package.json"), "utf8"));
14547
+ const pkg = JSON.parse(fs11.readFileSync(path14.join(packageRoot(), "package.json"), "utf8"));
14514
14548
  cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
14515
14549
  } catch {
14516
14550
  cachedRuntimeVersion = "0.0.0";
@@ -14532,12 +14566,12 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
14532
14566
  return false;
14533
14567
  }, 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), readText = (file) => {
14534
14568
  try {
14535
- return fs10.readFileSync(file, "utf8");
14569
+ return fs11.readFileSync(file, "utf8");
14536
14570
  } catch {
14537
14571
  return "";
14538
14572
  }
14539
14573
  }, REFLOG_LINE, branchStart = (location, branch, aliases) => {
14540
- const first = readText(path13.join(path13.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
14574
+ const first = readText(path14.join(path14.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
14541
14575
  `, 1)[0];
14542
14576
  const created = REFLOG_LINE.exec(first);
14543
14577
  const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
@@ -14547,7 +14581,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
14547
14581
  return start;
14548
14582
  if (!location.git)
14549
14583
  return null;
14550
- for (const line of readText(path13.join(location.git.gitDir, "logs", "HEAD")).split(`
14584
+ for (const line of readText(path14.join(location.git.gitDir, "logs", "HEAD")).split(`
14551
14585
  `)) {
14552
14586
  const entry = REFLOG_LINE.exec(line);
14553
14587
  const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
@@ -14556,21 +14590,21 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
14556
14590
  return moved[1];
14557
14591
  }
14558
14592
  return null;
14559
- }, refExists = (common, ref) => fs10.existsSync(path13.join(common, ref)) || readText(path13.join(common, "packed-refs")).includes(` ${ref}
14593
+ }, refExists = (common, ref) => fs11.existsSync(path14.join(common, ref)) || readText(path14.join(common, "packed-refs")).includes(` ${ref}
14560
14594
  `), origins, sameDirectoryIdentity = (left, right) => {
14561
- if (!path13.isAbsolute(left) || !path13.isAbsolute(right))
14595
+ if (!path14.isAbsolute(left) || !path14.isAbsolute(right))
14562
14596
  return false;
14563
- const normalizedLeft = path13.resolve(left);
14564
- const normalizedRight = path13.resolve(right);
14597
+ const normalizedLeft = path14.resolve(left);
14598
+ const normalizedRight = path14.resolve(right);
14565
14599
  try {
14566
- const a = fs10.statSync(normalizedLeft, { bigint: true });
14567
- const b = fs10.statSync(normalizedRight, { bigint: true });
14600
+ const a = fs11.statSync(normalizedLeft, { bigint: true });
14601
+ const b = fs11.statSync(normalizedRight, { bigint: true });
14568
14602
  if (!a.isDirectory() || !b.isDirectory())
14569
14603
  return false;
14570
14604
  if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
14571
14605
  return true;
14572
- const canonicalLeft = fs10.realpathSync(normalizedLeft);
14573
- const canonicalRight = fs10.realpathSync(normalizedRight);
14606
+ const canonicalLeft = fs11.realpathSync(normalizedLeft);
14607
+ const canonicalRight = fs11.realpathSync(normalizedRight);
14574
14608
  if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
14575
14609
  return true;
14576
14610
  return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
@@ -14618,8 +14652,8 @@ var init_workers = __esm(() => {
14618
14652
 
14619
14653
  // packages/workit-core/src/check-config.ts
14620
14654
  import { spawnSync as spawnSync9 } from "node:child_process";
14621
- import fs11 from "node:fs";
14622
- import path14 from "node:path";
14655
+ import fs12 from "node:fs";
14656
+ import path15 from "node:path";
14623
14657
  function splitCommand(command) {
14624
14658
  const argv = [];
14625
14659
  let current = "";
@@ -14657,12 +14691,12 @@ function splitCommand(command) {
14657
14691
  return argv.length ? argv : new Error("empty command");
14658
14692
  }
14659
14693
  function checkRoot(cwd) {
14660
- return path14.resolve(git4(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
14694
+ return path15.resolve(git4(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
14661
14695
  }
14662
14696
  function detectedChecks(root) {
14663
14697
  const read = (file) => {
14664
14698
  try {
14665
- return fs11.readFileSync(path14.join(root, file), "utf8");
14699
+ return fs12.readFileSync(path15.join(root, file), "utf8");
14666
14700
  } catch {
14667
14701
  return null;
14668
14702
  }
@@ -14700,7 +14734,7 @@ function detectedChecks(root) {
14700
14734
  }
14701
14735
  function loadCheckConfig(cwd) {
14702
14736
  const root = checkRoot(cwd);
14703
- const file = path14.join(root, CHECKS_FILE);
14737
+ const file = path15.join(root, CHECKS_FILE);
14704
14738
  const none = (error = null) => ({
14705
14739
  source: error ? CHECKS_FILE : "none",
14706
14740
  root,
@@ -14710,7 +14744,7 @@ function loadCheckConfig(cwd) {
14710
14744
  });
14711
14745
  let text = null;
14712
14746
  try {
14713
- text = fs11.readFileSync(file, "utf8");
14747
+ text = fs12.readFileSync(file, "utf8");
14714
14748
  } catch (error) {
14715
14749
  if (error.code !== "ENOENT")
14716
14750
  return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
@@ -14771,10 +14805,10 @@ function workitAvailable(env = process.env) {
14771
14805
  return workitOnPath;
14772
14806
  const windows = process.platform === "win32";
14773
14807
  const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
14774
- const dirs = (env.PATH ?? env.Path ?? "").split(path14.delimiter).filter(Boolean);
14808
+ const dirs = (env.PATH ?? env.Path ?? "").split(path15.delimiter).filter(Boolean);
14775
14809
  workitOnPath = dirs.some((dir) => exts.some((ext) => {
14776
14810
  try {
14777
- return fs11.statSync(path14.join(dir, `workit${ext}`)).isFile();
14811
+ return fs12.statSync(path15.join(dir, `workit${ext}`)).isFile();
14778
14812
  } catch {
14779
14813
  return false;
14780
14814
  }
@@ -14795,7 +14829,7 @@ var SCRIPT_CHECKS, CHECKS_FILE = "workit.checks.json", isRecord3 = (value) => ty
14795
14829
  const value = run.status === 0 ? (run.stdout ?? "").trim() : "";
14796
14830
  return value || null;
14797
14831
  }, packageManager = (root) => {
14798
- const has = (file) => fs11.existsSync(path14.join(root, file));
14832
+ const has = (file) => fs12.existsSync(path15.join(root, file));
14799
14833
  if (has("bun.lock") || has("bun.lockb"))
14800
14834
  return "bun";
14801
14835
  if (has("pnpm-lock.yaml"))
@@ -14818,14 +14852,14 @@ var init_check_config = __esm(() => {
14818
14852
  });
14819
14853
 
14820
14854
  // packages/workit-core/src/core/task-evaluation.ts
14821
- import { createHash as createHash9 } from "node:crypto";
14822
- import * as fs12 from "node:fs";
14855
+ import { createHash as createHash10 } from "node:crypto";
14856
+ import * as fs13 from "node:fs";
14823
14857
  import { spawnSync as spawnSync10 } from "node:child_process";
14824
- import path15 from "node:path";
14858
+ import path16 from "node:path";
14825
14859
  function captureCandidate(root, scope, environment = []) {
14826
14860
  let checkout;
14827
14861
  try {
14828
- checkout = fs12.realpathSync(root);
14862
+ checkout = fs13.realpathSync(root);
14829
14863
  } catch {
14830
14864
  return failure2("invalid_input", "candidate root does not exist");
14831
14865
  }
@@ -14843,11 +14877,11 @@ function captureCandidate(root, scope, environment = []) {
14843
14877
  if (item === ".git" || item === ".workit")
14844
14878
  continue;
14845
14879
  names.add(item);
14846
- if (!fs12.existsSync(target))
14880
+ if (!fs13.existsSync(target))
14847
14881
  continue;
14848
14882
  let stat;
14849
14883
  try {
14850
- stat = fs12.lstatSync(target);
14884
+ stat = fs13.lstatSync(target);
14851
14885
  } catch {
14852
14886
  uncertain = true;
14853
14887
  continue;
@@ -14863,17 +14897,17 @@ function captureCandidate(root, scope, environment = []) {
14863
14897
  for (const item of [...names].toSorted()) {
14864
14898
  if (!scopeMatches(item, normalizedScope))
14865
14899
  continue;
14866
- const target = path15.join(checkout, item);
14900
+ const target = path16.join(checkout, item);
14867
14901
  try {
14868
- const stat = fs12.lstatSync(target);
14902
+ const stat = fs13.lstatSync(target);
14869
14903
  if (stat.isSymbolicLink()) {
14870
- const link = fs12.readlinkSync(target);
14904
+ const link = fs13.readlinkSync(target);
14871
14905
  files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
14872
14906
  } else if (stat.isFile()) {
14873
14907
  files.push({
14874
14908
  path: item,
14875
14909
  kind: "file",
14876
- digest: digestBytes(fs12.readFileSync(target)),
14910
+ digest: digestBytes(fs13.readFileSync(target)),
14877
14911
  executable: (stat.mode & 73) !== 0
14878
14912
  });
14879
14913
  } else if (!stat.isDirectory()) {
@@ -15236,12 +15270,12 @@ function evaluateClosure(requestedOutcome, view) {
15236
15270
  requirementIds: evaluations.map((item) => item.requirementId)
15237
15271
  });
15238
15272
  }
15239
- var digestBytes = (value) => createHash9("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path15.sep}`), relative = (root, candidate) => path15.relative(root, candidate).split(path15.sep).join("/") || ".", canonicalPath = (value) => {
15273
+ var digestBytes = (value) => createHash10("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path16.sep}`), relative = (root, candidate) => path16.relative(root, candidate).split(path16.sep).join("/") || ".", canonicalPath = (value) => {
15240
15274
  if (typeof value !== "string" || !value)
15241
15275
  return null;
15242
15276
  if (value.includes("\\") || value.split("/").includes(".."))
15243
15277
  return null;
15244
- const normalized = path15.posix.normalize(value);
15278
+ const normalized = path16.posix.normalize(value);
15245
15279
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
15246
15280
  return null;
15247
15281
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -15283,20 +15317,20 @@ var digestBytes = (value) => createHash9("sha256").update(value).digest("hex"),
15283
15317
  git: !/not a git repository/i.test(stderr)
15284
15318
  };
15285
15319
  }
15286
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path15.sep).join("/")) : [];
15320
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")) : [];
15287
15321
  const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
15288
15322
  if (stagedDeleted.status !== 0) {
15289
15323
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
15290
15324
  if (!/not a git repository/i.test(stderr))
15291
15325
  return { paths, uncertain: true, git: true };
15292
15326
  } else if (stagedDeleted.stdout) {
15293
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path15.sep).join("/")));
15327
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path16.sep).join("/")));
15294
15328
  }
15295
15329
  return { paths: [...new Set(paths)], uncertain: false, git: true };
15296
15330
  }, walk = (root, directory, output) => {
15297
15331
  let names;
15298
15332
  try {
15299
- names = fs12.readdirSync(directory);
15333
+ names = fs13.readdirSync(directory);
15300
15334
  } catch {
15301
15335
  return true;
15302
15336
  }
@@ -15304,10 +15338,10 @@ var digestBytes = (value) => createHash9("sha256").update(value).digest("hex"),
15304
15338
  for (const name of names) {
15305
15339
  if (name === ".git" || name === ".workit")
15306
15340
  continue;
15307
- const target = path15.join(directory, name);
15341
+ const target = path16.join(directory, name);
15308
15342
  let stat;
15309
15343
  try {
15310
- stat = fs12.lstatSync(target);
15344
+ stat = fs13.lstatSync(target);
15311
15345
  } catch {
15312
15346
  uncertain = true;
15313
15347
  continue;
@@ -15321,14 +15355,14 @@ var digestBytes = (value) => createHash9("sha256").update(value).digest("hex"),
15321
15355
  }, scopeRoots = (root, scope) => {
15322
15356
  const roots = [];
15323
15357
  for (const item of scope.paths.length ? scope.paths : ["."]) {
15324
- const target = path15.resolve(root, item);
15358
+ const target = path16.resolve(root, item);
15325
15359
  if (!inside(root, target))
15326
15360
  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` }] });
15327
15361
  let ancestor = target;
15328
- while (!fs12.existsSync(ancestor) && ancestor !== root)
15329
- ancestor = path15.dirname(ancestor);
15362
+ while (!fs13.existsSync(ancestor) && ancestor !== root)
15363
+ ancestor = path16.dirname(ancestor);
15330
15364
  try {
15331
- if (!inside(root, fs12.realpathSync(ancestor)))
15365
+ if (!inside(root, fs13.realpathSync(ancestor)))
15332
15366
  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` }] });
15333
15367
  } catch {
15334
15368
  return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
@@ -15381,7 +15415,7 @@ var digestBytes = (value) => createHash9("sha256").update(value).digest("hex"),
15381
15415
  }, findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null), verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
15382
15416
  let root;
15383
15417
  try {
15384
- root = fs12.realpathSync(checkoutRoot);
15418
+ root = fs13.realpathSync(checkoutRoot);
15385
15419
  } catch {
15386
15420
  return failure2("permission_denied", "checkout root is unavailable");
15387
15421
  }
@@ -15390,23 +15424,23 @@ var digestBytes = (value) => createHash9("sha256").update(value).digest("hex"),
15390
15424
  continue;
15391
15425
  if (!reference.digest)
15392
15426
  return failure2("invalid_input", "document references require a byte digest");
15393
- const target = path15.resolve(root, reference.path);
15394
- if (target !== root && !target.startsWith(`${root}${path15.sep}`))
15427
+ const target = path16.resolve(root, reference.path);
15428
+ if (target !== root && !target.startsWith(`${root}${path16.sep}`))
15395
15429
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
15396
15430
  fields: [
15397
15431
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
15398
15432
  ]
15399
15433
  });
15400
15434
  try {
15401
- const below = path15.relative(root, target);
15435
+ const below = path16.relative(root, target);
15402
15436
  let current = root;
15403
- for (const segment of below.split(path15.sep)) {
15437
+ for (const segment of below.split(path16.sep)) {
15404
15438
  if (!segment)
15405
15439
  continue;
15406
- current = path15.join(current, segment);
15407
- const stat = fs12.lstatSync(current);
15408
- const real = fs12.realpathSync(current);
15409
- if (real !== root && !real.startsWith(`${root}${path15.sep}`))
15440
+ current = path16.join(current, segment);
15441
+ const stat = fs13.lstatSync(current);
15442
+ const real = fs13.realpathSync(current);
15443
+ if (real !== root && !real.startsWith(`${root}${path16.sep}`))
15410
15444
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
15411
15445
  fields: [
15412
15446
  {
@@ -15422,7 +15456,7 @@ var digestBytes = (value) => createHash9("sha256").update(value).digest("hex"),
15422
15456
  if (current !== target && !stat.isDirectory())
15423
15457
  return failure2("invalid_input", "document reference ancestor is not a directory");
15424
15458
  }
15425
- if (digestBytes(fs12.readFileSync(target)) !== reference.digest)
15459
+ if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
15426
15460
  return failure2("permission_denied", "decision document bytes have changed");
15427
15461
  } catch {
15428
15462
  return failure2("invalid_input", "decision document is unavailable");
@@ -15526,7 +15560,8 @@ Autonomy contract:
15526
15560
  satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
15527
15561
  - Author is not verifier: your own verdict is \`--self\`, shown as
15528
15562
  self-reviewed, never verified. High risk, \`verification: independent\` and
15529
- merge need a verifier with its own session (\`WORKIT_SESSION_ID=<lead>-v<n>\`).
15563
+ merge need a verifier with its own host-derived session (a subagent's
15564
+ \`<lead>:<child>\`, set by the host); never hand-mint one such as \`<lead>-v1\`.
15530
15565
  - A local-commit endpoint does not imply PR readiness, and a local commit alone
15531
15566
  is not evidence of a requested remote push. Before reporting delivery,
15532
15567
  reconcile every requested item and observe it (\`workit verify-delivery\`).
@@ -17361,9 +17396,9 @@ var support = (descriptor, axis) => {
17361
17396
  });
17362
17397
 
17363
17398
  // packages/workit-core/src/hooks/context.ts
17364
- import { createHash as createHash10 } from "node:crypto";
17365
- import fs13 from "node:fs";
17366
- import path16 from "node:path";
17399
+ import { createHash as createHash11 } from "node:crypto";
17400
+ import fs14 from "node:fs";
17401
+ import path17 from "node:path";
17367
17402
  function sessionCompactContext(store, session, context, selection = "session-bound") {
17368
17403
  const listed = store.listTaskIndex();
17369
17404
  if (!listed.ok)
@@ -17398,7 +17433,7 @@ function entryCompactContext(store, entry, session, context) {
17398
17433
  return null;
17399
17434
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
17400
17435
  const observed = task?.data.evidence.some(isObservedCheck) ?? false;
17401
- const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path16.resolve(store.root, ref.path)] : []));
17436
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path17.resolve(store.root, ref.path)] : []));
17402
17437
  const observedFiles = filesKey(files);
17403
17438
  const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
17404
17439
  if (!compact.ok || !compact.data)
@@ -17496,7 +17531,7 @@ ${options.addendum}` : ""}
17496
17531
  } catch {
17497
17532
  return null;
17498
17533
  }
17499
- }, TURN_TTL_MS, digestOf = (text) => createHash10("sha256").update(text).digest("hex"), turnMarker = (input) => {
17534
+ }, TURN_TTL_MS, digestOf = (text) => createHash11("sha256").update(text).digest("hex"), turnMarker = (input) => {
17500
17535
  if (!input.session.id)
17501
17536
  return null;
17502
17537
  try {
@@ -17504,29 +17539,29 @@ ${options.addendum}` : ""}
17504
17539
  if (location instanceof Error)
17505
17540
  return null;
17506
17541
  const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
17507
- return path16.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
17542
+ return path17.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
17508
17543
  } catch {
17509
17544
  return null;
17510
17545
  }
17511
17546
  }, writeTurnDigest = (file, digest) => {
17512
17547
  try {
17513
- fs13.mkdirSync(path16.dirname(file), { recursive: true });
17514
- fs13.writeFileSync(file, `${JSON.stringify({ digest })}
17548
+ fs14.mkdirSync(path17.dirname(file), { recursive: true });
17549
+ fs14.writeFileSync(file, `${JSON.stringify({ digest })}
17515
17550
  `);
17516
17551
  } catch {}
17517
17552
  }, clearTurnDigest = (file) => {
17518
17553
  try {
17519
- fs13.rmSync(file, { force: true });
17554
+ fs14.rmSync(file, { force: true });
17520
17555
  } catch {}
17521
17556
  }, pruneTurnDigests = (dir, now) => {
17522
17557
  try {
17523
- for (const name of fs13.readdirSync(dir)) {
17558
+ for (const name of fs14.readdirSync(dir)) {
17524
17559
  if (!name.startsWith("turn-"))
17525
17560
  continue;
17526
- const file = path16.join(dir, name);
17561
+ const file = path17.join(dir, name);
17527
17562
  try {
17528
- if (now - fs13.statSync(file).mtimeMs > TURN_TTL_MS)
17529
- fs13.rmSync(file, { force: true });
17563
+ if (now - fs14.statSync(file).mtimeMs > TURN_TTL_MS)
17564
+ fs14.rmSync(file, { force: true });
17530
17565
  } catch {}
17531
17566
  }
17532
17567
  } catch {}
@@ -17541,7 +17576,7 @@ ${options.addendum}` : ""}
17541
17576
  return text;
17542
17577
  const digest = digestOf(text);
17543
17578
  try {
17544
- const previous = JSON.parse(fs13.readFileSync(file, "utf8"));
17579
+ const previous = JSON.parse(fs14.readFileSync(file, "utf8"));
17545
17580
  if (previous.digest === digest)
17546
17581
  return null;
17547
17582
  } catch {}
@@ -17551,7 +17586,7 @@ ${options.addendum}` : ""}
17551
17586
  const file = turnMarker(input);
17552
17587
  if (!file)
17553
17588
  return;
17554
- pruneTurnDigests(path16.dirname(file), Date.now());
17589
+ pruneTurnDigests(path17.dirname(file), Date.now());
17555
17590
  if (text)
17556
17591
  writeTurnDigest(file, digestOf(text));
17557
17592
  else
@@ -17577,7 +17612,7 @@ var init_commit_flavors = () => {};
17577
17612
  var init_git = () => {};
17578
17613
 
17579
17614
  // packages/workit-core/src/core/branch.ts
17580
- import { createHash as createHash11 } from "node:crypto";
17615
+ import { createHash as createHash12 } from "node:crypto";
17581
17616
  function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
17582
17617
  const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
17583
17618
  const config = readConfig();
@@ -17612,10 +17647,41 @@ var withReleaseTracks = (workspaceRoot, policy) => {
17612
17647
  ...[...trackBranches(read.tracks), ...read.protectedBranches].map((name) => name.toLowerCase())
17613
17648
  ])
17614
17649
  };
17650
+ }, KIND_SYNONYMS, allowedKinds = (allowed) => [
17651
+ ...new Set(allowed.map((pattern) => /^\^([\w.-]+)\\?\/\.\*\$$/u.exec(pattern.source)?.[1]?.toLowerCase()).filter((kind) => Boolean(kind)))
17652
+ ], editDistance = (a, b) => {
17653
+ const row = Array.from({ length: b.length + 1 }, (_, index) => index);
17654
+ for (let i = 1;i <= a.length; i++) {
17655
+ let previous = row[0];
17656
+ row[0] = i;
17657
+ for (let j = 1;j <= b.length; j++) {
17658
+ const saved = row[j];
17659
+ row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1));
17660
+ previous = saved;
17661
+ }
17662
+ }
17663
+ return row[b.length];
17664
+ }, suggestBranchName = (name, kinds) => {
17665
+ if (!kinds.length)
17666
+ return null;
17667
+ const slash = name.indexOf("/");
17668
+ const prefix = slash > 0 ? name.slice(0, slash).toLowerCase() : "";
17669
+ const rest = (slash > 0 ? name.slice(slash + 1) : name) || "name";
17670
+ const synonym = KIND_SYNONYMS[prefix];
17671
+ 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;
17672
+ const kind = (synonym && kinds.includes(synonym) ? synonym : null) ?? near ?? (kinds.includes("feature") ? "feature" : kinds[0]);
17673
+ return `${kind}/${rest}`;
17674
+ }, kindList = (kinds) => kinds.map((kind) => `${kind}/`).join(", "), aliasNote = (kinds) => {
17675
+ const notes = [
17676
+ ["fix", "bugfix"],
17677
+ ["chore", "feature"]
17678
+ ].filter(([, kind]) => kinds.includes(kind)).map(([alias, kind]) => `${alias}/ is ${kind}/`);
17679
+ return notes.length ? ` (${notes.join(", ")})` : "";
17615
17680
  }, validateBranchNamePolicy = (name, policy, source) => {
17616
17681
  const allowed = policy.allowed.map((pattern) => `/${pattern.source}/${pattern.flags}`);
17682
+ const kinds = allowedKinds(policy.allowed);
17617
17683
  if (policy.protected.has(name.toLowerCase())) {
17618
- const correction = `choose a non-protected branch matching one of: ${allowed.join(", ")}`;
17684
+ 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(", ")}`;
17619
17685
  return {
17620
17686
  ok: false,
17621
17687
  rule: "protected_ref",
@@ -17626,7 +17692,8 @@ var withReleaseTracks = (workspaceRoot, policy) => {
17626
17692
  };
17627
17693
  }
17628
17694
  if (!policy.allowed.some((pattern) => pattern.test(name))) {
17629
- const correction = `choose a branch matching one of: ${allowed.join(", ")}`;
17695
+ const suggestion = suggestBranchName(name, kinds);
17696
+ 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(", ")}`;
17630
17697
  return {
17631
17698
  ok: false,
17632
17699
  rule: "allowed_pattern",
@@ -17643,7 +17710,7 @@ var withReleaseTracks = (workspaceRoot, policy) => {
17643
17710
  const source = resolved.provenance.branchPolicy;
17644
17711
  const allowed = branchPolicy.allowed.map((pattern) => `${pattern.source}/${pattern.flags}`).toSorted();
17645
17712
  const protectedRefs = [...branchPolicy.protected].map((name) => name.toLowerCase()).toSorted();
17646
- const fingerprint = createHash11("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
17713
+ const fingerprint = createHash12("sha256").update(JSON.stringify({ allowed, protectedRefs, source })).digest("hex");
17647
17714
  return {
17648
17715
  branchPolicy,
17649
17716
  namePolicy: { allowed: branchPolicy.allowed, protected: branchPolicy.protected },
@@ -17665,7 +17732,52 @@ var withReleaseTracks = (workspaceRoot, policy) => {
17665
17732
  error: `policy_unavailable: cannot validate branch ${JSON.stringify(name)} because ${error instanceof Error ? error.message : String(error)}; ${correction}`
17666
17733
  };
17667
17734
  }
17668
- }, resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
17735
+ }, resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy, gitFlowOf = (root, integration) => {
17736
+ try {
17737
+ if (resolveBranchPolicyFor(root).preset !== "gitflow")
17738
+ return null;
17739
+ const selected = resolveRuntimeWorkspacePolicy(root, "branch", process.env.WORKFLOW_PROFILE?.trim() || undefined).policy;
17740
+ const names = branchNames(root);
17741
+ const production = productionBranchOf(names);
17742
+ const develop = selected?.developBranch?.trim() || (integration && integration.toLowerCase() !== production ? integration : "develop");
17743
+ return { develop, production, developExists: names.has(develop) };
17744
+ } catch {
17745
+ return null;
17746
+ }
17747
+ }, releaseBranchesOf = (cwd) => {
17748
+ try {
17749
+ const resolved = resolveBranchPolicyFor(cwd);
17750
+ const flow = resolved.preset === "gitflow" ? gitFlowOf(cwd) : null;
17751
+ return {
17752
+ preset: resolved.preset,
17753
+ branches: flow ? [flow.production, flow.develop] : [],
17754
+ production: flow ? flow.production : resolved.defaultTargetBranch
17755
+ };
17756
+ } catch {
17757
+ return { preset: "unknown", branches: [], production: null };
17758
+ }
17759
+ }, releaseBranch = (cwd, branch) => releaseBranchesOf(cwd).branches.some((name) => name.toLowerCase() === branch.toLowerCase()), branchGuidance = (cwd) => {
17760
+ try {
17761
+ const patterns = branchPolicySnapshotFor(cwd).namePolicy.allowed;
17762
+ const kinds = allowedKinds(patterns);
17763
+ const kindShaped = new Set(kinds.map((kind) => branchGlobPattern(`${kind}/*`).source));
17764
+ const anyName = patterns.some((pattern) => pattern.source === "^.*$");
17765
+ const others = patterns.filter((pattern) => !kindShaped.has(pattern.source)).map((pattern) => globOf(pattern));
17766
+ 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.`;
17767
+ const resolved = vcsConfig("resolve", cwd);
17768
+ const integration = resolved.ok === false ? null : String(resolved.defaultTargetBranch ?? "") || null;
17769
+ const flow = resolved.releaseTrack?.track ? null : gitFlowOf(cwd, integration);
17770
+ if (flow?.developExists)
17771
+ 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}.`;
17772
+ const names = branchNames(cwd);
17773
+ const configured = String(resolved.baseBranch ?? integration ?? "") || null;
17774
+ const trunk = productionBranchOf(names);
17775
+ 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.";
17776
+ return `${allowed} ${base}`;
17777
+ } catch {
17778
+ return null;
17779
+ }
17780
+ }, globOf = (pattern) => pattern.source.replace(/^\^|\$$/gu, "").replaceAll(".*", "*").replace(/\\(.)/gu, "$1");
17669
17781
  var init_branch = __esm(() => {
17670
17782
  init_branch_policy();
17671
17783
  init_commit_flavors();
@@ -17674,33 +17786,63 @@ var init_branch = __esm(() => {
17674
17786
  init_workspaces();
17675
17787
  init_vcs_config();
17676
17788
  init_release_tracks();
17789
+ KIND_SYNONYMS = {
17790
+ fix: "bugfix",
17791
+ fixes: "bugfix",
17792
+ bug: "bugfix",
17793
+ bugs: "bugfix",
17794
+ bugfixes: "bugfix",
17795
+ feat: "feature",
17796
+ features: "feature",
17797
+ chore: "feature",
17798
+ docs: "feature",
17799
+ doc: "feature",
17800
+ refactor: "feature",
17801
+ test: "feature",
17802
+ tests: "feature",
17803
+ ci: "feature",
17804
+ build: "feature",
17805
+ perf: "feature",
17806
+ style: "feature",
17807
+ deps: "feature",
17808
+ task: "feature",
17809
+ enhancement: "feature",
17810
+ hot: "hotfix",
17811
+ hotfixes: "hotfix",
17812
+ releases: "release",
17813
+ rel: "release"
17814
+ };
17677
17815
  });
17678
17816
 
17679
- // packages/workit-core/src/core/route-intent.ts
17680
- var shellBranchTarget = (command) => {
17681
- if (!command.trim() || /[\r\n;&|<>`'"$()\\*?{}~!#]/u.test(command) || command.includes("[") || command.includes("]"))
17682
- return null;
17683
- const args = command.trim().split(/\s+/u);
17684
- if (args[0] !== "git")
17685
- return null;
17686
- const option = args[2];
17687
- 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;
17688
- return target && !target.startsWith("-") ? target : null;
17689
- }, shellBranchPolicyViolation = (root, command) => {
17690
- const target = shellBranchTarget(command);
17691
- return target === null ? null : validateBranchNameFor(root, target);
17692
- };
17693
- var init_route_intent = __esm(() => {
17694
- init_branch();
17695
- });
17696
-
17697
- // packages/workit-core/src/hooks/policy.ts
17698
- var shellPolicy = (cwd, command) => {
17699
- const policy = shellBranchPolicyViolation(cwd, command);
17700
- return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
17817
+ // packages/workit-core/src/hooks/session-marker.ts
17818
+ import { createHash as createHash13 } from "node:crypto";
17819
+ import fs15 from "node:fs";
17820
+ import path18 from "node:path";
17821
+ var sessionMarkerFile = (input, session, store) => {
17822
+ let dir = store;
17823
+ if (dir === undefined) {
17824
+ const location = resolveStore(input.cwd);
17825
+ if (location instanceof Error || !location.shared)
17826
+ return null;
17827
+ dir = location.dir;
17828
+ }
17829
+ const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
17830
+ return path18.join(dir, "hooks", `skills-${name}.json`);
17831
+ }, list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [], readSessionMarker = (file) => {
17832
+ try {
17833
+ const value = JSON.parse(fs15.readFileSync(file, "utf8"));
17834
+ return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
17835
+ } catch {
17836
+ return { nudged: [], loaded: [], raw: [] };
17837
+ }
17838
+ }, writeSessionMarker = (file, marker) => {
17839
+ try {
17840
+ fs15.mkdirSync(path18.dirname(file), { recursive: true });
17841
+ fs15.writeFileSync(file, JSON.stringify(marker));
17842
+ } catch {}
17701
17843
  };
17702
- var init_policy = __esm(() => {
17703
- init_route_intent();
17844
+ var init_session_marker = __esm(() => {
17845
+ init_paths();
17704
17846
  });
17705
17847
 
17706
17848
  // packages/workit-core/src/hooks/shell-words.ts
@@ -17824,234 +17966,27 @@ var closingQuote = (command, start, dialect) => {
17824
17966
  return segments;
17825
17967
  };
17826
17968
 
17827
- // packages/workit-core/src/hooks/write-gate.ts
17828
- import { realpathSync as realpathSync5 } from "node:fs";
17829
- import path17 from "node:path";
17830
- function writeGate(cwd, paths) {
17831
- try {
17832
- const gate = gateFor(cwd);
17833
- if (!gate || gate.blockers.length === 0)
17834
- return NONE2;
17835
- if (paths?.length && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
17836
- return NONE2;
17837
- const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
17838
- return {
17839
- kind: "deny",
17840
- reason: `workit before-write gate: ${reasons}. Markdown, docs/, plans/ and files outside the checkout stay writable.`,
17841
- unblock: gate.blockers[0].reason
17842
- };
17843
- } catch {
17844
- return NONE2;
17845
- }
17846
- }
17847
- var NONE2, CASE_INSENSITIVE, canonical2 = (file) => {
17848
- let rest = "";
17849
- for (let dir = path17.resolve(file);; dir = path17.dirname(dir)) {
17850
- try {
17851
- return path17.join(realpathSync5(dir), rest);
17852
- } catch {
17853
- if (path17.dirname(dir) === dir)
17854
- return path17.resolve(file);
17855
- rest = rest ? path17.join(path17.basename(dir), rest) : path17.basename(dir);
17856
- }
17857
- }
17858
- }, exempt = (cwd, root, file, plans) => {
17859
- const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
17860
- const relative = path17.relative(fold(canonical2(root)), fold(canonical2(path17.resolve(cwd, file))));
17861
- if (relative.startsWith("..") || path17.isAbsolute(relative))
17862
- return true;
17863
- const posix = relative.split(path17.sep).join("/");
17864
- const segments = posix.split("/");
17865
- return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
17866
- }, PREFIXES2, operands = (words) => words.filter((item) => !item.startsWith("-")), editsOf = (input) => {
17867
- let words = input;
17868
- while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
17869
- words = words.slice(1);
17870
- const [verb, ...args] = words;
17871
- const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
17872
- if (!name || name === "workit" || name === "npx" && args.some((item) => /workit/.test(item)))
17873
- return [];
17874
- switch (name) {
17875
- case "touch":
17876
- case "mkdir":
17877
- case "rm":
17878
- case "rmdir":
17879
- case "tee":
17880
- case "mv":
17881
- case "truncate":
17882
- return operands(args);
17883
- case "cp":
17884
- case "ln":
17885
- case "install": {
17886
- const target = args.indexOf("-t");
17887
- if (target >= 0)
17888
- return args[target + 1] ? [args[target + 1]] : null;
17889
- const last = operands(args).at(-1);
17890
- return last ? [last] : null;
17969
+ // packages/workit-core/src/hooks/raw-git.ts
17970
+ import { spawnSync as spawnSync11 } from "node:child_process";
17971
+ import { createHash as createHash14 } from "node:crypto";
17972
+ import fs16 from "node:fs";
17973
+ import os4 from "node:os";
17974
+ import path19 from "node:path";
17975
+ function rawInvocations(command, dialect = "posix", depth = 0) {
17976
+ const found = [];
17977
+ let dir = null;
17978
+ const groups = [];
17979
+ let stack = [];
17980
+ for (const segment of segmentsOf(command, dialect)) {
17981
+ if (segment.group === "open") {
17982
+ groups.push({ dir, stack: [...stack] });
17983
+ continue;
17891
17984
  }
17892
- case "dd":
17893
- return args.filter((item) => item.startsWith("of=")).map((item) => item.slice(3));
17894
- case "sed":
17895
- case "perl": {
17896
- if (!args.some((item) => /^-[a-zA-Z]*i|^--in-place/.test(item)))
17897
- return [];
17898
- const scripted = args.some((item) => item === "-e" || item === "-f");
17899
- const files = [];
17900
- for (let index = 0;index < args.length; index++) {
17901
- if (args[index] === "-e" || args[index] === "-f")
17902
- index++;
17903
- else if (!args[index].startsWith("-"))
17904
- files.push(args[index]);
17905
- }
17906
- return scripted ? files : files.slice(1);
17907
- }
17908
- case "patch":
17909
- return null;
17910
- case "set-content":
17911
- case "add-content":
17912
- case "out-file":
17913
- case "new-item":
17914
- case "ni":
17915
- case "remove-item":
17916
- case "del":
17917
- case "copy-item":
17918
- case "move-item": {
17919
- const flag = args.findIndex((item) => /^-(?:path|filepath|literalpath|destination)$/i.test(item));
17920
- if (flag >= 0)
17921
- return args[flag + 1] ? [args[flag + 1]] : null;
17922
- const files = operands(args);
17923
- if (name === "move-item")
17924
- return files.length ? files : null;
17925
- const file = name === "copy-item" ? files.at(-1) : files[0];
17926
- return file ? [file] : null;
17927
- }
17928
- case "git": {
17929
- const sub = operands(args)[0];
17930
- if (sub === "apply")
17931
- return null;
17932
- if (sub === "restore" || sub === "mv" || sub === "rm")
17933
- return operands(args).slice(1);
17934
- if (sub === "checkout" && args.includes("--"))
17935
- return args.slice(args.indexOf("--") + 1);
17936
- return [];
17937
- }
17938
- default:
17939
- return [];
17940
- }
17941
- }, shellWrites = (command) => {
17942
- const targets = [];
17943
- let unknown = false;
17944
- for (const segment of segmentsOf(command)) {
17945
- targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
17946
- const edits = editsOf(segment.words);
17947
- if (edits === null)
17948
- unknown = true;
17949
- else
17950
- targets.push(...edits);
17951
- }
17952
- if (!unknown && targets.length === 0)
17953
- return { writes: false, targets: null };
17954
- return { writes: true, targets: unknown ? null : targets };
17955
- }, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path17.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
17956
- const store = new TaskStore(cwd);
17957
- const listed = store.listTaskIndex();
17958
- if (!listed.ok)
17959
- return null;
17960
- const key = store.currentKey();
17961
- if (!key.ok)
17962
- return null;
17963
- const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
17964
- if (!entry)
17965
- return { blockers: [], root: store.root, plans: new Set };
17966
- const ledger = ledgerPath(store.root);
17967
- const cacheKey = `${entry.id}\x00${entry.revision}\x00${ledger.ok && fileSignature(ledger.value) || "-"}`;
17968
- const hit = cache2.get(store.root);
17969
- if (hit && hit.key === cacheKey && hit.planKey === planKeyOf(store.root, hit.plans))
17970
- return hit;
17971
- const task = store.readTask(entry.id);
17972
- if (!task.ok)
17973
- return null;
17974
- const workspace = store.readWorkspace();
17975
- const plans = new Set((latestJudgment(task.data)?.refs ?? []).flatMap((ref) => ref.kind === "file" ? [ref.path] : []));
17976
- const blockers = writeBlockers(task.data, store.root).filter((blocker) => {
17977
- const requirement = task.data.policy?.requirements.find((item) => item.ruleId === blocker.ruleId && item.before === "write");
17978
- return !(requirement?.acceptanceAllowed && workspace.ok && workspace.data && applicableDecision(task.data, workspace.data, requirement, store.root).length > 0);
17979
- });
17980
- const gate = {
17981
- blockers,
17982
- root: store.root,
17983
- plans,
17984
- key: cacheKey,
17985
- planKey: planKeyOf(store.root, plans)
17986
- };
17987
- cache2.set(store.root, gate);
17988
- if (cache2.size > 32)
17989
- cache2.delete(cache2.keys().next().value);
17990
- return gate;
17991
- };
17992
- var init_write_gate = __esm(() => {
17993
- init_task_store();
17994
- init_derive();
17995
- init_task_evaluation();
17996
- init_ledger();
17997
- NONE2 = { kind: "none" };
17998
- CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
17999
- PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
18000
- cache2 = new Map;
18001
- });
18002
-
18003
- // packages/workit-core/src/hooks/session-marker.ts
18004
- import { createHash as createHash12 } from "node:crypto";
18005
- import fs14 from "node:fs";
18006
- import path18 from "node:path";
18007
- var sessionMarkerFile = (input, session, store) => {
18008
- let dir = store;
18009
- if (dir === undefined) {
18010
- const location = resolveStore(input.cwd);
18011
- if (location instanceof Error || !location.shared)
18012
- return null;
18013
- dir = location.dir;
18014
- }
18015
- const name = createHash12("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
18016
- return path18.join(dir, "hooks", `skills-${name}.json`);
18017
- }, list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [], readSessionMarker = (file) => {
18018
- try {
18019
- const value = JSON.parse(fs14.readFileSync(file, "utf8"));
18020
- return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
18021
- } catch {
18022
- return { nudged: [], loaded: [], raw: [] };
18023
- }
18024
- }, writeSessionMarker = (file, marker) => {
18025
- try {
18026
- fs14.mkdirSync(path18.dirname(file), { recursive: true });
18027
- fs14.writeFileSync(file, JSON.stringify(marker));
18028
- } catch {}
18029
- };
18030
- var init_session_marker = __esm(() => {
18031
- init_paths();
18032
- });
18033
-
18034
- // packages/workit-core/src/hooks/raw-git.ts
18035
- import { spawnSync as spawnSync11 } from "node:child_process";
18036
- import { createHash as createHash13 } from "node:crypto";
18037
- import fs15 from "node:fs";
18038
- import os4 from "node:os";
18039
- import path19 from "node:path";
18040
- function rawInvocations(command, dialect = "posix", depth = 0) {
18041
- const found = [];
18042
- let dir = null;
18043
- const groups = [];
18044
- let stack = [];
18045
- for (const segment of segmentsOf(command, dialect)) {
18046
- if (segment.group === "open") {
18047
- groups.push({ dir, stack: [...stack] });
18048
- continue;
18049
- }
18050
- if (segment.group === "close") {
18051
- const saved = groups.pop();
18052
- if (saved)
18053
- ({ dir, stack } = saved);
18054
- continue;
17985
+ if (segment.group === "close") {
17986
+ const saved = groups.pop();
17987
+ if (saved)
17988
+ ({ dir, stack } = saved);
17989
+ continue;
18055
17990
  }
18056
17991
  const words = stripPrefixes(segment.words);
18057
17992
  const name = commandName(words[0]);
@@ -18146,7 +18081,7 @@ function rawAction(invocation) {
18146
18081
  function rawGitPre(input, command, options = {}) {
18147
18082
  try {
18148
18083
  if (!mentionsRawTool(command))
18149
- return NONE3;
18084
+ return NONE2;
18150
18085
  const dialect = dialectOf(input);
18151
18086
  const nudges = new Map;
18152
18087
  let store = null;
@@ -18154,25 +18089,36 @@ function rawGitPre(input, command, options = {}) {
18154
18089
  store ??= repo.location.dir;
18155
18090
  if (action.kind === "merge")
18156
18091
  return deny(refusal(action, null));
18157
- if (action.kind === "push")
18092
+ if (action.kind === "push" && toPushRemote(repo, action.remote)) {
18093
+ if (action.mirror)
18094
+ 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")}.`);
18158
18095
  for (const target of action.targets) {
18159
18096
  const branch = target ?? repo.branch;
18160
- if (branch && guardedBranch(repo.dir, branch) && toPushRemote(repo, action.remote))
18161
- return deny(refusal(action, branch));
18097
+ if (branch && guardedBranch(repo.dir, branch))
18098
+ return deny(refusal(action, branch, repo.dir));
18162
18099
  }
18100
+ const remote = action.remote ?? pushRemoteName(repo.dir, repo.branch) ?? "origin";
18101
+ const facts = refFacts(repo, remote);
18102
+ const tagDeny = tagRefusal(action, facts);
18103
+ if (tagDeny)
18104
+ return deny(tagDeny);
18105
+ const nameDeny = action.deletes ? null : offPolicyPush(action, repo, facts, remote);
18106
+ if (nameDeny)
18107
+ return deny(nameDeny);
18108
+ }
18163
18109
  const nudge = nudgeFor(action, input.host, input.session.id);
18164
18110
  if (nudge && !nudges.has(nudgeKind(action)))
18165
18111
  nudges.set(nudgeKind(action), nudge);
18166
18112
  }
18167
18113
  if (options.nudge === false || !store || nudges.size === 0)
18168
- return NONE3;
18114
+ return NONE2;
18169
18115
  if (workitInvocations(command, dialect).length > 0)
18170
- return NONE3;
18116
+ return NONE2;
18171
18117
  const shown = unseenNudges(input, nudges, store);
18172
18118
  return shown.length ? { kind: "context", text: shown.join(`
18173
- `) } : NONE3;
18119
+ `) } : NONE2;
18174
18120
  } catch {
18175
- return NONE3;
18121
+ return NONE2;
18176
18122
  }
18177
18123
  }
18178
18124
  function noteRawCommit(input, command, key) {
@@ -18184,8 +18130,8 @@ function noteRawCommit(input, command, key) {
18184
18130
  if (action.kind !== "commit")
18185
18131
  continue;
18186
18132
  const file = markerFile(repo, input.session.id, key);
18187
- fs15.mkdirSync(path19.dirname(file), { recursive: true });
18188
- fs15.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
18133
+ fs16.mkdirSync(path19.dirname(file), { recursive: true });
18134
+ fs16.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
18189
18135
  `);
18190
18136
  }
18191
18137
  } catch {}
@@ -18207,11 +18153,11 @@ function settlePendingCommit(input) {
18207
18153
  if (!repo)
18208
18154
  return;
18209
18155
  const file = markerFile(repo, input.session.id, NEXT_COMMAND);
18210
- if (fs15.existsSync(file))
18156
+ if (fs16.existsSync(file))
18211
18157
  settle2(input, repo, file);
18212
18158
  } catch {}
18213
18159
  }
18214
- var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, isAbsolute = (value) => path19.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\"), joinDir = (base, next) => {
18160
+ var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, isAbsolute = (value) => path19.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\"), joinDir = (base, next) => {
18215
18161
  if (base === undefined || /[$`*?]/.test(next) || next === "-")
18216
18162
  return;
18217
18163
  const expanded = next === "~" || next.startsWith("~/") ? path19.join(os4.homedir(), next.slice(1)) : next;
@@ -18230,7 +18176,7 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18230
18176
  return words;
18231
18177
  if (/^\w+=/.test(first))
18232
18178
  words = words.slice(1);
18233
- else if (PREFIXES3.has(first)) {
18179
+ else if (PREFIXES2.has(first)) {
18234
18180
  words = words.slice(1);
18235
18181
  while (words[0]?.startsWith("-"))
18236
18182
  words = words.slice(words[0] === "-u" ? 2 : 1);
@@ -18246,6 +18192,10 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18246
18192
  let force = false;
18247
18193
  let lease = false;
18248
18194
  let wholesale = false;
18195
+ let allTags = false;
18196
+ let deletes = false;
18197
+ let allBranches = false;
18198
+ let mirror = false;
18249
18199
  let remote = null;
18250
18200
  const positionals = [];
18251
18201
  for (let index = 1;index < args.length; index++) {
@@ -18266,14 +18216,22 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18266
18216
  force = true;
18267
18217
  else if (arg.startsWith("--force-with-lease") || arg === "--force-if-includes")
18268
18218
  lease = true;
18269
- else if (["--all", "--mirror", "--branches", "--tags"].includes(arg))
18270
- wholesale = true;
18219
+ else if (arg === "--delete" || arg === "-d")
18220
+ deletes = true;
18221
+ else if (arg === "--tags")
18222
+ allTags = wholesale = true;
18223
+ else if (arg === "--all" || arg === "--branches")
18224
+ allBranches = wholesale = true;
18225
+ else if (arg === "--mirror")
18226
+ mirror = wholesale = true;
18271
18227
  else if (!arg.startsWith("-"))
18272
18228
  positionals.push(arg);
18273
18229
  }
18274
18230
  const specs = remote === null ? positionals.slice(1) : positionals;
18275
18231
  remote ??= positionals[0] ?? null;
18276
18232
  const targets = [];
18233
+ const tags = [];
18234
+ const deleted = [];
18277
18235
  for (const raw of specs) {
18278
18236
  if (raw.startsWith("+"))
18279
18237
  force = true;
@@ -18282,16 +18240,29 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18282
18240
  const destination = colon >= 0 ? spec.slice(colon + 1) : spec;
18283
18241
  if (!destination)
18284
18242
  continue;
18285
- if (destination === "HEAD")
18286
- targets.push(null);
18287
- else if (destination.startsWith("refs/heads/"))
18288
- targets.push(destination.slice(11));
18289
- else if (!destination.startsWith("refs/"))
18290
- targets.push(destination);
18243
+ const name = destination === "HEAD" ? null : destination.startsWith("refs/heads/") ? destination.slice(11) : destination.startsWith("refs/") ? undefined : destination;
18244
+ if (destination.startsWith("refs/tags/"))
18245
+ tags.push(destination.slice(10));
18246
+ if (name === undefined)
18247
+ continue;
18248
+ targets.push(name);
18249
+ if (colon === 0 && name !== null)
18250
+ deleted.push(name);
18291
18251
  }
18292
18252
  if (specs.length === 0 && !wholesale)
18293
18253
  targets.push(null);
18294
- return { kind: "push", targets, blindForce: force && !lease, remote };
18254
+ return {
18255
+ kind: "push",
18256
+ targets,
18257
+ blindForce: force && !lease,
18258
+ remote,
18259
+ ...tags.length ? { tags } : {},
18260
+ ...allTags ? { allTags: true } : {},
18261
+ ...deletes ? { deletes: true } : {},
18262
+ ...deleted.length ? { deleted } : {},
18263
+ ...allBranches ? { allBranches: true } : {},
18264
+ ...mirror ? { mirror: true } : {}
18265
+ };
18295
18266
  }, VALUE_OPTIONS, asksHelp = (invocation) => {
18296
18267
  const { tool, args } = invocation;
18297
18268
  if (args[0] === "help" || tool !== "git" && args[1] === "help")
@@ -18313,7 +18284,7 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18313
18284
  return null;
18314
18285
  return { location, dir, branch: resolveTaskKey(dir, location).branch };
18315
18286
  }, isWorkitWorkspace = (repo) => {
18316
- if (fs15.existsSync(repo.location.dir))
18287
+ if (fs16.existsSync(repo.location.dir))
18317
18288
  return true;
18318
18289
  try {
18319
18290
  return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
@@ -18341,7 +18312,7 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18341
18312
  if (!git || git.reftable)
18342
18313
  return null;
18343
18314
  try {
18344
- const head = fs15.readFileSync(path19.join(git.gitDir, "HEAD"), "utf8").trim();
18315
+ const head = fs16.readFileSync(path19.join(git.gitDir, "HEAD"), "utf8").trim();
18345
18316
  if (/^[0-9a-f]{40,64}$/.test(head))
18346
18317
  return head;
18347
18318
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
@@ -18349,17 +18320,64 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18349
18320
  return null;
18350
18321
  const common = path19.dirname(repo.location.dir);
18351
18322
  try {
18352
- return fs15.readFileSync(path19.join(common, ref), "utf8").trim() || null;
18323
+ return fs16.readFileSync(path19.join(common, ref), "utf8").trim() || null;
18353
18324
  } catch {}
18354
- const packed = fs15.readFileSync(path19.join(common, "packed-refs"), "utf8");
18325
+ const packed = fs16.readFileSync(path19.join(common, "packed-refs"), "utf8");
18355
18326
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
18356
18327
  } catch {
18357
18328
  return null;
18358
18329
  }
18359
- }, quote = (text) => `\`${text}\``, refusal = (action, branch) => {
18330
+ }, quote = (text) => `\`${text}\``, refusal = (action, branch, dir) => {
18360
18331
  if (action.kind === "merge")
18361
18332
  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")}).`;
18333
+ const plainPush = action.kind === "push" && !action.blindForce && !action.deletes;
18334
+ if (dir && branch && plainPush && releaseBranch(dir, branch))
18335
+ 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")}.`;
18362
18336
  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")}.`;
18337
+ }, refFacts = (repo, remote) => {
18338
+ const facts = { heads: new Set, tags: new Set, remoteHeads: new Set };
18339
+ const listed = spawnSync11("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/tags", `refs/remotes/${remote}`], { cwd: repo.dir, encoding: "utf8", timeout: 5000 });
18340
+ const remotePrefix = `refs/remotes/${remote}/`;
18341
+ for (const ref of (listed.stdout ?? "").split(`
18342
+ `)) {
18343
+ if (ref.startsWith("refs/heads/"))
18344
+ facts.heads.add(ref.slice(11));
18345
+ else if (ref.startsWith("refs/tags/"))
18346
+ facts.tags.add(ref.slice(10));
18347
+ else if (ref.startsWith(remotePrefix))
18348
+ facts.remoteHeads.add(ref.slice(remotePrefix.length));
18349
+ }
18350
+ return facts;
18351
+ }, isTag = (facts, name) => facts.tags.has(name) && !facts.heads.has(name), tagRefusal = (action, facts) => {
18352
+ const tags = [
18353
+ ...action.tags ?? [],
18354
+ ...action.targets.filter((name) => name !== null && isTag(facts, name))
18355
+ ];
18356
+ if (!tags.length && !action.allTags)
18357
+ return null;
18358
+ const named = tags.length ? tags.join(" --release ") : "v<version>";
18359
+ 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")}).`;
18360
+ }, remoteHas = (repo, remote, branch) => {
18361
+ const listed = spawnSync11("git", ["ls-remote", "--heads", remote, `refs/heads/${branch}`], {
18362
+ cwd: repo.dir,
18363
+ encoding: "utf8",
18364
+ timeout: 3000,
18365
+ env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
18366
+ });
18367
+ return listed.status !== 0 || (listed.stdout ?? "").trim() !== "";
18368
+ }, offPolicyPush = (action, repo, facts, remote) => {
18369
+ const names = action.allBranches ? [...facts.heads] : action.targets.map((target) => target ?? repo.branch);
18370
+ for (const branch of names) {
18371
+ if (!branch || action.deleted?.includes(branch) || isTag(facts, branch))
18372
+ continue;
18373
+ if (facts.remoteHeads.has(branch))
18374
+ continue;
18375
+ const check = validateBranchNameFor(repo.dir, branch);
18376
+ if (check.ok || check.rule !== "allowed_pattern" || remoteHas(repo, remote, branch))
18377
+ continue;
18378
+ return `workit: raw ${quote("git push")} onto ${branch}: ${check.error}`;
18379
+ }
18380
+ return null;
18363
18381
  }, nudgeFor = (action, host, session) => {
18364
18382
  switch (action.kind) {
18365
18383
  case "commit": {
@@ -18411,7 +18429,7 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18411
18429
  raw: [...marker.raw, ...fresh.map((kind) => agent + kind)]
18412
18430
  });
18413
18431
  return fresh.map((kind) => nudges.get(kind));
18414
- }, deny = (reason) => ({ kind: "deny", reason, unblock: null }), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) => path19.join(repo.location.dir, "hooks", `commit-${createHash13("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`), callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash13("sha256").update(command).digest("hex")}`, headCommit = (dir) => {
18432
+ }, deny = (reason) => ({ kind: "deny", reason, unblock: null }), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) => path19.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`), callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`, headCommit = (dir) => {
18415
18433
  const run = spawnSync11("git", [
18416
18434
  "log",
18417
18435
  "-1",
@@ -18428,11 +18446,11 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
18428
18446
  }, settle2 = (input, repo, file) => {
18429
18447
  let marker;
18430
18448
  try {
18431
- marker = JSON.parse(fs15.readFileSync(file, "utf8"));
18449
+ marker = JSON.parse(fs16.readFileSync(file, "utf8"));
18432
18450
  } catch {
18433
18451
  return;
18434
18452
  }
18435
- fs15.rmSync(file, { force: true });
18453
+ fs16.rmSync(file, { force: true });
18436
18454
  const session = input.session.id;
18437
18455
  const now = Date.now() / 1000;
18438
18456
  if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
@@ -18471,9 +18489,9 @@ var init_raw_git = __esm(() => {
18471
18489
  init_ledger();
18472
18490
  init_paths();
18473
18491
  init_session_marker();
18474
- NONE3 = { kind: "none" };
18492
+ NONE2 = { kind: "none" };
18475
18493
  SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
18476
- PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
18494
+ PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
18477
18495
  SCRIPT_FLAG = /^-[a-zA-Z]*c[a-zA-Z]*$/;
18478
18496
  CD = new Set(["cd", "chdir", "set-location", "sl"]);
18479
18497
  PUSHD = new Set(["pushd", "push-location"]);
@@ -18531,8 +18549,298 @@ var init_raw_git = __esm(() => {
18531
18549
  };
18532
18550
  });
18533
18551
 
18534
- // packages/workit-core/src/hooks/stop.ts
18552
+ // packages/workit-core/src/core/route-intent.ts
18535
18553
  import { spawnSync as spawnSync12 } from "node:child_process";
18554
+ import { existsSync as existsSync6 } from "node:fs";
18555
+ import path20 from "node:path";
18556
+ var UNKNOWABLE, SWITCH_CREATE, CHECKOUT_CREATE, WORKTREE_CREATE, BRANCH_NO_CREATE, BRANCH_RENAME, flagValue = (args, flags) => {
18557
+ for (let index = 1;index < args.length; index++) {
18558
+ const arg = args[index];
18559
+ if (arg === "--")
18560
+ return null;
18561
+ if (flags.has(arg))
18562
+ return args[index + 1] ?? null;
18563
+ const eq = arg.indexOf("=");
18564
+ if (arg.startsWith("--") && eq > 0 && flags.has(arg.slice(0, eq)))
18565
+ return arg.slice(eq + 1);
18566
+ if (!arg.startsWith("--") && arg.length > 2 && flags.has(arg.slice(0, 2)))
18567
+ return arg.slice(2);
18568
+ }
18569
+ return null;
18570
+ }, WORKTREE_VALUES, worktreeTarget = (args) => {
18571
+ const named = flagValue(args, WORKTREE_CREATE);
18572
+ if (named !== null)
18573
+ return named;
18574
+ const positionals = [];
18575
+ for (let index = 1;index < args.length; index++) {
18576
+ const arg = args[index];
18577
+ if (arg === "--detach" || arg === "-d")
18578
+ return null;
18579
+ if (WORKTREE_VALUES.has(arg))
18580
+ index++;
18581
+ else if (!arg.startsWith("-"))
18582
+ positionals.push(arg);
18583
+ }
18584
+ if (positionals.length !== 1)
18585
+ return null;
18586
+ const base = positionals[0].split(/[\\/]/u).findLast(Boolean) ?? "";
18587
+ return base && base !== "." && base !== ".." ? base : null;
18588
+ }, branchCommandTarget = (args) => {
18589
+ let rename = false;
18590
+ const positionals = [];
18591
+ for (let index = 1;index < args.length; index++) {
18592
+ const arg = args[index];
18593
+ if (arg === "--") {
18594
+ positionals.push(...args.slice(index + 1));
18595
+ break;
18596
+ }
18597
+ const flag = arg.split("=", 1)[0];
18598
+ if (BRANCH_NO_CREATE.has(flag))
18599
+ return null;
18600
+ if (BRANCH_RENAME.has(flag))
18601
+ rename = true;
18602
+ else if (!arg.startsWith("-"))
18603
+ positionals.push(arg);
18604
+ }
18605
+ if (positionals.length < 1 || positionals.length > 2)
18606
+ return null;
18607
+ return rename ? positionals.at(-1) ?? null : positionals[0];
18608
+ }, gitBranchCreation = (args) => {
18609
+ const [sub, second] = args;
18610
+ 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;
18611
+ return target && !target.startsWith("-") && !UNKNOWABLE.test(target) ? target : null;
18612
+ }, shellBranchTargets = (command, dialect = "posix") => {
18613
+ if (!/\bgit\b/u.test(command))
18614
+ return [];
18615
+ const found = [];
18616
+ for (const invocation of rawInvocations(command, dialect)) {
18617
+ if (invocation.tool !== "git")
18618
+ continue;
18619
+ const branch = gitBranchCreation(invocation.args);
18620
+ if (branch)
18621
+ found.push({ branch, dir: invocation.dir });
18622
+ }
18623
+ return found;
18624
+ }, localBranchExists = (dir, branch) => spawnSync12("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
18625
+ cwd: dir,
18626
+ stdio: "ignore",
18627
+ timeout: 5000
18628
+ }).status === 0, shellBranchPolicyViolation = (root, command, dialect = "posix") => {
18629
+ let judged = false;
18630
+ for (const target of shellBranchTargets(command, dialect)) {
18631
+ if (target.dir === undefined)
18632
+ continue;
18633
+ const dir = target.dir === null ? root : path20.resolve(root, target.dir);
18634
+ if (target.dir !== null && (!existsSync6(dir) || !inWorkitWorkspace(dir)))
18635
+ continue;
18636
+ if (localBranchExists(dir, target.branch))
18637
+ continue;
18638
+ judged = true;
18639
+ const check = validateBranchNameFor(dir, target.branch);
18640
+ if (!check.ok)
18641
+ return check;
18642
+ }
18643
+ return judged ? { ok: true } : null;
18644
+ };
18645
+ var init_route_intent = __esm(() => {
18646
+ init_raw_git();
18647
+ init_branch();
18648
+ UNKNOWABLE = /[$`*?[\]{}~]/u;
18649
+ SWITCH_CREATE = new Set(["-c", "--create", "-C", "--force-create", "--orphan"]);
18650
+ CHECKOUT_CREATE = new Set(["-b", "-B", "--orphan"]);
18651
+ WORKTREE_CREATE = new Set(["-b", "-B"]);
18652
+ 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
+ BRANCH_RENAME = new Set(["-m", "-M", "--move", "-c", "-C", "--copy"]);
18654
+ WORKTREE_VALUES = new Set(["--reason"]);
18655
+ });
18656
+
18657
+ // packages/workit-core/src/hooks/policy.ts
18658
+ var shellPolicy = (cwd, command, dialect = "posix") => {
18659
+ const policy = shellBranchPolicyViolation(cwd, command, dialect);
18660
+ return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
18661
+ };
18662
+ var init_policy = __esm(() => {
18663
+ init_route_intent();
18664
+ });
18665
+
18666
+ // packages/workit-core/src/hooks/write-gate.ts
18667
+ import { realpathSync as realpathSync5 } from "node:fs";
18668
+ import path21 from "node:path";
18669
+ function writeGate(cwd, paths) {
18670
+ try {
18671
+ const gate = gateFor(cwd);
18672
+ if (!gate || gate.blockers.length === 0)
18673
+ return NONE3;
18674
+ if (paths?.length && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
18675
+ return NONE3;
18676
+ const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
18677
+ return {
18678
+ kind: "deny",
18679
+ reason: `workit before-write gate: ${reasons}. Markdown, docs/, plans/ and files outside the checkout stay writable.`,
18680
+ unblock: gate.blockers[0].reason
18681
+ };
18682
+ } catch {
18683
+ return NONE3;
18684
+ }
18685
+ }
18686
+ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
18687
+ let rest = "";
18688
+ for (let dir = path21.resolve(file);; dir = path21.dirname(dir)) {
18689
+ try {
18690
+ return path21.join(realpathSync5(dir), rest);
18691
+ } catch {
18692
+ if (path21.dirname(dir) === dir)
18693
+ return path21.resolve(file);
18694
+ rest = rest ? path21.join(path21.basename(dir), rest) : path21.basename(dir);
18695
+ }
18696
+ }
18697
+ }, exempt = (cwd, root, file, plans) => {
18698
+ const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
18699
+ const relative = path21.relative(fold(canonical2(root)), fold(canonical2(path21.resolve(cwd, file))));
18700
+ if (relative.startsWith("..") || path21.isAbsolute(relative))
18701
+ return true;
18702
+ const posix = relative.split(path21.sep).join("/");
18703
+ const segments = posix.split("/");
18704
+ return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
18705
+ }, PREFIXES3, operands = (words) => words.filter((item) => !item.startsWith("-")), editsOf = (input) => {
18706
+ let words = input;
18707
+ while (words.length && (PREFIXES3.has(words[0]) || /^\w+=/.test(words[0])))
18708
+ words = words.slice(1);
18709
+ const [verb, ...args] = words;
18710
+ const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
18711
+ if (!name || name === "workit" || name === "npx" && args.some((item) => /workit/.test(item)))
18712
+ return [];
18713
+ switch (name) {
18714
+ case "touch":
18715
+ case "mkdir":
18716
+ case "rm":
18717
+ case "rmdir":
18718
+ case "tee":
18719
+ case "mv":
18720
+ case "truncate":
18721
+ return operands(args);
18722
+ case "cp":
18723
+ case "ln":
18724
+ case "install": {
18725
+ const target = args.indexOf("-t");
18726
+ if (target >= 0)
18727
+ return args[target + 1] ? [args[target + 1]] : null;
18728
+ const last = operands(args).at(-1);
18729
+ return last ? [last] : null;
18730
+ }
18731
+ case "dd":
18732
+ return args.filter((item) => item.startsWith("of=")).map((item) => item.slice(3));
18733
+ case "sed":
18734
+ case "perl": {
18735
+ if (!args.some((item) => /^-[a-zA-Z]*i|^--in-place/.test(item)))
18736
+ return [];
18737
+ const scripted = args.some((item) => item === "-e" || item === "-f");
18738
+ const files = [];
18739
+ for (let index = 0;index < args.length; index++) {
18740
+ if (args[index] === "-e" || args[index] === "-f")
18741
+ index++;
18742
+ else if (!args[index].startsWith("-"))
18743
+ files.push(args[index]);
18744
+ }
18745
+ return scripted ? files : files.slice(1);
18746
+ }
18747
+ case "patch":
18748
+ return null;
18749
+ case "set-content":
18750
+ case "add-content":
18751
+ case "out-file":
18752
+ case "new-item":
18753
+ case "ni":
18754
+ case "remove-item":
18755
+ case "del":
18756
+ case "copy-item":
18757
+ case "move-item": {
18758
+ const flag = args.findIndex((item) => /^-(?:path|filepath|literalpath|destination)$/i.test(item));
18759
+ if (flag >= 0)
18760
+ return args[flag + 1] ? [args[flag + 1]] : null;
18761
+ const files = operands(args);
18762
+ if (name === "move-item")
18763
+ return files.length ? files : null;
18764
+ const file = name === "copy-item" ? files.at(-1) : files[0];
18765
+ return file ? [file] : null;
18766
+ }
18767
+ case "git": {
18768
+ const sub = operands(args)[0];
18769
+ if (sub === "apply")
18770
+ return null;
18771
+ if (sub === "restore" || sub === "mv" || sub === "rm")
18772
+ return operands(args).slice(1);
18773
+ if (sub === "checkout" && args.includes("--"))
18774
+ return args.slice(args.indexOf("--") + 1);
18775
+ return [];
18776
+ }
18777
+ default:
18778
+ return [];
18779
+ }
18780
+ }, shellWrites = (command) => {
18781
+ const targets = [];
18782
+ let unknown = false;
18783
+ for (const segment of segmentsOf(command)) {
18784
+ targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
18785
+ const edits = editsOf(segment.words);
18786
+ if (edits === null)
18787
+ unknown = true;
18788
+ else
18789
+ targets.push(...edits);
18790
+ }
18791
+ if (!unknown && targets.length === 0)
18792
+ return { writes: false, targets: null };
18793
+ return { writes: true, targets: unknown ? null : targets };
18794
+ }, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path21.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
18795
+ const store = new TaskStore(cwd);
18796
+ const listed = store.listTaskIndex();
18797
+ if (!listed.ok)
18798
+ return null;
18799
+ const key = store.currentKey();
18800
+ if (!key.ok)
18801
+ return null;
18802
+ const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
18803
+ if (!entry)
18804
+ return { blockers: [], root: store.root, plans: new Set };
18805
+ const ledger = ledgerPath(store.root);
18806
+ const cacheKey = `${entry.id}\x00${entry.revision}\x00${ledger.ok && fileSignature(ledger.value) || "-"}`;
18807
+ const hit = cache2.get(store.root);
18808
+ if (hit && hit.key === cacheKey && hit.planKey === planKeyOf(store.root, hit.plans))
18809
+ return hit;
18810
+ const task = store.readTask(entry.id);
18811
+ if (!task.ok)
18812
+ return null;
18813
+ const workspace = store.readWorkspace();
18814
+ const plans = new Set((latestJudgment(task.data)?.refs ?? []).flatMap((ref) => ref.kind === "file" ? [ref.path] : []));
18815
+ const blockers = writeBlockers(task.data, store.root).filter((blocker) => {
18816
+ const requirement = task.data.policy?.requirements.find((item) => item.ruleId === blocker.ruleId && item.before === "write");
18817
+ return !(requirement?.acceptanceAllowed && workspace.ok && workspace.data && applicableDecision(task.data, workspace.data, requirement, store.root).length > 0);
18818
+ });
18819
+ const gate = {
18820
+ blockers,
18821
+ root: store.root,
18822
+ plans,
18823
+ key: cacheKey,
18824
+ planKey: planKeyOf(store.root, plans)
18825
+ };
18826
+ cache2.set(store.root, gate);
18827
+ if (cache2.size > 32)
18828
+ cache2.delete(cache2.keys().next().value);
18829
+ return gate;
18830
+ };
18831
+ var init_write_gate = __esm(() => {
18832
+ init_task_store();
18833
+ init_derive();
18834
+ init_task_evaluation();
18835
+ init_ledger();
18836
+ NONE3 = { kind: "none" };
18837
+ CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
18838
+ PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
18839
+ cache2 = new Map;
18840
+ });
18841
+
18842
+ // packages/workit-core/src/hooks/stop.ts
18843
+ import { spawnSync as spawnSync13 } from "node:child_process";
18536
18844
  function stopObligation(input, descriptor) {
18537
18845
  try {
18538
18846
  const session = input.session.id;
@@ -18596,7 +18904,7 @@ var NONE4, QUESTION, OFFER, YES_NO, LIST_ITEM, asksUser = (message) => {
18596
18904
  `);
18597
18905
  return QUESTION.test(tail) || OFFER.test(tail) || YES_NO.test(tail);
18598
18906
  }, short = (sha) => (sha ?? "?").slice(0, 12), gitList = (cwd, args) => {
18599
- const run = spawnSync12("git", args, {
18907
+ const run = spawnSync13("git", args, {
18600
18908
  cwd,
18601
18909
  encoding: "utf8",
18602
18910
  timeout: 3000,
@@ -18906,10 +19214,10 @@ var init_skill_nudge = __esm(() => {
18906
19214
  });
18907
19215
 
18908
19216
  // packages/workit-core/src/hooks/hosts/fields.ts
18909
- import { existsSync as existsSync6, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
18910
- import path20 from "node:path";
19217
+ import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
19218
+ import path22 from "node:path";
18911
19219
  var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "", optionalText = (value) => typeof value === "string" ? value : null, existingDirectory = (value) => {
18912
- if (!nonEmpty2(value) || !path20.isAbsolute(value) || !existsSync6(value))
19220
+ if (!nonEmpty2(value) || !path22.isAbsolute(value) || !existsSync7(value))
18913
19221
  return null;
18914
19222
  try {
18915
19223
  return statSync2(value).isDirectory() ? realpathSync6(value) : null;
@@ -19005,7 +19313,7 @@ function handleHook(input, deps) {
19005
19313
  if (skillRead)
19006
19314
  recordSkillLoad(input, skillRead, "read");
19007
19315
  const canDeny = usable2(descriptor.shellPolicy.deny);
19008
- const policy = canDeny ? shellPolicy(input.cwd, event.command) : NONE5;
19316
+ const policy = canDeny ? shellPolicy(input.cwd, event.command, event.dialect) : NONE5;
19009
19317
  if (policy.kind !== "none")
19010
19318
  return policy;
19011
19319
  const postTool = usable2(descriptor.events["shell.post"].support);
@@ -19059,17 +19367,20 @@ var usable2 = (value) => value === "native" || value === "partial", NONE5, resen
19059
19367
  const role = workitRole(descriptor, event.agentType);
19060
19368
  const who = `${descriptor.label} subagent ${event.agentId} (${event.agentType})`;
19061
19369
  const isolated = descriptor.subagents.worktreeIsolation === "native" && (role === "implementer" || CLAUDE_WORKTREE.test(input.cwd));
19370
+ const guide = role === "implementer" ? branchGuidance(input.cwd) : null;
19062
19371
  if (role === "implementer" && isolated)
19063
- 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.`;
19372
+ 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.`;
19064
19373
  if (isolated)
19065
19374
  return `${ISOLATED_GIT}
19066
19375
  ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
19067
19376
  if (role === "implementer")
19068
- 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.`;
19377
+ 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.`;
19069
19378
  const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
19070
19379
  if (role !== "judge" || !event.agentId)
19071
19380
  return readOnly;
19072
19381
  const own = subagentSession(session, event.agentId);
19382
+ if (session)
19383
+ recordIssuedSession(input.cwd || process.cwd(), session, own);
19073
19384
  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.`;
19074
19385
  }, failureDecision = (descriptor, event, error) => {
19075
19386
  if (event === "shell.pre" || event === "tool.pre" || event === "write.pre")
@@ -19101,6 +19412,8 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
19101
19412
  };
19102
19413
  var init_handle = __esm(() => {
19103
19414
  init_context();
19415
+ init_branch();
19416
+ init_host_session();
19104
19417
  init_policy();
19105
19418
  init_raw_git();
19106
19419
  init_skill_nudge();
@@ -19638,13 +19951,13 @@ var init_codex = __esm(() => {
19638
19951
  };
19639
19952
  },
19640
19953
  render: render2,
19641
- 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>`
19954
+ 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>`
19642
19955
  };
19643
19956
  });
19644
19957
 
19645
19958
  // packages/workit-core/src/hooks/hosts/cursor.ts
19646
- import fs16 from "node:fs";
19647
- import path21 from "node:path";
19959
+ import fs17 from "node:fs";
19960
+ import path23 from "node:path";
19648
19961
  var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
19649
19962
  if (!isRecord4(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
19650
19963
  return { ok: false, error: "hook_event_name is required" };
@@ -19725,17 +20038,17 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
19725
20038
  };
19726
20039
  }
19727
20040
  }, TRANSCRIPT_TAIL_BYTES, lastAssistantText = (file) => {
19728
- if (!file || !path21.isAbsolute(file))
20041
+ if (!file || !path23.isAbsolute(file))
19729
20042
  return null;
19730
20043
  try {
19731
- const size = fs16.statSync(file).size;
20044
+ const size = fs17.statSync(file).size;
19732
20045
  const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
19733
20046
  const buffer = Buffer.alloc(size - start);
19734
- const fd = fs16.openSync(file, "r");
20047
+ const fd = fs17.openSync(file, "r");
19735
20048
  try {
19736
- fs16.readSync(fd, buffer, 0, buffer.length, start);
20049
+ fs17.readSync(fd, buffer, 0, buffer.length, start);
19737
20050
  } finally {
19738
- fs16.closeSync(fd);
20051
+ fs17.closeSync(fd);
19739
20052
  }
19740
20053
  const lines = buffer.toString("utf8").split(`
19741
20054
  `);
@@ -19761,7 +20074,7 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
19761
20074
  } catch {
19762
20075
  return null;
19763
20076
  }
19764
- }, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path21.sep) ? root : `${root}${path21.sep}`), hookCwd = (input) => {
20077
+ }, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path23.sep) ? root : `${root}${path23.sep}`), hookCwd = (input) => {
19765
20078
  const cwd = existingDirectory(input.cwd);
19766
20079
  if (cwd && input.hook_event_name === "beforeShellExecution")
19767
20080
  return cwd;