@brainervirus/workit-cursor 8.22.3 → 8.24.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.
@@ -6981,6 +6981,110 @@ function resolveRef(cwd, ref) {
6981
6981
  return null;
6982
6982
  return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
6983
6983
  }
6984
+ function diffNumstat(cwd, fork, head) {
6985
+ const range = laneRange(fork, head);
6986
+ if (!range)
6987
+ return null;
6988
+ const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
6989
+ if (!run.ok)
6990
+ return null;
6991
+ const out = [];
6992
+ for (const record of run.stdout.split("\x00")) {
6993
+ const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
6994
+ if (!match)
6995
+ continue;
6996
+ const binary = match[1] === "-";
6997
+ out.push({
6998
+ path: match[3],
6999
+ added: binary ? 0 : Number(match[1]),
7000
+ deleted: binary ? 0 : Number(match[2]),
7001
+ binary
7002
+ });
7003
+ }
7004
+ return out;
7005
+ }
7006
+ function diffRaw(cwd, fork, head) {
7007
+ const range = laneRange(fork, head);
7008
+ if (!range)
7009
+ return null;
7010
+ const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
7011
+ if (!run.ok)
7012
+ return null;
7013
+ const fields = run.stdout.split("\x00");
7014
+ const out = [];
7015
+ for (let index = 0;index + 1 < fields.length; index += 2) {
7016
+ const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
7017
+ if (!meta)
7018
+ return null;
7019
+ out.push({
7020
+ path: fields[index + 1],
7021
+ oldMode: meta[1],
7022
+ newMode: meta[2],
7023
+ oldBlob: meta[3],
7024
+ newBlob: meta[4]
7025
+ });
7026
+ }
7027
+ return out;
7028
+ }
7029
+ function readBlobs(cwd, ids) {
7030
+ const out = new Map;
7031
+ const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
7032
+ if (!wanted.length)
7033
+ return out;
7034
+ const run = spawnSync("git", ["cat-file", "--batch"], {
7035
+ cwd,
7036
+ input: `${wanted.join(`
7037
+ `)}
7038
+ `,
7039
+ maxBuffer: MAX_BUFFER,
7040
+ timeout: GIT_TIMEOUTS.local,
7041
+ killSignal: "SIGKILL",
7042
+ windowsHide: true
7043
+ });
7044
+ if (run.status !== 0 || !run.stdout)
7045
+ return null;
7046
+ const bytes = run.stdout;
7047
+ let at = 0;
7048
+ for (const id of wanted) {
7049
+ const end = bytes.indexOf(10, at);
7050
+ if (end < 0)
7051
+ return null;
7052
+ const header = bytes.subarray(at, end).toString("utf8").split(" ");
7053
+ at = end + 1;
7054
+ if (header[1] === "missing")
7055
+ continue;
7056
+ const size = Number(header[2]);
7057
+ if (header[0] !== id || !Number.isSafeInteger(size))
7058
+ return null;
7059
+ out.set(id, bytes.subarray(at, at + size));
7060
+ at += size + 1;
7061
+ }
7062
+ return out;
7063
+ }
7064
+ function treeFilesNamed(cwd, rev, name) {
7065
+ if (!safeArg(rev))
7066
+ return null;
7067
+ const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
7068
+ if (!run.ok)
7069
+ return null;
7070
+ const out = new Map;
7071
+ for (const record of run.stdout.split("\x00")) {
7072
+ const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
7073
+ if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
7074
+ out.set(match[2], match[1]);
7075
+ }
7076
+ return out;
7077
+ }
7078
+ function fileAt(cwd, rev, file) {
7079
+ if (!safeArg(rev))
7080
+ return null;
7081
+ const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
7082
+ return run.ok ? run.stdout : null;
7083
+ }
7084
+ function untrackedFiles(cwd) {
7085
+ const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
7086
+ return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
7087
+ }
6984
7088
  function currentBranch(cwd) {
6985
7089
  return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
6986
7090
  }
@@ -7027,11 +7131,24 @@ var MAX_BUFFER, GIT_TIMEOUTS, git = (cwd, args, options = {}) => {
7027
7131
  const { atimeNs, mtimeNs } = fs.statSync(from, { bigint: true });
7028
7132
  const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
7029
7133
  fs.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
7134
+ }, LANE_DIFF, laneRange = (fork, head) => {
7135
+ if (!safeArg(fork) || head !== null && !safeArg(head))
7136
+ return null;
7137
+ return head === null ? [fork] : [fork, head];
7030
7138
  }, config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
7031
7139
  var init_rev = __esm(() => {
7032
7140
  MAX_BUFFER = 256 * 1024 * 1024;
7033
7141
  GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
7034
7142
  MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
7143
+ LANE_DIFF = [
7144
+ "-c",
7145
+ "core.quotePath=false",
7146
+ "diff",
7147
+ "--no-color",
7148
+ "--no-ext-diff",
7149
+ "--no-textconv",
7150
+ "--no-renames"
7151
+ ];
7035
7152
  });
7036
7153
 
7037
7154
  // packages/workit-core/src/store/paths.ts
@@ -8094,7 +8211,7 @@ var init_workspaces = __esm(() => {
8094
8211
  }).passthrough().optional(),
8095
8212
  autoApprove: union([boolean2(), array(nonBlank)]).optional(),
8096
8213
  defaultEndpoint: unknown().optional(),
8097
- verification: _enum(["self", "independent"]).optional(),
8214
+ verification: _enum(["proportional", "self", "independent"]).optional(),
8098
8215
  delegation: unknown().optional(),
8099
8216
  trailers: boolean2().optional(),
8100
8217
  liveSessionGuard: boolean2().optional(),
@@ -9787,7 +9904,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
9787
9904
  verdict: failing?.verdict ?? null,
9788
9905
  reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
9789
9906
  };
9790
- const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
9907
+ const latestSelf = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && (STRONG_RESULTS.has(String(entry.verdict.result)) || FAILING.has(String(entry.verdict.result))));
9908
+ const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
9791
9909
  return {
9792
9910
  branch,
9793
9911
  head,
@@ -10068,12 +10186,709 @@ var init_ledger = __esm(() => {
10068
10186
  ]);
10069
10187
  });
10070
10188
 
10071
- // packages/workit-core/src/core/policy/derive.ts
10072
- import { existsSync as existsSync2 } from "node:fs";
10189
+ // packages/workit-core/src/forge/pr-body.ts
10190
+ var init_pr_body = __esm(() => {
10191
+ init_vcs_config();
10192
+ });
10193
+
10194
+ // packages/workit-core/src/core/pr-create.ts
10195
+ var init_pr_create = __esm(() => {
10196
+ init_vcs_config();
10197
+ init_vcs_config();
10198
+ init_branch();
10199
+ init_pr_body();
10200
+ });
10201
+
10202
+ // packages/workit-core/src/forge/redact.ts
10203
+ var KEY, ASSIGNMENT, QUOTED_ASSIGNMENT, ANSI;
10204
+ var init_redact = __esm(() => {
10205
+ KEY = String.raw`[A-Za-z0-9_.-]*?(?:token|secret|password|passwd|pwd|api[_-]?key|access[_-]?key|account[_-]?key|private[_-]?key|client[_-]?secret|credentials?|authorization|cookie|signature)[A-Za-z0-9_.-]*`;
10206
+ ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*["']?)((?:bearer|basic|token|digest)\s+)?([^\s"',;&]+)`, "giu");
10207
+ QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
10208
+ ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
10209
+ });
10210
+ // packages/workit-core/src/git/ops.ts
10211
+ var COMMIT_TIMEOUT_MS;
10212
+ var init_ops = __esm(() => {
10213
+ init_branch();
10214
+ init_branch_policy();
10215
+ init_commit_flavors();
10216
+ init_pr_create();
10217
+ init_release_tracks();
10218
+ init_vcs_config();
10219
+ init_workspaces();
10220
+ init_redact();
10221
+ init_host_session();
10222
+ init_ledger();
10223
+ init_rev();
10224
+ COMMIT_TIMEOUT_MS = 10 * 60000;
10225
+ });
10226
+
10227
+ // packages/workit-core/src/core/policy/attributes.ts
10228
+ function patternRegex(dir, pattern) {
10229
+ if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
10230
+ return null;
10231
+ const anchored = pattern.includes("/");
10232
+ const body = pattern.replace(/^\//u, "");
10233
+ let source = "";
10234
+ for (let index = 0;index < body.length; index += 1) {
10235
+ const char = body[index];
10236
+ if (char === "*" && body[index + 1] === "*") {
10237
+ const slash = body[index + 2] === "/";
10238
+ source += slash ? "(?:.*/)?" : ".*";
10239
+ index += slash ? 2 : 1;
10240
+ } else if (char === "*")
10241
+ source += "[^/]*";
10242
+ else if (char === "?")
10243
+ source += "[^/]";
10244
+ else if (char === "[") {
10245
+ const end = body.indexOf("]", index + 2);
10246
+ if (end < 0)
10247
+ return null;
10248
+ const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
10249
+ source += `[${set}]`;
10250
+ index = end;
10251
+ } else if (char === "\\" && index + 1 < body.length) {
10252
+ source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
10253
+ index += 1;
10254
+ } else
10255
+ source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
10256
+ }
10257
+ const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
10258
+ try {
10259
+ return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
10260
+ } catch {
10261
+ return null;
10262
+ }
10263
+ }
10264
+ function rulesOf(dir, text) {
10265
+ const out = [];
10266
+ for (const raw of text.split(`
10267
+ `)) {
10268
+ const line = raw.trim();
10269
+ if (!line || line.startsWith("#") || line.startsWith("[attr]"))
10270
+ continue;
10271
+ const [pattern, ...attributes] = line.split(/\s+/u);
10272
+ let generated = null;
10273
+ for (const attribute of attributes) {
10274
+ if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
10275
+ generated = true;
10276
+ else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
10277
+ generated = false;
10278
+ }
10279
+ if (generated === null)
10280
+ continue;
10281
+ const regex = patternRegex(dir, pattern);
10282
+ if (regex)
10283
+ out.push({ regex, generated });
10284
+ }
10285
+ return out;
10286
+ }
10287
+ function generatedByAttributes(files, paths) {
10288
+ const rules = [...files].map(([file, text]) => {
10289
+ const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
10290
+ return {
10291
+ depth: dir ? dir.split("/").length : 0,
10292
+ rules: rulesOf(dir, text)
10293
+ };
10294
+ }).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
10295
+ const out = new Set;
10296
+ for (const file of paths) {
10297
+ const last = rules.findLast((rule) => rule.regex.test(file));
10298
+ if (last?.generated)
10299
+ out.add(file);
10300
+ }
10301
+ return out;
10302
+ }
10303
+
10304
+ // packages/workit-core/src/core/policy/substance.ts
10305
+ function substanceOf(file, text) {
10306
+ const syntax = syntaxOf(file);
10307
+ if (!syntax)
10308
+ return plainSubstance(text);
10309
+ const markup = syntax.block.some(([open]) => open === "<!--");
10310
+ if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
10311
+ return plainSubstance(text);
10312
+ return lex(syntax, text);
10313
+ }
10314
+ function plainSubstance(text) {
10315
+ return text.split(`
10316
+ `).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
10317
+ `);
10318
+ }
10319
+ function lex(syntax, text) {
10320
+ const collapse = !syntax.indented;
10321
+ let out = "";
10322
+ let lineStart = 0;
10323
+ let kept = 0;
10324
+ let pendingSpace = false;
10325
+ const frames = [{ kind: "code", braces: 0 }];
10326
+ let index = 0;
10327
+ if (text.startsWith("#!")) {
10328
+ const end = text.indexOf(`
10329
+ `);
10330
+ out = end < 0 ? text : text.slice(0, end);
10331
+ kept = out.length;
10332
+ index = end < 0 ? text.length : end;
10333
+ }
10334
+ const emitCode = (chunk) => {
10335
+ if (collapse && pendingSpace && out.length > lineStart) {
10336
+ const before = out[out.length - 1];
10337
+ if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
10338
+ out += " ";
10339
+ }
10340
+ pendingSpace = false;
10341
+ out += chunk;
10342
+ };
10343
+ const emitVerbatim = (chunk) => {
10344
+ emitCode(chunk);
10345
+ kept = out.length;
10346
+ };
10347
+ const newline = () => {
10348
+ if (!collapse) {
10349
+ let end = out.length;
10350
+ while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
10351
+ end -= 1;
10352
+ out = out.slice(0, end);
10353
+ }
10354
+ pendingSpace = false;
10355
+ if (out.length > lineStart) {
10356
+ out += `
10357
+ `;
10358
+ lineStart = out.length;
10359
+ }
10360
+ };
10361
+ const regexAllowed = () => {
10362
+ let end = out.length;
10363
+ while (end > 0 && /\s/u.test(out[end - 1]))
10364
+ end -= 1;
10365
+ if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
10366
+ return true;
10367
+ let start = end;
10368
+ while (start > 0 && WORD.test(out[start - 1]))
10369
+ start -= 1;
10370
+ return REGEX_KEYWORDS.has(out.slice(start, end));
10371
+ };
10372
+ const scanString = (quote, escapes, multiline) => {
10373
+ const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
10374
+ const fstring = syntax.fstring === true && /f/iu.test(prefix);
10375
+ let end = index + quote.length;
10376
+ while (end < text.length) {
10377
+ const char = text[end];
10378
+ if (escapes && char === "\\") {
10379
+ end += 2;
10380
+ continue;
10381
+ }
10382
+ if (text.startsWith(quote, end)) {
10383
+ if (fstring && unbalanced(text.slice(index + quote.length, end)))
10384
+ return false;
10385
+ emitVerbatim(text.slice(index, end + quote.length));
10386
+ index = end + quote.length;
10387
+ return true;
10388
+ }
10389
+ if (char === `
10390
+ ` && !multiline)
10391
+ return false;
10392
+ end += 1;
10393
+ }
10394
+ return false;
10395
+ };
10396
+ while (index < text.length) {
10397
+ const frame = frames[frames.length - 1];
10398
+ const char = text[index];
10399
+ if (frame.kind === "template") {
10400
+ if (char === "\\") {
10401
+ emitVerbatim(text.slice(index, index + 2));
10402
+ index += 2;
10403
+ } else if (char === "`") {
10404
+ emitVerbatim(char);
10405
+ frames.pop();
10406
+ index += 1;
10407
+ } else if (text.startsWith("${", index)) {
10408
+ emitVerbatim("${");
10409
+ frames.push({ kind: "code", braces: 0 });
10410
+ index += 2;
10411
+ } else {
10412
+ out += char;
10413
+ kept = out.length;
10414
+ index += 1;
10415
+ }
10416
+ continue;
10417
+ }
10418
+ if (char === `
10419
+ `) {
10420
+ newline();
10421
+ index += 1;
10422
+ continue;
10423
+ }
10424
+ if (SPACE.test(char)) {
10425
+ if (collapse)
10426
+ pendingSpace = true;
10427
+ else
10428
+ out += char;
10429
+ index += 1;
10430
+ continue;
10431
+ }
10432
+ const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
10433
+ if (lineOpener) {
10434
+ const end = text.indexOf(`
10435
+ `, index);
10436
+ const comment = text.slice(index, end < 0 ? text.length : end);
10437
+ if (DIRECTIVE.test(comment.slice(lineOpener.length)))
10438
+ emitVerbatim(comment);
10439
+ else
10440
+ pendingSpace = true;
10441
+ index = end < 0 ? text.length : end;
10442
+ continue;
10443
+ }
10444
+ const block = syntax.block.find(([open]) => text.startsWith(open, index));
10445
+ if (block) {
10446
+ const end = text.indexOf(block[1], index + block[0].length);
10447
+ if (end < 0)
10448
+ return null;
10449
+ const comment = text.slice(index, end + block[1].length);
10450
+ if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
10451
+ emitVerbatim(comment);
10452
+ else
10453
+ pendingSpace = true;
10454
+ if (comment.includes(`
10455
+ `))
10456
+ newline();
10457
+ index = end + block[1].length;
10458
+ continue;
10459
+ }
10460
+ if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
10461
+ if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
10462
+ return null;
10463
+ continue;
10464
+ }
10465
+ if (syntax.quotes.includes(char)) {
10466
+ if (!scanString(char, true, Boolean(syntax.multiline)))
10467
+ return null;
10468
+ continue;
10469
+ }
10470
+ if (syntax.literal?.includes(char)) {
10471
+ if (!scanString(char, false, false))
10472
+ return null;
10473
+ continue;
10474
+ }
10475
+ if (syntax.rawBacktick && char === "`") {
10476
+ if (!scanString("`", false, true))
10477
+ return null;
10478
+ continue;
10479
+ }
10480
+ if (syntax.js && char === "`") {
10481
+ emitVerbatim(char);
10482
+ frames.push({ kind: "template" });
10483
+ index += 1;
10484
+ continue;
10485
+ }
10486
+ if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
10487
+ return null;
10488
+ if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
10489
+ const lineEnd = text.indexOf(`
10490
+ `, index);
10491
+ if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
10492
+ return null;
10493
+ }
10494
+ if (syntax.js && char === "/" && regexAllowed()) {
10495
+ let end = index + 1;
10496
+ let inClass = false;
10497
+ while (end < text.length && (inClass || text[end] !== "/")) {
10498
+ if (text[end] === `
10499
+ `)
10500
+ return null;
10501
+ if (text[end] === "\\")
10502
+ end += 1;
10503
+ else if (text[end] === "[")
10504
+ inClass = true;
10505
+ else if (text[end] === "]")
10506
+ inClass = false;
10507
+ end += 1;
10508
+ }
10509
+ if (end >= text.length)
10510
+ return null;
10511
+ emitVerbatim(text.slice(index, end + 1));
10512
+ index = end + 1;
10513
+ continue;
10514
+ }
10515
+ if (frames.length > 1 && frame.kind === "code") {
10516
+ if (char === "{")
10517
+ frame.braces += 1;
10518
+ else if (char === "}") {
10519
+ if (frame.braces === 0) {
10520
+ emitVerbatim(char);
10521
+ frames.pop();
10522
+ index += 1;
10523
+ continue;
10524
+ }
10525
+ frame.braces -= 1;
10526
+ }
10527
+ }
10528
+ emitCode(char);
10529
+ index += 1;
10530
+ }
10531
+ if (frames.length !== 1)
10532
+ return null;
10533
+ newline();
10534
+ return out;
10535
+ }
10536
+ function unbalanced(body) {
10537
+ let depth = 0;
10538
+ for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
10539
+ if (part === "{")
10540
+ depth += 1;
10541
+ else if (part === "}")
10542
+ depth = Math.max(0, depth - 1);
10543
+ }
10544
+ return depth > 0;
10545
+ }
10546
+ function hasOpaqueLineBreak(text) {
10547
+ return /\r(?!\n)|[\u2028\u2029]/u.test(text);
10548
+ }
10549
+ function textOf(bytes) {
10550
+ if (bytes.includes(0))
10551
+ return null;
10552
+ try {
10553
+ return decoder.decode(bytes);
10554
+ } catch {
10555
+ return null;
10556
+ }
10557
+ }
10558
+ function cosmeticChange(file, before, after) {
10559
+ const left = textOf(before);
10560
+ const right = textOf(after);
10561
+ if (left === null || right === null)
10562
+ return false;
10563
+ if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
10564
+ return false;
10565
+ const a = substanceOf(file, left);
10566
+ return a !== null && a === substanceOf(file, right);
10567
+ }
10568
+ var SLASH_BLOCK, C_LIKE, SYNTAXES, DIRECTIVE, extensionOf = (file) => {
10569
+ const name = file.slice(file.lastIndexOf("/") + 1);
10570
+ const dot = name.lastIndexOf(".");
10571
+ return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
10572
+ }, CI_PATH, syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null, SPACE, WORD, NO_SPACE_AFTER = "([{,;", NO_SPACE_BEFORE = ")]},;", REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^", REGEX_KEYWORDS, decoder;
10573
+ var init_substance = __esm(() => {
10574
+ SLASH_BLOCK = [["/*", "*/"]];
10575
+ C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
10576
+ SYNTAXES = Object.fromEntries([
10577
+ ...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
10578
+ ...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
10579
+ ext,
10580
+ { ...C_LIKE, triple: true }
10581
+ ]),
10582
+ ["go", { ...C_LIKE, rawBacktick: true }],
10583
+ ...["css", "scss", "less"].map((ext) => [
10584
+ ext,
10585
+ { line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
10586
+ ]),
10587
+ ...["py", "pyi"].map((ext) => [
10588
+ ext,
10589
+ { line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
10590
+ ]),
10591
+ ["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
10592
+ ...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
10593
+ ext,
10594
+ { line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
10595
+ ])
10596
+ ]);
10597
+ DIRECTIVE = /@ts-|eslint|prettier|biome|oxlint|istanbul|\bc8\b|\bv8\b|@jsx|@flow|@vite|webpack|__PURE__|__NO_SIDE_EFFECTS__|source(?:Mapping)?URL|^\/\s*<|go:|\+build|nolint|noqa|type:|pyright|mypy|pylint|pragma|coding[:=]|fmt:|NOSONAR|@license|@preserve|@refresh|environment|language=|cython|ruff|isort|deno-|@vitest|@jest|swiftlint|ktlint|checkstyle|suppress|nullable|#(?:end)?region/iu;
10598
+ CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
10599
+ SPACE = /[ \t\r\f\v]/u;
10600
+ WORD = /[\w$]/u;
10601
+ REGEX_KEYWORDS = new Set([
10602
+ "return",
10603
+ "typeof",
10604
+ "instanceof",
10605
+ "case",
10606
+ "do",
10607
+ "else",
10608
+ "in",
10609
+ "of",
10610
+ "new",
10611
+ "delete",
10612
+ "void",
10613
+ "throw",
10614
+ "yield",
10615
+ "await"
10616
+ ]);
10617
+ decoder = new TextDecoder("utf-8", { fatal: true });
10618
+ });
10619
+
10620
+ // packages/workit-core/src/core/policy/lane.ts
10621
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
10073
10622
  import path10 from "node:path";
10623
+ function kindOf(file) {
10624
+ if (file.binary)
10625
+ return "behavior";
10626
+ if (file.generated)
10627
+ return "generated";
10628
+ if (LOCKFILES.has(basename(file.path)))
10629
+ return "lockfile";
10630
+ if (isDoc(file.path))
10631
+ return "doc";
10632
+ if (file.cosmetic)
10633
+ return "cosmetic";
10634
+ return TEST_PATH.test(file.path) ? "test" : "behavior";
10635
+ }
10636
+ function globMatch(pattern, file) {
10637
+ let regex = globCache.get(pattern);
10638
+ if (!regex) {
10639
+ let source = "";
10640
+ for (let index = 0;index < pattern.length; index += 1) {
10641
+ const char = pattern[index];
10642
+ if (char === "*" && pattern[index + 1] === "*") {
10643
+ source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
10644
+ index += pattern[index + 2] === "/" ? 2 : 1;
10645
+ } else if (char === "*")
10646
+ source += "[^/]*";
10647
+ else if (char === "?")
10648
+ source += "[^/]";
10649
+ else
10650
+ source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
10651
+ }
10652
+ regex = new RegExp(`^${source}$`, "u");
10653
+ globCache.set(pattern, regex);
10654
+ }
10655
+ return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
10656
+ }
10657
+ function isRiskPath(file, extra = []) {
10658
+ if (extra.some((pattern) => globMatch(pattern, file)))
10659
+ return true;
10660
+ if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
10661
+ return true;
10662
+ if (TEST_PATH.test(file) || isDoc(file))
10663
+ return false;
10664
+ return file.split("/").map((segment, index, all) => index === all.length - 1 ? segment.replace(/\.[^.]+$/u, "") : segment).some((segment) => segment.toLowerCase().split(/[-_.]/u).some((token) => RISK_TOKENS.has(token)));
10665
+ }
10666
+ function computeLane(input) {
10667
+ const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
10668
+ const risky = new Set(riskFiles);
10669
+ const kinds = input.files.map((file) => ({
10670
+ file,
10671
+ kind: risky.has(file.path) ? "behavior" : kindOf(file)
10672
+ }));
10673
+ const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
10674
+ const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
10675
+ const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
10676
+ const result = (lane, reasons) => ({
10677
+ lane,
10678
+ reasons,
10679
+ lines,
10680
+ files: behavior.length,
10681
+ riskFiles
10682
+ });
10683
+ const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
10684
+ if (input.riskTier === "high")
10685
+ return result("full", ["the task judgment records high risk"]);
10686
+ if (riskFiles.length)
10687
+ return result("full", [
10688
+ `touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
10689
+ ]);
10690
+ if (!behavior.length)
10691
+ return result("none", [
10692
+ input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
10693
+ ]);
10694
+ if (lines >= fullMinLines)
10695
+ return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
10696
+ if (lines > selfMaxLines)
10697
+ return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
10698
+ if (behavior.length > selfMaxFiles)
10699
+ return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
10700
+ if (opaque.length)
10701
+ return result("focused", [
10702
+ `a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
10703
+ ]);
10704
+ return result("self", [
10705
+ `small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
10706
+ ]);
10707
+ }
10708
+ function untrackedStat(cwd, file) {
10709
+ try {
10710
+ const bytes = readFileSync3(path10.join(cwd, file));
10711
+ if (bytes.includes(0))
10712
+ return { path: file, added: 0, deleted: 0, binary: true };
10713
+ const text = bytes.toString("utf8");
10714
+ if (hasOpaqueLineBreak(text))
10715
+ return { path: file, added: 0, deleted: 0, binary: true };
10716
+ const lines = text.split(`
10717
+ `).length - (text.endsWith(`
10718
+ `) ? 1 : 0);
10719
+ return { path: file, added: lines, deleted: 0 };
10720
+ } catch {
10721
+ return { path: file, added: 0, deleted: 0, binary: true };
10722
+ }
10723
+ }
10724
+ function cosmeticFiles(cwd, fork, head) {
10725
+ const raw = diffRaw(cwd, fork, head);
10726
+ if (!raw)
10727
+ return null;
10728
+ const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
10729
+ const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
10730
+ if (!blobs)
10731
+ return null;
10732
+ const out = new Set;
10733
+ const opaque = new Set;
10734
+ for (const entry of candidates) {
10735
+ const before = blobs.get(entry.oldBlob);
10736
+ let after = blobs.get(entry.newBlob);
10737
+ if (!after && head === null && ABSENT.test(entry.newBlob))
10738
+ try {
10739
+ after = readFileSync3(path10.join(cwd, entry.path));
10740
+ } catch {
10741
+ after = undefined;
10742
+ }
10743
+ if (before && after && cosmeticChange(entry.path, before, after))
10744
+ out.add(entry.path);
10745
+ else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
10746
+ opaque.add(entry.path);
10747
+ }
10748
+ return { cosmetic: out, opaque };
10749
+ }
10750
+ function generatedAt(cwd, fork, paths) {
10751
+ const files = treeFilesNamed(cwd, fork, ".gitattributes");
10752
+ const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
10753
+ if (!files || !blobs)
10754
+ return new Set;
10755
+ const texts = new Map;
10756
+ for (const [file, id] of files) {
10757
+ const bytes = blobs.get(id);
10758
+ if (bytes)
10759
+ texts.set(file, bytes.toString("utf8"));
10760
+ }
10761
+ return generatedByAttributes(texts, paths);
10762
+ }
10763
+ function laneFiles(cwd, fork, head) {
10764
+ const stats = diffNumstat(cwd, fork, head);
10765
+ const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
10766
+ if (!stats || !cosmetic)
10767
+ return null;
10768
+ const files = [
10769
+ ...stats.map((entry) => ({
10770
+ ...entry,
10771
+ ...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
10772
+ cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
10773
+ })),
10774
+ ...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
10775
+ ];
10776
+ const generated = generatedAt(cwd, fork, files.map((entry) => entry.path));
10777
+ return files.map((entry) => ({
10778
+ ...entry,
10779
+ generated: generated.has(entry.path)
10780
+ }));
10781
+ }
10782
+ function branchLane(cwd, options) {
10783
+ const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
10784
+ const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
10785
+ const head = options.workingTree ? null : options.head;
10786
+ const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
10787
+ const files = fork ? laneFiles(cwd, fork, head) : null;
10788
+ const riskTier = options.judgment?.riskTier ?? null;
10789
+ if (!fork || !files) {
10790
+ const high = riskTier === "high";
10791
+ return {
10792
+ lane: high ? "full" : "focused",
10793
+ reasons: [
10794
+ `the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
10795
+ ...high ? ["the task judgment records high risk"] : []
10796
+ ],
10797
+ lines: 0,
10798
+ files: 0,
10799
+ riskFiles: [],
10800
+ base,
10801
+ head: options.head
10802
+ };
10803
+ }
10804
+ const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
10805
+ return {
10806
+ ...computeLane({
10807
+ files,
10808
+ riskTier,
10809
+ riskPaths: [...repoRisk, ...options.riskPaths ?? []]
10810
+ }),
10811
+ base,
10812
+ head: options.head
10813
+ };
10814
+ }
10815
+ var LANE_THRESHOLDS, RISK_TOKENS, RISK_FILES, RISK_NAMES, RISK_PATHS_FILE = ".workit/risk-paths", LOCKFILES, DOC_EXT, DOC_NAMES, INSTRUCTIONS, TEST_PATH, basename = (file) => file.slice(file.lastIndexOf("/") + 1), isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file))), globCache, parseRiskPaths = (text) => text.split(`
10816
+ `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, "")), REGULAR, ABSENT, forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD"), laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
10817
+ var init_lane = __esm(() => {
10818
+ init_rev();
10819
+ init_ops();
10820
+ init_ledger();
10821
+ init_substance();
10822
+ LANE_THRESHOLDS = {
10823
+ selfMaxLines: 100,
10824
+ selfMaxFiles: 5,
10825
+ fullMinLines: 500
10826
+ };
10827
+ RISK_TOKENS = new Set([
10828
+ "auth",
10829
+ "authn",
10830
+ "authz",
10831
+ "oauth",
10832
+ "security",
10833
+ "permission",
10834
+ "permissions",
10835
+ "secret",
10836
+ "secrets",
10837
+ "crypto",
10838
+ "gate",
10839
+ "gates",
10840
+ "policy",
10841
+ "policies",
10842
+ "hook",
10843
+ "hooks",
10844
+ "husky",
10845
+ "lefthook",
10846
+ "release",
10847
+ "releases",
10848
+ "migration",
10849
+ "migrations",
10850
+ "migrate",
10851
+ "workflows"
10852
+ ]);
10853
+ RISK_FILES = new Set([
10854
+ ".gitlab-ci.yml",
10855
+ "Jenkinsfile",
10856
+ "azure-pipelines.yml",
10857
+ ".pre-commit-config.yaml",
10858
+ ".workit/risk-paths"
10859
+ ]);
10860
+ RISK_NAMES = new Set([".gitattributes"]);
10861
+ LOCKFILES = new Set([
10862
+ "bun.lock",
10863
+ "bun.lockb",
10864
+ "package-lock.json",
10865
+ "npm-shrinkwrap.json",
10866
+ "yarn.lock",
10867
+ "pnpm-lock.yaml",
10868
+ "Cargo.lock",
10869
+ "go.sum",
10870
+ "poetry.lock",
10871
+ "uv.lock",
10872
+ "Pipfile.lock",
10873
+ "Gemfile.lock",
10874
+ "composer.lock"
10875
+ ]);
10876
+ DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc|txt)$/iu;
10877
+ DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
10878
+ INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
10879
+ TEST_PATH = /(?:^|\/)(?:test|tests|__tests__|spec|specs|fixtures)\/|\.(?:test|spec)\.[^/]+$/iu;
10880
+ globCache = new Map;
10881
+ REGULAR = new Set(["100644", "100755"]);
10882
+ ABSENT = /^0+$/u;
10883
+ });
10884
+
10885
+ // packages/workit-core/src/core/policy/derive.ts
10886
+ import { existsSync as existsSync3 } from "node:fs";
10887
+ import path11 from "node:path";
10074
10888
  function deriveRequirements(judgment, scope, constraints, verification = "self") {
10075
10889
  const out = [];
10076
10890
  const base = { scope, dependentAction: null };
10891
+ const proportional = verification === "proportional";
10077
10892
  if (judgment.behaviorChange)
10078
10893
  out.push(derived2({
10079
10894
  ...base,
@@ -10084,7 +10899,17 @@ function deriveRequirements(judgment, scope, constraints, verification = "self")
10084
10899
  before: "close",
10085
10900
  acceptanceAllowed: false
10086
10901
  }));
10087
- if (judgment.behaviorChange && judgment.riskTier === "normal")
10902
+ if (proportional && judgment.riskTier === "normal")
10903
+ out.push(derived2({
10904
+ ...base,
10905
+ ruleId: RULES.lane,
10906
+ dimension: "review",
10907
+ reason: "The review lane computed from the diff decides the verdict: none needs none, self the author's own (shown as self-reviewed), focused and full a non-author one.",
10908
+ satisfaction: "Read the lane in `workit policy assess` or `workit pr status`; record what it asks: `workit ledger verdict tests-verified --self --how <what you ran>` for self, or hand focused and full to a verifier.",
10909
+ before: "close",
10910
+ acceptanceAllowed: false
10911
+ }));
10912
+ if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
10088
10913
  out.push(verification === "independent" ? derived2({
10089
10914
  ...base,
10090
10915
  ruleId: RULES.nonAuthor,
@@ -10163,7 +10988,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
10163
10988
  case RULES.self:
10164
10989
  case RULES.nonAuthor:
10165
10990
  case RULES.verified:
10166
- return verdict(requirement, root, ledger);
10991
+ return verdict(requirement.ruleId, root, ledger);
10992
+ case RULES.lane:
10993
+ return laneVerdict(task, root, ledger);
10167
10994
  default:
10168
10995
  return null;
10169
10996
  }
@@ -10222,7 +11049,7 @@ var RULES, derived2 = (spec) => ({
10222
11049
  decisionIds: []
10223
11050
  };
10224
11051
  }, planRecorded = (task, root) => {
10225
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync2(path10.resolve(root, ref.path)));
11052
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync3(path11.resolve(root, ref.path)));
10226
11053
  if (cited)
10227
11054
  return {
10228
11055
  met: true,
@@ -10235,14 +11062,28 @@ var RULES, derived2 = (spec) => ({
10235
11062
  reason: "no plan recorded: write it (docs/ and Markdown writes stay allowed), then `workit policy assess --ref <path>`",
10236
11063
  decisionIds: []
10237
11064
  };
10238
- }, verdict = (requirement, root, ledger) => {
11065
+ }, workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) }), laneVerdict = (task, root, ledger) => {
11066
+ if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
11067
+ return {
11068
+ met: true,
11069
+ reason: "no branch to read a lane from, and the judgment records no behavior change",
11070
+ decisionIds: []
11071
+ };
11072
+ const lane = workingLane(task, root);
11073
+ const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
11074
+ const need = laneNeed(lane.lane);
11075
+ if (need === "none")
11076
+ return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
11077
+ const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
11078
+ return { ...status, reason: `${label}: ${status.reason}` };
11079
+ }, verdict = (ruleId, root, ledger) => {
10239
11080
  const branch = ledger.branch();
10240
11081
  if (!branch)
10241
11082
  return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
10242
11083
  const check = checkVerdicts(root, branch, ledger.rows());
10243
11084
  const failing = check.accepted.reasons.includes("failing_verdict");
10244
11085
  const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
10245
- const met = !failing && (requirement.ruleId === RULES.verified ? check.verdicts.find((entry) => entry.accepted && entry.kind === "live" && String(entry.verdict.result) === "verified") : requirement.ruleId === RULES.nonAuthor ? check.verdicts.find((entry) => entry.accepted && strong(entry)) : check.verdicts.find((entry) => entry.current && strong(entry)));
11086
+ const met = !failing && (ruleId === RULES.verified ? check.verdicts.find((entry) => entry.accepted && entry.kind === "live" && String(entry.verdict.result) === "verified") : ruleId === RULES.nonAuthor ? check.verdicts.find((entry) => entry.accepted && strong(entry)) : check.verdicts.find((entry) => entry.current && strong(entry)));
10246
11087
  if (met)
10247
11088
  return {
10248
11089
  met: true,
@@ -10250,18 +11091,20 @@ var RULES, derived2 = (spec) => ({
10250
11091
  decisionIds: []
10251
11092
  };
10252
11093
  const why = failing ? "a current independent verdict fails" : check.verdicts.some((entry) => entry.current && entry.verdict.result === "type-check-only") ? "type-check-only does not prove a behavior change" : (check.accepted.reasons.join(", ") || "no_verdict").replace(/_/g, " ");
10253
- const unblock = requirement.ruleId === RULES.self ? "record your own verdict after an observed `workit check test`: `workit ledger verdict tests-verified --self --how <what you ran>` (shown as self-reviewed), or have a verifier record one" : `an independent session records \`workit ledger verdict verified${requirement.ruleId === RULES.verified ? " --kind live" : ""} --how <what it exercised>\``;
11094
+ const unblock = ruleId === RULES.self ? "record your own verdict after an observed `workit check test`: `workit ledger verdict tests-verified --self --how <what you ran>` (shown as self-reviewed), or have a verifier record one" : `an independent session records \`workit ledger verdict verified${ruleId === RULES.verified ? " --kind live" : ""} --how <what it exercised>\``;
10254
11095
  return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
10255
11096
  };
10256
11097
  var init_derive = __esm(() => {
10257
11098
  init_rev();
10258
11099
  init_ledger();
11100
+ init_lane();
10259
11101
  init_task_contract();
10260
11102
  RULES = {
10261
11103
  test: "check:test",
10262
11104
  self: "verdict:self",
10263
11105
  nonAuthor: "verdict:non-author",
10264
11106
  verified: "verdict:verified",
11107
+ lane: "verdict:lane",
10265
11108
  product: "decision:product",
10266
11109
  plan: "plan"
10267
11110
  };
@@ -10819,7 +11662,7 @@ var init_safe_write = () => {};
10819
11662
 
10820
11663
  // packages/workit-core/src/autonomy.ts
10821
11664
  import os4 from "node:os";
10822
- import path11 from "node:path";
11665
+ import path12 from "node:path";
10823
11666
  function endpointOf(entry) {
10824
11667
  const raw = entry?.defaultEndpoint;
10825
11668
  if (raw === undefined)
@@ -10916,10 +11759,13 @@ var accountHome = () => {
10916
11759
  } catch {
10917
11760
  return os4.homedir();
10918
11761
  }
10919
- }, grantsHome, grantsDir = () => path11.join(grantsHome.resolve(), ".config", "workit"), grantsOverride = () => {
10920
- const configured = path11.resolve(resolveConfigDir());
10921
- return configured === path11.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 ${path11.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
10922
- }, 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) => {
11762
+ }, grantsHome, grantsDir = () => path12.join(grantsHome.resolve(), ".config", "workit"), grantsOverride = () => {
11763
+ const configured = path12.resolve(resolveConfigDir());
11764
+ return configured === path12.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 ${path12.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
11765
+ }, GRANT_KINDS, DEFAULT_GRANTS, DEFAULT_ENDPOINTS, DEFAULT_ENDPOINT = "commit", isDefaultEndpoint = (value) => typeof value === "string" && DEFAULT_ENDPOINTS.includes(value), VERIFICATION_MODES, DEFAULT_VERIFICATION = "proportional", verificationOf = (entry) => {
11766
+ const value = entry?.verification;
11767
+ return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
11768
+ }, isGrantValue = (value) => value === true || value === false || value === "verified", normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value, fromAutoApprove = (value) => {
10923
11769
  if (value === true)
10924
11770
  return { push: true, pr: true, merge: "verified" };
10925
11771
  if (!Array.isArray(value))
@@ -10946,6 +11792,11 @@ var init_autonomy = __esm(() => {
10946
11792
  rerun: true
10947
11793
  });
10948
11794
  DEFAULT_ENDPOINTS = ["commit", "pr", "green", "merged"];
11795
+ VERIFICATION_MODES = [
11796
+ "proportional",
11797
+ "self",
11798
+ "independent"
11799
+ ];
10949
11800
  });
10950
11801
 
10951
11802
  // node_modules/@openclaw/fs-safe/dist/errors.js
@@ -11058,7 +11909,7 @@ var init_read_open_flags = () => {};
11058
11909
  // node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
11059
11910
  import { randomBytes as randomBytes3 } from "node:crypto";
11060
11911
  import fsSync from "node:fs";
11061
- import path12 from "node:path";
11912
+ import path13 from "node:path";
11062
11913
  function parseSidecarLockSnapshot(snapshot, parser) {
11063
11914
  return snapshot && { ...snapshot, payload: parseSidecarLockPayload(snapshot.raw, parser) };
11064
11915
  }
@@ -11086,13 +11937,13 @@ ${ownershipToken}
11086
11937
  return { raw, ownershipToken };
11087
11938
  }
11088
11939
  function relativeSidecarLockPath(lockRoot, lockPath) {
11089
- const resolved = path12.resolve(lockPath);
11090
- const lexicalRelative = path12.relative(lockRoot.rootDir, resolved);
11091
- const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path12.sep}`) && !path12.isAbsolute(lexicalRelative) ? lexicalRelative : path12.relative(lockRoot.rootReal, resolved);
11092
- if (!relative || relative === ".." || relative.startsWith(`..${path12.sep}`) || path12.isAbsolute(relative)) {
11940
+ const resolved = path13.resolve(lockPath);
11941
+ const lexicalRelative = path13.relative(lockRoot.rootDir, resolved);
11942
+ const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path13.sep}`) && !path13.isAbsolute(lexicalRelative) ? lexicalRelative : path13.relative(lockRoot.rootReal, resolved);
11943
+ if (!relative || relative === ".." || relative.startsWith(`..${path13.sep}`) || path13.isAbsolute(relative)) {
11093
11944
  throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
11094
11945
  }
11095
- return relative.split(path12.sep).join(path12.posix.sep);
11946
+ return relative.split(path13.sep).join(path13.posix.sep);
11096
11947
  }
11097
11948
  function parseSidecarLockPayload(raw, parser) {
11098
11949
  if (parser) {
@@ -11262,7 +12113,7 @@ function sleepSync(ms) {
11262
12113
 
11263
12114
  // node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
11264
12115
  import fs10 from "node:fs";
11265
- import path13 from "node:path";
12116
+ import path14 from "node:path";
11266
12117
  function getSyncHeldLocks() {
11267
12118
  const globalWithState = globalThis;
11268
12119
  if (!globalWithState[SYNC_HELD_LOCKS_KEY]) {
@@ -11340,26 +12191,26 @@ function canonicalLockParentSync(parent) {
11340
12191
  return process.platform === "win32" ? fs10.realpathSync.native(parent) : fs10.realpathSync(parent);
11341
12192
  }
11342
12193
  function normalizeTargetPath(targetPath) {
11343
- const resolved = path13.resolve(targetPath);
11344
- fs10.mkdirSync(path13.dirname(resolved), { recursive: true });
12194
+ const resolved = path14.resolve(targetPath);
12195
+ fs10.mkdirSync(path14.dirname(resolved), { recursive: true });
11345
12196
  try {
11346
- return path13.join(canonicalLockParentSync(path13.dirname(resolved)), path13.basename(resolved));
12197
+ return path14.join(canonicalLockParentSync(path14.dirname(resolved)), path14.basename(resolved));
11347
12198
  } catch {
11348
12199
  return resolved;
11349
12200
  }
11350
12201
  }
11351
12202
  function boundedLockPath(lockPath, lockRoot) {
11352
- const resolved = path13.resolve(lockPath);
12203
+ const resolved = path14.resolve(lockPath);
11353
12204
  if (!lockRoot)
11354
12205
  return resolved;
11355
12206
  relativeSidecarLockPath(lockRoot, resolved);
11356
- const parent = path13.dirname(resolved);
12207
+ const parent = path14.dirname(resolved);
11357
12208
  const parentReal = canonicalLockParentSync(parent);
11358
- const parentRelative = path13.relative(lockRoot.rootReal, parentReal);
11359
- if (parentRelative === ".." || parentRelative.startsWith(`..${path13.sep}`) || path13.isAbsolute(parentRelative)) {
12209
+ const parentRelative = path14.relative(lockRoot.rootReal, parentReal);
12210
+ if (parentRelative === ".." || parentRelative.startsWith(`..${path14.sep}`) || path14.isAbsolute(parentRelative)) {
11360
12211
  throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
11361
12212
  }
11362
- return path13.join(parentReal, path13.basename(resolved));
12213
+ return path14.join(parentReal, path14.basename(resolved));
11363
12214
  }
11364
12215
  function defaultShouldReclaim(snapshot, staleMs, nowMs) {
11365
12216
  const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
@@ -11575,14 +12426,14 @@ var init_file_lock = __esm(() => {
11575
12426
  });
11576
12427
 
11577
12428
  // packages/workit-core/src/core/package-root.ts
11578
- import { existsSync as existsSync3 } from "node:fs";
11579
- import path14 from "node:path";
12429
+ import { existsSync as existsSync4 } from "node:fs";
12430
+ import path15 from "node:path";
11580
12431
  import { fileURLToPath } from "node:url";
11581
12432
  var packageRoot = () => {
11582
- let dir = path14.dirname(fileURLToPath(import.meta.url));
12433
+ let dir = path15.dirname(fileURLToPath(import.meta.url));
11583
12434
  while (true) {
11584
- const parent = path14.dirname(dir);
11585
- if (existsSync3(path14.join(dir, "package.json")) || parent === dir)
12435
+ const parent = path15.dirname(dir);
12436
+ if (existsSync4(path15.join(dir, "package.json")) || parent === dir)
11586
12437
  return dir;
11587
12438
  dir = parent;
11588
12439
  }
@@ -11592,7 +12443,7 @@ var init_package_root = () => {};
11592
12443
  // packages/workit-core/src/store/event-log.ts
11593
12444
  import { randomUUID as randomUUID2 } from "node:crypto";
11594
12445
  import fs11 from "node:fs";
11595
- import path15 from "node:path";
12446
+ import path16 from "node:path";
11596
12447
  function readLog(file, from = 0, expectSeq = null) {
11597
12448
  let fd;
11598
12449
  try {
@@ -11665,7 +12516,7 @@ function appendEvent(file, event, validEnd) {
11665
12516
  fs11.closeSync(fd);
11666
12517
  }
11667
12518
  if (created)
11668
- fsyncDirectory(path15.dirname(file));
12519
+ fsyncDirectory(path16.dirname(file));
11669
12520
  return validEnd;
11670
12521
  }
11671
12522
  function replaceFile(file, bytes, durable) {
@@ -11681,7 +12532,7 @@ function replaceFile(file, bytes, durable) {
11681
12532
  }
11682
12533
  renameWithRetry(temporary, file);
11683
12534
  if (durable)
11684
- fsyncDirectory(path15.dirname(file));
12535
+ fsyncDirectory(path16.dirname(file));
11685
12536
  } catch (error) {
11686
12537
  try {
11687
12538
  fs11.unlinkSync(temporary);
@@ -11991,7 +12842,7 @@ var init_notes = __esm(() => {
11991
12842
  // packages/workit-core/src/core/task-store.ts
11992
12843
  import { createHash as createHash11, randomUUID as randomUUID3 } from "node:crypto";
11993
12844
  import * as fs12 from "node:fs";
11994
- import path16 from "node:path";
12845
+ import path17 from "node:path";
11995
12846
 
11996
12847
  class TaskStore {
11997
12848
  root;
@@ -12003,7 +12854,7 @@ class TaskStore {
12003
12854
  migrationChecked = false;
12004
12855
  compactDue = new Set;
12005
12856
  constructor(root, options = {}) {
12006
- const resolved = path16.resolve(root);
12857
+ const resolved = path17.resolve(root);
12007
12858
  this.origin = fs12.existsSync(resolved) ? fs12.realpathSync(resolved) : resolved;
12008
12859
  this.located = resolveStore(this.origin);
12009
12860
  this.root = checkoutRootOf(this.origin, this.located);
@@ -12012,7 +12863,7 @@ class TaskStore {
12012
12863
  this.migrateOnRead = options.migrateOnRead;
12013
12864
  }
12014
12865
  location() {
12015
- if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs12.existsSync(path16.join(this.root, ".git"))) {
12866
+ if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs12.existsSync(path17.join(this.root, ".git"))) {
12016
12867
  this.located = undefined;
12017
12868
  this.migrationChecked = false;
12018
12869
  }
@@ -12168,7 +13019,7 @@ class TaskStore {
12168
13019
  const moved = this.bindLocked(again.data.id, key, input.now);
12169
13020
  return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
12170
13021
  }
12171
- const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path16.basename(this.root) || this.root;
13022
+ const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path17.basename(this.root) || this.root;
12172
13023
  const intent = {
12173
13024
  objective: input.objective?.trim() || `Work on ${label}`,
12174
13025
  scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
@@ -12234,14 +13085,14 @@ class TaskStore {
12234
13085
  const branch = key.ok ? key.data.branch : null;
12235
13086
  if (!branch || !location.ok)
12236
13087
  return success(null, null, null);
12237
- const common = path16.dirname(location.data.dir);
12238
- const heads = path16.join(common, "logs", "refs", "heads");
12239
- const headLog = location.data.git ? path16.join(location.data.git.gitDir, "logs", "HEAD") : "";
13088
+ const common = path17.dirname(location.data.dir);
13089
+ const heads = path17.join(common, "logs", "refs", "heads");
13090
+ const headLog = location.data.git ? path17.join(location.data.git.gitDir, "logs", "HEAD") : "";
12240
13091
  const signatures = [
12241
- path16.join(heads, branch),
13092
+ path17.join(heads, branch),
12242
13093
  headLog,
12243
13094
  heads,
12244
- path16.join(common, "packed-refs")
13095
+ path17.join(common, "packed-refs")
12245
13096
  ].map((file) => fileSignature(file) ?? "-");
12246
13097
  const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
12247
13098
  let resolved = origins.get(slot);
@@ -12253,7 +13104,7 @@ class TaskStore {
12253
13104
  aliases.push(rename.from);
12254
13105
  const start = branchStart(location.data, branch, aliases);
12255
13106
  const slash = start?.indexOf("/") ?? -1;
12256
- const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path16.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
13107
+ const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path17.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
12257
13108
  const name = start && (remote ? start.slice(slash + 1) : start);
12258
13109
  resolved = {
12259
13110
  signature: signatures.join("|"),
@@ -12491,13 +13342,13 @@ class TaskStore {
12491
13342
  if (!ready.ok)
12492
13343
  return ready;
12493
13344
  const dirs = this.paths();
12494
- const recovery = path16.join(this.root, ".workit", "recovery");
13345
+ const recovery = path17.join(this.root, ".workit", "recovery");
12495
13346
  if (fs12.existsSync(recovery)) {
12496
13347
  let files = 0;
12497
13348
  let bytes = 0;
12498
13349
  try {
12499
13350
  for (const name of fs12.readdirSync(recovery)) {
12500
- const stat = fs12.lstatSync(path16.join(recovery, name));
13351
+ const stat = fs12.lstatSync(path17.join(recovery, name));
12501
13352
  if (!stat.isFile())
12502
13353
  continue;
12503
13354
  files += 1;
@@ -12550,7 +13401,7 @@ class TaskStore {
12550
13401
  if (fs12.existsSync(dirs.blobs))
12551
13402
  for (const name of fs12.readdirSync(dirs.blobs)) {
12552
13403
  const digest = name.replace(/\.json$/, "");
12553
- const file = path16.join(dirs.blobs, name);
13404
+ const file = path17.join(dirs.blobs, name);
12554
13405
  let stat;
12555
13406
  try {
12556
13407
  stat = fs12.lstatSync(file);
@@ -12568,7 +13419,7 @@ class TaskStore {
12568
13419
  }
12569
13420
  return success(null, null, null);
12570
13421
  };
12571
- const swept = dryRun ? sweep() : this.withLock(path16.join(dirs.dir, "gc.lock"), sweep);
13422
+ const swept = dryRun ? sweep() : this.withLock(path17.join(dirs.dir, "gc.lock"), sweep);
12572
13423
  if (!swept.ok)
12573
13424
  return swept;
12574
13425
  const temporaryDirs = [
@@ -12576,7 +13427,7 @@ class TaskStore {
12576
13427
  dirs.tasks,
12577
13428
  dirs.blobs,
12578
13429
  dirs.checkout,
12579
- ...names.map((name) => path16.join(dirs.tasks, name))
13430
+ ...names.map((name) => path17.join(dirs.tasks, name))
12580
13431
  ];
12581
13432
  for (const directory of temporaryDirs) {
12582
13433
  if (!fs12.existsSync(directory))
@@ -12584,7 +13435,7 @@ class TaskStore {
12584
13435
  for (const name of fs12.readdirSync(directory)) {
12585
13436
  if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
12586
13437
  continue;
12587
- const file = path16.join(directory, name);
13438
+ const file = path17.join(directory, name);
12588
13439
  try {
12589
13440
  const stat = fs12.lstatSync(file);
12590
13441
  if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
@@ -12601,30 +13452,30 @@ class TaskStore {
12601
13452
  }
12602
13453
  paths() {
12603
13454
  const location = this.location();
12604
- const dir = location.ok ? location.data.dir : path16.join(this.root, ".workit");
12605
- const checkout = path16.join(dir, "checkouts", checkoutSlug(this.root));
13455
+ const dir = location.ok ? location.data.dir : path17.join(this.root, ".workit");
13456
+ const checkout = path17.join(dir, "checkouts", checkoutSlug(this.root));
12606
13457
  return {
12607
13458
  dir,
12608
13459
  shared: location.ok ? location.data.shared : false,
12609
- marker: path16.join(dir, "store.json"),
13460
+ marker: path17.join(dir, "store.json"),
12610
13461
  checkout,
12611
- workspace: path16.join(checkout, "workspace.json"),
12612
- checkoutLock: path16.join(checkout, "metadata.lock"),
12613
- tasks: path16.join(dir, "tasks"),
12614
- index: path16.join(dir, "task-index.json"),
12615
- blobs: path16.join(dir, "blobs", "candidates"),
12616
- legacy: path16.join(dir, "legacy", checkoutSlug(this.root), "v2"),
12617
- v2: path16.join(this.root, ".workit")
13462
+ workspace: path17.join(checkout, "workspace.json"),
13463
+ checkoutLock: path17.join(checkout, "metadata.lock"),
13464
+ tasks: path17.join(dir, "tasks"),
13465
+ index: path17.join(dir, "task-index.json"),
13466
+ blobs: path17.join(dir, "blobs", "candidates"),
13467
+ legacy: path17.join(dir, "legacy", checkoutSlug(this.root), "v2"),
13468
+ v2: path17.join(this.root, ".workit")
12618
13469
  };
12619
13470
  }
12620
13471
  eventsPath(taskId) {
12621
- return path16.join(this.paths().tasks, taskId, "events.jsonl");
13472
+ return path17.join(this.paths().tasks, taskId, "events.jsonl");
12622
13473
  }
12623
13474
  snapshotPath(taskId) {
12624
- return path16.join(this.paths().tasks, taskId, "snapshot.json");
13475
+ return path17.join(this.paths().tasks, taskId, "snapshot.json");
12625
13476
  }
12626
13477
  taskLockPath(taskId) {
12627
- return path16.join(this.paths().tasks, taskId, "lock");
13478
+ return path17.join(this.paths().tasks, taskId, "lock");
12628
13479
  }
12629
13480
  ready(write = false) {
12630
13481
  const location = this.location();
@@ -12663,13 +13514,13 @@ class TaskStore {
12663
13514
  return {
12664
13515
  read: (digest) => {
12665
13516
  try {
12666
- return JSON.parse(fs12.readFileSync(path16.join(dir, `${digest}.json`), "utf8"));
13517
+ return JSON.parse(fs12.readFileSync(path17.join(dir, `${digest}.json`), "utf8"));
12667
13518
  } catch (error) {
12668
13519
  throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
12669
13520
  }
12670
13521
  },
12671
13522
  write: (digest, value) => {
12672
- const file = path16.join(dir, `${digest}.json`);
13523
+ const file = path17.join(dir, `${digest}.json`);
12673
13524
  if (fs12.existsSync(file)) {
12674
13525
  const stamp = new Date;
12675
13526
  try {
@@ -12861,7 +13712,7 @@ class TaskStore {
12861
13712
  }
12862
13713
  }
12863
13714
  openTask(record, type, key, legacy = null) {
12864
- const dir = path16.join(this.paths().tasks, record.id);
13715
+ const dir = path17.join(this.paths().tasks, record.id);
12865
13716
  fs12.mkdirSync(dir, { recursive: true });
12866
13717
  const blobs = this.blobStore();
12867
13718
  const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
@@ -12954,7 +13805,7 @@ class TaskStore {
12954
13805
  }
12955
13806
  withKeyLock(key, operation) {
12956
13807
  const digest = createHash11("sha256").update(key).digest("hex").slice(0, 32);
12957
- return this.withLock(path16.join(this.paths().dir, "keys", `${digest}.lock`), operation);
13808
+ return this.withLock(path17.join(this.paths().dir, "keys", `${digest}.lock`), operation);
12958
13809
  }
12959
13810
  bindLocked(taskId, key, timestamp) {
12960
13811
  const current = this.readWorkspace();
@@ -13095,7 +13946,7 @@ class TaskStore {
13095
13946
  writeWorkspace(record) {
13096
13947
  const file = this.paths().workspace;
13097
13948
  try {
13098
- fs12.mkdirSync(path16.dirname(file), { recursive: true });
13949
+ fs12.mkdirSync(path17.dirname(file), { recursive: true });
13099
13950
  replaceFile(file, `${canonicalJson(record)}
13100
13951
  `, true);
13101
13952
  return success(null, null, null);
@@ -13173,23 +14024,23 @@ class TaskStore {
13173
14024
  return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
13174
14025
  }
13175
14026
  v2Dirs() {
13176
- return [...new Set([this.root, this.origin])].map((dir) => path16.join(dir, ".workit"));
14027
+ return [...new Set([this.root, this.origin])].map((dir) => path17.join(dir, ".workit"));
13177
14028
  }
13178
14029
  pendingMigrations() {
13179
14030
  return this.v2Dirs().filter((dir) => {
13180
- const raw = this.readV2Workspace(path16.join(dir, "workspace.json"));
14031
+ const raw = this.readV2Workspace(path17.join(dir, "workspace.json"));
13181
14032
  return raw !== null && raw !== TOMBSTONE;
13182
14033
  });
13183
14034
  }
13184
14035
  migrateV2(v2) {
13185
14036
  const dirs = this.paths();
13186
- const v2Workspace = path16.join(v2, "workspace.json");
13187
- const v2Tasks = path16.join(v2, "tasks");
13188
- const legacy = path16.join(dirs.dir, "legacy", checkoutSlug(path16.dirname(v2)), "v2");
14037
+ const v2Workspace = path17.join(v2, "workspace.json");
14038
+ const v2Tasks = path17.join(v2, "tasks");
14039
+ const legacy = path17.join(dirs.dir, "legacy", checkoutSlug(path17.dirname(v2)), "v2");
13189
14040
  const raw = this.readV2Workspace(v2Workspace);
13190
14041
  if (raw === null || raw === TOMBSTONE)
13191
14042
  return success(null, null, null);
13192
- return this.withLock(dirs.checkoutLock, () => this.withLock(path16.join(v2, "metadata.lock"), () => {
14043
+ return this.withLock(dirs.checkoutLock, () => this.withLock(path17.join(v2, "metadata.lock"), () => {
13193
14044
  const workspace = this.readV2Workspace(v2Workspace);
13194
14045
  if (workspace === null || workspace === TOMBSTONE)
13195
14046
  return success(null, null, null);
@@ -13198,7 +14049,7 @@ class TaskStore {
13198
14049
  return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
13199
14050
  let files = [];
13200
14051
  try {
13201
- files = fs12.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path16.join(v2Tasks, name)));
14052
+ files = fs12.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path17.join(v2Tasks, name)));
13202
14053
  } catch {}
13203
14054
  const current = this.readWorkspace();
13204
14055
  if (!current.ok)
@@ -13216,21 +14067,21 @@ class TaskStore {
13216
14067
  let skipped = 0;
13217
14068
  const done = new Map;
13218
14069
  try {
13219
- fs12.mkdirSync(path16.join(legacy, "tasks"), { recursive: true });
14070
+ fs12.mkdirSync(path17.join(legacy, "tasks"), { recursive: true });
13220
14071
  for (const name of ["workspace.json", "index.json"])
13221
- if (fs12.existsSync(path16.join(v2, name)))
13222
- fs12.copyFileSync(path16.join(v2, name), path16.join(legacy, name));
14072
+ if (fs12.existsSync(path17.join(v2, name)))
14073
+ fs12.copyFileSync(path17.join(v2, name), path17.join(legacy, name));
13223
14074
  for (const name of files) {
13224
14075
  const id = name.slice(0, -5);
13225
- const file = path16.join(v2Tasks, name);
14076
+ const file = path17.join(v2Tasks, name);
13226
14077
  const bytes = fs12.readFileSync(file, "utf8");
13227
14078
  const digest = sha256(bytes);
13228
- fs12.writeFileSync(path16.join(legacy, "tasks", name), bytes);
14079
+ fs12.writeFileSync(path17.join(legacy, "tasks", name), bytes);
13229
14080
  const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
13230
14081
  if (!parsed.ok || parsed.data.id !== id)
13231
14082
  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 });
13232
14083
  const record = { ...parsed.data, workspaceId: target.id };
13233
- const origin = { path: path16.join(legacy, "tasks", name), digest };
14084
+ const origin = { path: path17.join(legacy, "tasks", name), digest };
13234
14085
  if (!fs12.existsSync(this.eventsPath(id))) {
13235
14086
  this.openTask(record, "migrated.from_v2", null, origin);
13236
14087
  migrated += 1;
@@ -13266,7 +14117,7 @@ class TaskStore {
13266
14117
  try {
13267
14118
  let complete = true;
13268
14119
  for (const [name, digest] of done) {
13269
- const file = path16.join(v2Tasks, name);
14120
+ const file = path17.join(v2Tasks, name);
13270
14121
  if (sha256(fs12.readFileSync(file, "utf8")) !== digest) {
13271
14122
  complete = false;
13272
14123
  continue;
@@ -13277,7 +14128,7 @@ class TaskStore {
13277
14128
  return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
13278
14129
  path: v2
13279
14130
  });
13280
- fs12.rmSync(path16.join(v2, "index.json"), { force: true });
14131
+ fs12.rmSync(path17.join(v2, "index.json"), { force: true });
13281
14132
  this.writeTombstone(v2, target.id);
13282
14133
  } catch (error) {
13283
14134
  return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
@@ -13297,7 +14148,7 @@ class TaskStore {
13297
14148
  }
13298
14149
  adoptLocalStore() {
13299
14150
  const dirs = this.paths();
13300
- const marker = path16.join(dirs.v2, "store.json");
14151
+ const marker = path17.join(dirs.v2, "store.json");
13301
14152
  if (!dirs.shared || !fs12.existsSync(marker))
13302
14153
  return success(null, null, null);
13303
14154
  return this.withLock(dirs.checkoutLock, () => {
@@ -13307,7 +14158,7 @@ class TaskStore {
13307
14158
  try {
13308
14159
  const merge = (from, to) => {
13309
14160
  if (!fs12.existsSync(to)) {
13310
- fs12.mkdirSync(path16.dirname(to), { recursive: true });
14161
+ fs12.mkdirSync(path17.dirname(to), { recursive: true });
13311
14162
  try {
13312
14163
  fs12.renameSync(from, to);
13313
14164
  } catch {
@@ -13318,24 +14169,24 @@ class TaskStore {
13318
14169
  }
13319
14170
  if (fs12.lstatSync(from).isDirectory() && fs12.lstatSync(to).isDirectory())
13320
14171
  for (const name of fs12.readdirSync(from))
13321
- merge(path16.join(from, name), path16.join(to, name));
14172
+ merge(path17.join(from, name), path17.join(to, name));
13322
14173
  };
13323
14174
  for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
13324
- const from = path16.join(dirs.v2, sub);
14175
+ const from = path17.join(dirs.v2, sub);
13325
14176
  if (!fs12.existsSync(from))
13326
14177
  continue;
13327
14178
  if (sub === "tasks") {
13328
14179
  for (const name of fs12.readdirSync(from))
13329
- if (validId(name) && !fs12.existsSync(path16.join(dirs.dir, sub, name)))
14180
+ if (validId(name) && !fs12.existsSync(path17.join(dirs.dir, sub, name)))
13330
14181
  tasks += 1;
13331
14182
  }
13332
- merge(from, path16.join(dirs.dir, sub));
14183
+ merge(from, path17.join(dirs.dir, sub));
13333
14184
  }
13334
- const ledger = path16.join(dirs.v2, "ledger");
13335
- if (fs12.existsSync(ledger) && !fs12.existsSync(path16.join(dirs.dir, "ledger")))
13336
- merge(ledger, path16.join(dirs.dir, "ledger"));
13337
- fs12.rmSync(path16.join(dirs.v2, "task-index.json"), { force: true });
13338
- fs12.renameSync(marker, path16.join(dirs.v2, "store.moved.json"));
14185
+ const ledger = path17.join(dirs.v2, "ledger");
14186
+ if (fs12.existsSync(ledger) && !fs12.existsSync(path17.join(dirs.dir, "ledger")))
14187
+ merge(ledger, path17.join(dirs.dir, "ledger"));
14188
+ fs12.rmSync(path17.join(dirs.v2, "task-index.json"), { force: true });
14189
+ fs12.renameSync(marker, path17.join(dirs.v2, "store.moved.json"));
13339
14190
  } catch (error) {
13340
14191
  return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
13341
14192
  path: dirs.v2
@@ -13373,7 +14224,7 @@ class TaskStore {
13373
14224
  schemaVersion: SCHEMA_VERSION,
13374
14225
  id: workspaceId ?? newId(),
13375
14226
  revision: newRevision(),
13376
- root: path16.dirname(v2),
14227
+ root: path17.dirname(v2),
13377
14228
  runtime: { createdWith: version, updatedWith: version },
13378
14229
  writer: null,
13379
14230
  store: {
@@ -13385,11 +14236,11 @@ class TaskStore {
13385
14236
  critical: ["store"]
13386
14237
  };
13387
14238
  fs12.mkdirSync(v2, { recursive: true });
13388
- const ignore = path16.join(v2, ".gitignore");
14239
+ const ignore = path17.join(v2, ".gitignore");
13389
14240
  if (!fs12.existsSync(ignore))
13390
14241
  fs12.writeFileSync(ignore, `*
13391
14242
  `);
13392
- replaceFile(path16.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
14243
+ replaceFile(path17.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
13393
14244
  `, true);
13394
14245
  }
13395
14246
  stubFor(taskId) {
@@ -13413,8 +14264,8 @@ class TaskStore {
13413
14264
  }
13414
14265
  writeTombstoneIfMissing() {
13415
14266
  try {
13416
- const v2 = path16.join(this.root, ".workit");
13417
- if (fs12.existsSync(path16.join(v2, "workspace.json")))
14267
+ const v2 = path17.join(this.root, ".workit");
14268
+ if (fs12.existsSync(path17.join(v2, "workspace.json")))
13418
14269
  return;
13419
14270
  let id = null;
13420
14271
  try {
@@ -13431,7 +14282,7 @@ class TaskStore {
13431
14282
  replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
13432
14283
  `, false);
13433
14284
  if (!dirs.shared) {
13434
- const ignore = path16.join(dirs.dir, ".gitignore");
14285
+ const ignore = path17.join(dirs.dir, ".gitignore");
13435
14286
  let current = null;
13436
14287
  try {
13437
14288
  current = fs12.readFileSync(ignore, "utf8");
@@ -13491,7 +14342,7 @@ class TaskStore {
13491
14342
  withLock(lockPath, operation) {
13492
14343
  try {
13493
14344
  this.initializeStorage();
13494
- fs12.mkdirSync(path16.dirname(lockPath), { recursive: true });
14345
+ fs12.mkdirSync(path17.dirname(lockPath), { recursive: true });
13495
14346
  } catch (error) {
13496
14347
  return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
13497
14348
  path: this.paths().dir
@@ -13554,7 +14405,7 @@ class TaskStore {
13554
14405
  if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
13555
14406
  return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
13556
14407
  path: lockPath,
13557
- guidance: `Check permissions and the read-only attribute on ${path16.dirname(lockPath)}.`
14408
+ guidance: `Check permissions and the read-only attribute on ${path17.dirname(lockPath)}.`
13558
14409
  });
13559
14410
  if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
13560
14411
  let lock = null;
@@ -13612,7 +14463,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
13612
14463
  }, 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 = () => {
13613
14464
  if (cachedRuntimeVersion === null) {
13614
14465
  try {
13615
- const pkg = JSON.parse(fs12.readFileSync(path16.join(packageRoot(), "package.json"), "utf8"));
14466
+ const pkg = JSON.parse(fs12.readFileSync(path17.join(packageRoot(), "package.json"), "utf8"));
13616
14467
  cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
13617
14468
  } catch {
13618
14469
  cachedRuntimeVersion = "0.0.0";
@@ -13639,7 +14490,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
13639
14490
  return "";
13640
14491
  }
13641
14492
  }, REFLOG_LINE, branchStart = (location, branch, aliases) => {
13642
- const first = readText(path16.join(path16.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
14493
+ const first = readText(path17.join(path17.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
13643
14494
  `, 1)[0];
13644
14495
  const created = REFLOG_LINE.exec(first);
13645
14496
  const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
@@ -13649,7 +14500,7 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
13649
14500
  return start;
13650
14501
  if (!location.git)
13651
14502
  return null;
13652
- for (const line of readText(path16.join(location.git.gitDir, "logs", "HEAD")).split(`
14503
+ for (const line of readText(path17.join(location.git.gitDir, "logs", "HEAD")).split(`
13653
14504
  `)) {
13654
14505
  const entry = REFLOG_LINE.exec(line);
13655
14506
  const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
@@ -13658,12 +14509,12 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
13658
14509
  return moved[1];
13659
14510
  }
13660
14511
  return null;
13661
- }, refExists = (common, ref) => fs12.existsSync(path16.join(common, ref)) || readText(path16.join(common, "packed-refs")).includes(` ${ref}
14512
+ }, refExists = (common, ref) => fs12.existsSync(path17.join(common, ref)) || readText(path17.join(common, "packed-refs")).includes(` ${ref}
13662
14513
  `), origins, sameDirectoryIdentity = (left, right) => {
13663
- if (!path16.isAbsolute(left) || !path16.isAbsolute(right))
14514
+ if (!path17.isAbsolute(left) || !path17.isAbsolute(right))
13664
14515
  return false;
13665
- const normalizedLeft = path16.resolve(left);
13666
- const normalizedRight = path16.resolve(right);
14516
+ const normalizedLeft = path17.resolve(left);
14517
+ const normalizedRight = path17.resolve(right);
13667
14518
  try {
13668
14519
  const a = fs12.statSync(normalizedLeft, { bigint: true });
13669
14520
  const b = fs12.statSync(normalizedRight, { bigint: true });
@@ -13721,10 +14572,10 @@ var init_workers = __esm(() => {
13721
14572
  // packages/workit-core/src/check-config.ts
13722
14573
  import { spawnSync as spawnSync9 } from "node:child_process";
13723
14574
  import fs13 from "node:fs";
13724
- import path17 from "node:path";
14575
+ import path18 from "node:path";
13725
14576
  function checksConfigFile(root) {
13726
- const current = path17.join(root, CHECKS_FILE);
13727
- const legacy = path17.join(root, LEGACY_CHECKS_FILE);
14577
+ const current = path18.join(root, CHECKS_FILE);
14578
+ const legacy = path18.join(root, LEGACY_CHECKS_FILE);
13728
14579
  return !fs13.existsSync(current) && fs13.existsSync(legacy) ? legacy : current;
13729
14580
  }
13730
14581
  function repoHeavyCommands(root) {
@@ -13774,12 +14625,12 @@ function splitCommand(command) {
13774
14625
  return argv.length ? argv : new Error("empty command");
13775
14626
  }
13776
14627
  function checkRoot(cwd) {
13777
- return path17.resolve(git4(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
14628
+ return path18.resolve(git5(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
13778
14629
  }
13779
14630
  function detectedChecks(root) {
13780
14631
  const read = (file) => {
13781
14632
  try {
13782
- return fs13.readFileSync(path17.join(root, file), "utf8");
14633
+ return fs13.readFileSync(path18.join(root, file), "utf8");
13783
14634
  } catch {
13784
14635
  return null;
13785
14636
  }
@@ -13818,7 +14669,7 @@ function detectedChecks(root) {
13818
14669
  function loadCheckConfig(cwd) {
13819
14670
  const root = checkRoot(cwd);
13820
14671
  const file = checksConfigFile(root);
13821
- const legacy = path17.basename(file) === LEGACY_CHECKS_FILE;
14672
+ const legacy = path18.basename(file) === LEGACY_CHECKS_FILE;
13822
14673
  const label = legacy ? LEGACY_CHECKS_FILE : CHECKS_FILE;
13823
14674
  const note = legacy ? {
13824
14675
  note: `${LEGACY_CHECKS_FILE} is deprecated; move it to ${CHECKS_FILE} (workit doctor --fix)`
@@ -13894,10 +14745,10 @@ function workitAvailable(env = process.env) {
13894
14745
  return workitOnPath;
13895
14746
  const windows = process.platform === "win32";
13896
14747
  const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
13897
- const dirs = (env.PATH ?? env.Path ?? "").split(path17.delimiter).filter(Boolean);
14748
+ const dirs = (env.PATH ?? env.Path ?? "").split(path18.delimiter).filter(Boolean);
13898
14749
  workitOnPath = dirs.some((dir) => exts.some((ext) => {
13899
14750
  try {
13900
- return fs13.statSync(path17.join(dir, `workit${ext}`)).isFile();
14751
+ return fs13.statSync(path18.join(dir, `workit${ext}`)).isFile();
13901
14752
  } catch {
13902
14753
  return false;
13903
14754
  }
@@ -13907,7 +14758,7 @@ function workitAvailable(env = process.env) {
13907
14758
  function checkCommand(args, version, onPath = workitAvailable()) {
13908
14759
  return onPath ? `workit check ${args}` : `npx -y @brainervirus/workit-cli@${version} check ${args}`;
13909
14760
  }
13910
- var SCRIPT_CHECKS, CHECKS_FILE = ".workit/checks.json", LEGACY_CHECKS_FILE = "workit.checks.json", isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SHELL_META, git4 = (cwd, args) => {
14761
+ var SCRIPT_CHECKS, CHECKS_FILE = ".workit/checks.json", LEGACY_CHECKS_FILE = "workit.checks.json", isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SHELL_META, git5 = (cwd, args) => {
13911
14762
  const run = spawnSync9("git", args, {
13912
14763
  cwd,
13913
14764
  encoding: "utf8",
@@ -13918,7 +14769,7 @@ var SCRIPT_CHECKS, CHECKS_FILE = ".workit/checks.json", LEGACY_CHECKS_FILE = "wo
13918
14769
  const value = run.status === 0 ? (run.stdout ?? "").trim() : "";
13919
14770
  return value || null;
13920
14771
  }, packageManager = (root) => {
13921
- const has = (file) => fs13.existsSync(path17.join(root, file));
14772
+ const has = (file) => fs13.existsSync(path18.join(root, file));
13922
14773
  if (has("bun.lock") || has("bun.lockb"))
13923
14774
  return "bun";
13924
14775
  if (has("pnpm-lock.yaml"))
@@ -13944,7 +14795,7 @@ var init_check_config = __esm(() => {
13944
14795
  import { createHash as createHash12 } from "node:crypto";
13945
14796
  import * as fs14 from "node:fs";
13946
14797
  import { spawnSync as spawnSync10 } from "node:child_process";
13947
- import path18 from "node:path";
14798
+ import path19 from "node:path";
13948
14799
  function captureCandidate(root, scope, environment = []) {
13949
14800
  let checkout;
13950
14801
  try {
@@ -13986,7 +14837,7 @@ function captureCandidate(root, scope, environment = []) {
13986
14837
  for (const item of [...names].toSorted()) {
13987
14838
  if (!scopeMatches(item, normalizedScope))
13988
14839
  continue;
13989
- const target = path18.join(checkout, item);
14840
+ const target = path19.join(checkout, item);
13990
14841
  try {
13991
14842
  const stat = fs14.lstatSync(target);
13992
14843
  if (stat.isSymbolicLink()) {
@@ -14395,12 +15246,12 @@ function boundTaskSummary(summary) {
14395
15246
  brief.truncated = JSON.stringify(brief).includes("…") || progress.blockers.length > BRIEF_BLOCKERS || unmet.length > BRIEF_OPEN;
14396
15247
  return brief;
14397
15248
  }
14398
- var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path18.sep}`), relative = (root, candidate) => path18.relative(root, candidate).split(path18.sep).join("/") || ".", canonicalPath = (value) => {
15249
+ var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path19.sep}`), relative = (root, candidate) => path19.relative(root, candidate).split(path19.sep).join("/") || ".", canonicalPath = (value) => {
14399
15250
  if (typeof value !== "string" || !value)
14400
15251
  return null;
14401
15252
  if (value.includes("\\") || value.split("/").includes(".."))
14402
15253
  return null;
14403
- const normalized = path18.posix.normalize(value);
15254
+ const normalized = path19.posix.normalize(value);
14404
15255
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
14405
15256
  return null;
14406
15257
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -14442,14 +15293,14 @@ var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"),
14442
15293
  git: !/not a git repository/i.test(stderr)
14443
15294
  };
14444
15295
  }
14445
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")) : [];
15296
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path19.sep).join("/")) : [];
14446
15297
  const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
14447
15298
  if (stagedDeleted.status !== 0) {
14448
15299
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
14449
15300
  if (!/not a git repository/i.test(stderr))
14450
15301
  return { paths, uncertain: true, git: true };
14451
15302
  } else if (stagedDeleted.stdout) {
14452
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")));
15303
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path19.sep).join("/")));
14453
15304
  }
14454
15305
  return { paths: [...new Set(paths)], uncertain: false, git: true };
14455
15306
  }, walk = (root, directory, output) => {
@@ -14463,7 +15314,7 @@ var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"),
14463
15314
  for (const name of names) {
14464
15315
  if (name === ".git" || name === ".workit")
14465
15316
  continue;
14466
- const target = path18.join(directory, name);
15317
+ const target = path19.join(directory, name);
14467
15318
  let stat;
14468
15319
  try {
14469
15320
  stat = fs14.lstatSync(target);
@@ -14480,12 +15331,12 @@ var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"),
14480
15331
  }, scopeRoots = (root, scope) => {
14481
15332
  const roots = [];
14482
15333
  for (const item of scope.paths.length ? scope.paths : ["."]) {
14483
- const target = path18.resolve(root, item);
15334
+ const target = path19.resolve(root, item);
14484
15335
  if (!inside(root, target))
14485
15336
  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` }] });
14486
15337
  let ancestor = target;
14487
15338
  while (!fs14.existsSync(ancestor) && ancestor !== root)
14488
- ancestor = path18.dirname(ancestor);
15339
+ ancestor = path19.dirname(ancestor);
14489
15340
  try {
14490
15341
  if (!inside(root, fs14.realpathSync(ancestor)))
14491
15342
  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` }] });
@@ -14549,23 +15400,23 @@ var digestBytes = (value) => createHash12("sha256").update(value).digest("hex"),
14549
15400
  continue;
14550
15401
  if (!reference.digest)
14551
15402
  return failure2("invalid_input", "document references require a byte digest");
14552
- const target = path18.resolve(root, reference.path);
14553
- if (target !== root && !target.startsWith(`${root}${path18.sep}`))
15403
+ const target = path19.resolve(root, reference.path);
15404
+ if (target !== root && !target.startsWith(`${root}${path19.sep}`))
14554
15405
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
14555
15406
  fields: [
14556
15407
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
14557
15408
  ]
14558
15409
  });
14559
15410
  try {
14560
- const below = path18.relative(root, target);
15411
+ const below = path19.relative(root, target);
14561
15412
  let current = root;
14562
- for (const segment of below.split(path18.sep)) {
15413
+ for (const segment of below.split(path19.sep)) {
14563
15414
  if (!segment)
14564
15415
  continue;
14565
- current = path18.join(current, segment);
15416
+ current = path19.join(current, segment);
14566
15417
  const stat = fs14.lstatSync(current);
14567
15418
  const real = fs14.realpathSync(current);
14568
- if (real !== root && !real.startsWith(`${root}${path18.sep}`))
15419
+ if (real !== root && !real.startsWith(`${root}${path19.sep}`))
14569
15420
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
14570
15421
  fields: [
14571
15422
  {
@@ -14693,9 +15544,9 @@ Autonomy contract:
14693
15544
  Prose is a note: only a configured check the CLI ran (\`workit check <name>\`)
14694
15545
  satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
14695
15546
  - Author is not verifier: your own verdict is \`--self\`, shown as
14696
- self-reviewed, never verified. High risk, \`verification: independent\` and
14697
- merge need a verifier with its own host-derived session (a subagent's
14698
- \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
15547
+ self-reviewed, never verified. High risk, a focused or full lane and
15548
+ merge under \`verification: self|independent\` need a verifier with its
15549
+ own host-derived session (a subagent's \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
14699
15550
  - A local-commit endpoint does not imply PR readiness, and a local commit alone
14700
15551
  is not evidence of a requested remote push. Before reporting delivery,
14701
15552
  reconcile every requested item and observe it (\`workit verify-delivery\`).
@@ -16566,7 +17417,7 @@ var support = (descriptor, axis) => {
16566
17417
  // packages/workit-core/src/hooks/context.ts
16567
17418
  import { createHash as createHash13 } from "node:crypto";
16568
17419
  import fs15 from "node:fs";
16569
- import path19 from "node:path";
17420
+ import path20 from "node:path";
16570
17421
  function sessionCompactContext(store, session, context, selection = "session-bound") {
16571
17422
  const listed = store.listTaskIndex();
16572
17423
  if (!listed.ok)
@@ -16601,7 +17452,7 @@ function entryCompactContext(store, entry, session, context) {
16601
17452
  return null;
16602
17453
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
16603
17454
  const observed = task?.data.evidence.some(isObservedCheck) ?? false;
16604
- const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path19.resolve(store.root, ref.path)] : []));
17455
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path20.resolve(store.root, ref.path)] : []));
16605
17456
  const observedFiles = filesKey(files);
16606
17457
  const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
16607
17458
  if (!compact.ok || !compact.data)
@@ -16707,13 +17558,13 @@ ${options.addendum}` : ""}
16707
17558
  if (location instanceof Error)
16708
17559
  return null;
16709
17560
  const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
16710
- return path19.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
17561
+ return path20.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
16711
17562
  } catch {
16712
17563
  return null;
16713
17564
  }
16714
17565
  }, writeTurnDigest = (file, digest) => {
16715
17566
  try {
16716
- fs15.mkdirSync(path19.dirname(file), { recursive: true });
17567
+ fs15.mkdirSync(path20.dirname(file), { recursive: true });
16717
17568
  fs15.writeFileSync(file, `${JSON.stringify({ digest })}
16718
17569
  `);
16719
17570
  } catch {}
@@ -16726,7 +17577,7 @@ ${options.addendum}` : ""}
16726
17577
  for (const name of fs15.readdirSync(dir)) {
16727
17578
  if (!name.startsWith("turn-"))
16728
17579
  continue;
16729
- const file = path19.join(dir, name);
17580
+ const file = path20.join(dir, name);
16730
17581
  try {
16731
17582
  if (now - fs15.statSync(file).mtimeMs > TURN_TTL_MS)
16732
17583
  fs15.rmSync(file, { force: true });
@@ -16754,7 +17605,7 @@ ${options.addendum}` : ""}
16754
17605
  const file = turnMarker(input);
16755
17606
  if (!file)
16756
17607
  return;
16757
- pruneTurnDigests(path19.dirname(file), Date.now());
17608
+ pruneTurnDigests(path20.dirname(file), Date.now());
16758
17609
  if (text)
16759
17610
  writeTurnDigest(file, digestOf(text));
16760
17611
  else
@@ -16776,7 +17627,7 @@ var init_context = __esm(() => {
16776
17627
  // packages/workit-core/src/hooks/session-marker.ts
16777
17628
  import { createHash as createHash14 } from "node:crypto";
16778
17629
  import fs16 from "node:fs";
16779
- import path20 from "node:path";
17630
+ import path21 from "node:path";
16780
17631
  var sessionMarkerFile = (input, session, store) => {
16781
17632
  let dir = store;
16782
17633
  if (dir === undefined) {
@@ -16786,7 +17637,7 @@ var sessionMarkerFile = (input, session, store) => {
16786
17637
  dir = location.dir;
16787
17638
  }
16788
17639
  const name = createHash14("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
16789
- return path20.join(dir, "hooks", `skills-${name}.json`);
17640
+ return path21.join(dir, "hooks", `skills-${name}.json`);
16790
17641
  }, list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [], EMPTY, readRaw = (file) => {
16791
17642
  try {
16792
17643
  return fs16.readFileSync(file, "utf8");
@@ -16807,7 +17658,7 @@ var sessionMarkerFile = (input, session, store) => {
16807
17658
  return { ...EMPTY };
16808
17659
  }
16809
17660
  }, readSessionMarker = (file) => parseMarker(readRaw(file)), writeAtomic = (file, marker) => {
16810
- fs16.mkdirSync(path20.dirname(file), { recursive: true });
17661
+ fs16.mkdirSync(path21.dirname(file), { recursive: true });
16811
17662
  const temp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
16812
17663
  try {
16813
17664
  fs16.writeFileSync(temp, JSON.stringify(marker));
@@ -17277,7 +18128,7 @@ import { spawnSync as spawnSync11 } from "node:child_process";
17277
18128
  import { createHash as createHash15 } from "node:crypto";
17278
18129
  import fs17 from "node:fs";
17279
18130
  import os5 from "node:os";
17280
- import path21 from "node:path";
18131
+ import path22 from "node:path";
17281
18132
  function rawInvocations(command, dialect = "posix", depth = 0) {
17282
18133
  if (depth > 0)
17283
18134
  return walk2(command, dialect, depth);
@@ -17476,6 +18327,23 @@ function workitInvocations(command, dialect = "posix", depth = 0) {
17476
18327
  }
17477
18328
  return found;
17478
18329
  }
18330
+ function skipWorkitGlobals(args) {
18331
+ let cwd = null;
18332
+ let at = 0;
18333
+ for (;at < args.length; at++) {
18334
+ const match = WORKIT_GLOBAL.exec(args[at]);
18335
+ if (match?.[1] === "=") {
18336
+ if (args[at].startsWith("--cwd="))
18337
+ cwd = args[at].slice(6);
18338
+ } else if (match && args[at + 1] !== undefined) {
18339
+ if (args[at] === "--cwd")
18340
+ cwd = args[at + 1];
18341
+ at++;
18342
+ } else if (args[at] !== "--json")
18343
+ break;
18344
+ }
18345
+ return { rest: args.slice(at), cwd };
18346
+ }
17479
18347
  function rawAction(invocation) {
17480
18348
  const args = invocation.args;
17481
18349
  if (invocation.tool !== "git" && args[0] === "api")
@@ -17559,7 +18427,7 @@ function noteRawCommit(input, command, key) {
17559
18427
  if (action.kind !== "commit")
17560
18428
  continue;
17561
18429
  const file = markerFile(repo, input.session.id, key);
17562
- fs17.mkdirSync(path21.dirname(file), { recursive: true });
18430
+ fs17.mkdirSync(path22.dirname(file), { recursive: true });
17563
18431
  fs17.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
17564
18432
  `);
17565
18433
  }
@@ -17586,12 +18454,12 @@ function settlePendingCommit(input) {
17586
18454
  settle2(input, repo, file);
17587
18455
  } catch {}
17588
18456
  }
17589
- var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLARATIONS, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, GIT_CONFIG_OPTIONS, GIT_CONFIG_ENV, ASSIGNMENT, MAX_DEPTH = 4, MAX_EXPANSIONS = 16, isAbsolute = (value) => path21.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\"), HERE, joinDir = (base, raw) => {
18457
+ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLARATIONS, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, GIT_CONFIG_OPTIONS, GIT_CONFIG_ENV, ASSIGNMENT2, MAX_DEPTH = 4, MAX_EXPANSIONS = 16, isAbsolute = (value) => path22.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\"), HERE, joinDir = (base, raw) => {
17590
18458
  const next = raw.replace(HERE, ".");
17591
18459
  if (base === undefined || /[$`*?]/.test(next) || next === "-")
17592
18460
  return;
17593
- const expanded = next === "~" || next.startsWith("~/") ? path21.join(os5.homedir(), next.slice(1)) : next;
17594
- return base === null || isAbsolute(expanded) ? expanded : path21.join(base, expanded);
18461
+ const expanded = next === "~" || next.startsWith("~/") ? path22.join(os5.homedir(), next.slice(1)) : next;
18462
+ return base === null || isAbsolute(expanded) ? expanded : path22.join(base, expanded);
17595
18463
  }, rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner), cdTarget = (words) => {
17596
18464
  const args = words.slice(1);
17597
18465
  const flag = args.findIndex((word) => /^-(?:literal)?path$/i.test(word));
@@ -17605,7 +18473,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
17605
18473
  const first = words[0];
17606
18474
  if (first === undefined)
17607
18475
  return { words, env };
17608
- const assignment = ASSIGNMENT.exec(first);
18476
+ const assignment = ASSIGNMENT2.exec(first);
17609
18477
  if (assignment) {
17610
18478
  if (GIT_CONFIG_ENV.test(assignment[1]))
17611
18479
  env[assignment[1]] = assignment[2];
@@ -17659,10 +18527,10 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
17659
18527
  while (rest[0]?.startsWith("-"))
17660
18528
  rest = rest.slice(1);
17661
18529
  }
17662
- if (rest.length === 0 || !rest.every((word) => ASSIGNMENT.test(word)))
18530
+ if (rest.length === 0 || !rest.every((word) => ASSIGNMENT2.test(word)))
17663
18531
  return null;
17664
18532
  return rest.map((word) => {
17665
- const [, name, value] = ASSIGNMENT.exec(word);
18533
+ const [, name, value] = ASSIGNMENT2.exec(word);
17666
18534
  return [name, expandWord(value, vars)];
17667
18535
  });
17668
18536
  }, literalOutput = (segment) => {
@@ -17827,7 +18695,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
17827
18695
  return line.slice(index).join(" ").slice(15);
17828
18696
  if (/^-S./.test(word))
17829
18697
  return [word.slice(2), ...line.slice(index + 1)].join(" ");
17830
- if (!word.startsWith("-") && !ASSIGNMENT.test(word))
18698
+ if (!word.startsWith("-") && !ASSIGNMENT2.test(word))
17831
18699
  return null;
17832
18700
  }
17833
18701
  return null;
@@ -17875,7 +18743,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
17875
18743
  ...Object.keys(env).length ? { env } : {},
17876
18744
  ...stdin === undefined ? {} : { stdin }
17877
18745
  };
17878
- }, RECENT, LAUNCHERS, WORKIT_BIN, WORKIT_PACKAGE, PACKAGE_RUNNERS, PUSH_VALUE_OPTIONS, CURRENT_BRANCH, pushDestination = (destination) => {
18746
+ }, RECENT, LAUNCHERS, WORKIT_BIN, WORKIT_PACKAGE, PACKAGE_RUNNERS, WORKIT_GLOBAL, PUSH_VALUE_OPTIONS, CURRENT_BRANCH, pushDestination = (destination) => {
17879
18747
  if (destination === "HEAD" || destination === "@" || CURRENT_BRANCH.test(destination))
17880
18748
  return { name: null };
17881
18749
  if (destination.startsWith("refs/heads/"))
@@ -18079,17 +18947,17 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
18079
18947
  if (!git || git.reftable)
18080
18948
  return null;
18081
18949
  try {
18082
- const head = fs17.readFileSync(path21.join(git.gitDir, "HEAD"), "utf8").trim();
18950
+ const head = fs17.readFileSync(path22.join(git.gitDir, "HEAD"), "utf8").trim();
18083
18951
  if (/^[0-9a-f]{40,64}$/.test(head))
18084
18952
  return head;
18085
18953
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
18086
18954
  if (!ref)
18087
18955
  return null;
18088
- const common = path21.dirname(repo.location.dir);
18956
+ const common = path22.dirname(repo.location.dir);
18089
18957
  try {
18090
- return fs17.readFileSync(path21.join(common, ref), "utf8").trim() || null;
18958
+ return fs17.readFileSync(path22.join(common, ref), "utf8").trim() || null;
18091
18959
  } catch {}
18092
- const packed = fs17.readFileSync(path21.join(common, "packed-refs"), "utf8");
18960
+ const packed = fs17.readFileSync(path22.join(common, "packed-refs"), "utf8");
18093
18961
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
18094
18962
  } catch {
18095
18963
  return null;
@@ -18155,7 +19023,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
18155
19023
  const sub = invocation.args[0];
18156
19024
  if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
18157
19025
  return [invocation];
18158
- const dir = path21.resolve(root, invocation.dir ?? ".");
19026
+ const dir = path22.resolve(root, invocation.dir ?? ".");
18159
19027
  if (!fs17.existsSync(dir))
18160
19028
  return [invocation];
18161
19029
  const config = gitConfig(dir, invocation, cache);
@@ -18177,11 +19045,11 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
18177
19045
  }, normalizedUrl = (url, dir) => {
18178
19046
  const trimmed = url.trim();
18179
19047
  if (/^file:\/\//i.test(trimmed))
18180
- return path21.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
19048
+ return path22.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
18181
19049
  const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
18182
19050
  const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
18183
19051
  if (!scheme && !scp)
18184
- return path21.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
19052
+ return path22.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
18185
19053
  const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
18186
19054
  return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
18187
19055
  }, rewrittenUrl = (url, config) => {
@@ -18199,7 +19067,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
18199
19067
  return best.base + url.slice(best.prefix.length);
18200
19068
  }
18201
19069
  return url;
18202
- }, pushUrl = (config, remote, dir) => {
19070
+ }, pushUrl2 = (config, remote, dir) => {
18203
19071
  const configured = configValue(config, `remote.${remote}.pushurl`) ?? configValue(config, `remote.${remote}.url`);
18204
19072
  return normalizedUrl(rewrittenUrl(configured ?? remote, config), dir);
18205
19073
  }, pushRemoteOf = (config, branch) => {
@@ -18225,7 +19093,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
18225
19093
  const pushRemote = pushRemoteOf(config, repo.branch);
18226
19094
  if (pushRemote === null || pushRemote === remote)
18227
19095
  return true;
18228
- return pushUrl(config, remote, repo.dir) === pushUrl(config, pushRemote, repo.dir);
19096
+ return pushUrl2(config, remote, repo.dir) === pushUrl2(config, pushRemote, repo.dir);
18229
19097
  }, quote = (text) => `\`${text}\``, refusal = (action, branch, dir) => {
18230
19098
  if (action.kind === "merge")
18231
19099
  return `workit: raw ${quote(action.via === "api" ? `${action.forge} api` : action.forge === "gh" ? "gh pr merge" : "glab mr merge")}${action.via === "api" ? " merge" : ""} bypasses the merge grant and the verdict gate. Run ${quote("workit pr merge")} instead (it checks both; ${quote("workit help pr")}).`;
@@ -18387,7 +19255,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
18387
19255
  const out = [];
18388
19256
  const repos = new Map;
18389
19257
  const repoFor = (relative) => {
18390
- const dir = path21.resolve(cwd, relative ?? ".");
19258
+ const dir = path22.resolve(cwd, relative ?? ".");
18391
19259
  if (!repos.has(dir)) {
18392
19260
  const repo = repoAt(dir);
18393
19261
  repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
@@ -18431,7 +19299,7 @@ var NONE2, SHELLS, POWERSHELLS, EVALS, WRAPPERS, WRAPPER_VALUES, KEYWORDS, DECLA
18431
19299
  kind: "deny",
18432
19300
  reason: `workit: the shell hook could not analyze this command (${error.message}), so it does not run. ${SPLIT_IT}`,
18433
19301
  unblock: SPLIT_IT
18434
- }), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) => path21.join(repo.location.dir, "hooks", `commit-${createHash15("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`), callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash15("sha256").update(command).digest("hex")}`, headCommit = (dir) => {
19302
+ }), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) => path22.join(repo.location.dir, "hooks", `commit-${createHash15("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`), callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash15("sha256").update(command).digest("hex")}`, headCommit = (dir) => {
18435
19303
  const run = spawnSync11("git", [
18436
19304
  "log",
18437
19305
  "-1",
@@ -18537,7 +19405,7 @@ var init_raw_git = __esm(() => {
18537
19405
  ]);
18538
19406
  GIT_CONFIG_OPTIONS = new Set(["-c", "--config-env", "--git-dir"]);
18539
19407
  GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
18540
- ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
19408
+ ASSIGNMENT2 = /^([A-Za-z_]\w*)=([\s\S]*)$/;
18541
19409
  HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
18542
19410
  VARIABLE = /\$(?:\{(\w+)\}|(\w+))/g;
18543
19411
  XARGS_VALUES = new Set(["-n", "-L", "-l", "-P", "-d", "-E", "-e", "-s", "-a", "--arg-file", "--delimiter"].concat([
@@ -18569,6 +19437,7 @@ var init_raw_git = __esm(() => {
18569
19437
  WORKIT_BIN = /(?:^|[\\/])workit(?:\.exe)?$|^@brainervirus\/workit-cli$/;
18570
19438
  WORKIT_PACKAGE = /^@brainervirus\/workit-cli(?:@\S*)?$/;
18571
19439
  PACKAGE_RUNNERS = new Set(["npx", "bunx", "pnpx"]);
19440
+ WORKIT_GLOBAL = /^--(?:cwd|session|token|as)(=|$)/;
18572
19441
  PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
18573
19442
  CURRENT_BRANCH = /^\$\(\s*git\s+(?:branch\s+--show-current|rev-parse\s+--abbrev-ref\s+HEAD|symbolic-ref\s+--short\s+HEAD)\s*\)$/;
18574
19443
  VALUE_OPTIONS = {
@@ -18629,8 +19498,8 @@ var init_raw_git = __esm(() => {
18629
19498
 
18630
19499
  // packages/workit-core/src/core/route-intent.ts
18631
19500
  import { spawnSync as spawnSync12 } from "node:child_process";
18632
- import { existsSync as existsSync6 } from "node:fs";
18633
- import path22 from "node:path";
19501
+ import { existsSync as existsSync7 } from "node:fs";
19502
+ import path23 from "node:path";
18634
19503
  var UNKNOWABLE, SWITCH_CREATE, CHECKOUT_CREATE, WORKTREE_CREATE, SUBTREE_CREATE, BRANCH_NO_CREATE, BRANCH_RENAME, longOption = (name, flags) => flags.has(name) || name.length >= 3 && [...flags].some((flag) => flag.startsWith("--") && flag.startsWith(name)), shortsOf = (flags) => [...flags].filter((flag) => /^-[^-]$/u.test(flag)).join("").replaceAll("-", ""), flagValue = (args, flags) => {
18635
19504
  const shorts = shortsOf(flags);
18636
19505
  for (let index = 1;index < args.length; index++) {
@@ -18756,8 +19625,8 @@ var UNKNOWABLE, SWITCH_CREATE, CHECKOUT_CREATE, WORKTREE_CREATE, SUBTREE_CREATE,
18756
19625
  return null;
18757
19626
  if (dir === null)
18758
19627
  return root;
18759
- const resolved = path22.resolve(root, dir);
18760
- return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
19628
+ const resolved = path23.resolve(root, dir);
19629
+ return existsSync7(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
18761
19630
  }, shellBranchPolicyViolation = (root, command, dialect = "posix") => {
18762
19631
  let judged = false;
18763
19632
  const cache = new Map;
@@ -18811,7 +19680,7 @@ var init_policy = __esm(() => {
18811
19680
  // packages/workit-core/src/core/heavy-rules.ts
18812
19681
  import fs18 from "node:fs";
18813
19682
  import os6 from "node:os";
18814
- import path23 from "node:path";
19683
+ import path24 from "node:path";
18815
19684
  function parseJsonc(text) {
18816
19685
  let out = "";
18817
19686
  for (let index = 0;index < text.length; index += 1) {
@@ -18841,21 +19710,21 @@ function parseJsonc(text) {
18841
19710
  return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
18842
19711
  }
18843
19712
  function gitTop(cwd) {
18844
- for (let dir = path23.resolve(cwd);; dir = path23.dirname(dir)) {
18845
- if (fs18.existsSync(path23.join(dir, ".git")))
19713
+ for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
19714
+ if (fs18.existsSync(path24.join(dir, ".git")))
18846
19715
  return dir;
18847
- if (path23.dirname(dir) === dir)
19716
+ if (path24.dirname(dir) === dir)
18848
19717
  return null;
18849
19718
  }
18850
19719
  }
18851
19720
  function claudeSettingsFiles(cwd, env, platform = process.platform) {
18852
19721
  const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
18853
- const user = env.CLAUDE_CONFIG_DIR || path23.join(homeOf(env), ".claude");
19722
+ const user = env.CLAUDE_CONFIG_DIR || path24.join(homeOf(env), ".claude");
18854
19723
  return [
18855
19724
  ...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
18856
- path23.join(user, "settings.json"),
18857
- path23.join(project, ".claude", "settings.json"),
18858
- path23.join(project, ".claude", "settings.local.json")
19725
+ path24.join(user, "settings.json"),
19726
+ path24.join(project, ".claude", "settings.json"),
19727
+ path24.join(project, ".claude", "settings.local.json")
18859
19728
  ];
18860
19729
  }
18861
19730
  function claudeRules(cwd, env = process.env) {
@@ -18900,10 +19769,10 @@ function claudeRules(cwd, env = process.env) {
18900
19769
  function opencodeRules(cwd, env = process.env) {
18901
19770
  const rules = EMPTY2();
18902
19771
  try {
18903
- const config = env.XDG_CONFIG_HOME || path23.join(homeOf(env), ".config");
19772
+ const config = env.XDG_CONFIG_HOME || path24.join(homeOf(env), ".config");
18904
19773
  const files = [
18905
- ...OPENCODE_NAMES.map((name) => path23.join(config, "opencode", name)),
18906
- ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path23.join(dir, name), path23.join(dir, ".opencode", name)])),
19774
+ ...OPENCODE_NAMES.map((name) => path24.join(config, "opencode", name)),
19775
+ ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path24.join(dir, name), path24.join(dir, ".opencode", name)])),
18907
19776
  ...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
18908
19777
  ];
18909
19778
  for (const file of files) {
@@ -18921,9 +19790,9 @@ function opencodeRules(cwd, env = process.env) {
18921
19790
  function codexRules(cwd, env = process.env) {
18922
19791
  const rules = EMPTY2();
18923
19792
  try {
18924
- const home = env.CODEX_HOME || path23.join(homeOf(env), ".codex");
19793
+ const home = env.CODEX_HOME || path24.join(homeOf(env), ".codex");
18925
19794
  const top = gitTop(cwd) ?? cwd;
18926
- for (const dir of [path23.join(home, "rules"), path23.join(top, ".codex", "rules")]) {
19795
+ for (const dir of [path24.join(home, "rules"), path24.join(top, ".codex", "rules")]) {
18927
19796
  let files;
18928
19797
  try {
18929
19798
  files = fs18.readdirSync(dir).filter((name) => name.endsWith(".rules"));
@@ -18933,7 +19802,7 @@ function codexRules(cwd, env = process.env) {
18933
19802
  return "unreadable";
18934
19803
  }
18935
19804
  for (const name of files) {
18936
- const text = readText2(path23.join(dir, name)) ?? "";
19805
+ const text = readText2(path24.join(dir, name)) ?? "";
18937
19806
  for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
18938
19807
  const rule = codexRule(part);
18939
19808
  if (!rule)
@@ -18952,8 +19821,8 @@ function cursorRules(cwd, env = process.env) {
18952
19821
  try {
18953
19822
  const top = gitTop(cwd) ?? cwd;
18954
19823
  for (const file of [
18955
- path23.join(homeOf(env), ".cursor", "cli-config.json"),
18956
- path23.join(top, ".cursor", "cli.json")
19824
+ path24.join(homeOf(env), ".cursor", "cli-config.json"),
19825
+ path24.join(top, ".cursor", "cli.json")
18957
19826
  ]) {
18958
19827
  const text = readText2(file);
18959
19828
  if (text === null)
@@ -19015,9 +19884,9 @@ var EMPTY2 = () => ({ deny: [], allow: [] }), readText2 = (file) => {
19015
19884
  }, isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), projectDirs = (cwd) => {
19016
19885
  const top = gitTop(cwd);
19017
19886
  const dirs = [];
19018
- for (let dir = path23.resolve(cwd);; dir = path23.dirname(dir)) {
19887
+ for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
19019
19888
  dirs.push(dir);
19020
- if (!top || dir === top || path23.dirname(dir) === dir)
19889
+ if (!top || dir === top || path24.dirname(dir) === dir)
19021
19890
  return dirs;
19022
19891
  }
19023
19892
  }, homeOf = (env) => env.HOME || os6.homedir(), CLAUDE_MANAGED, bashPattern = (entry) => {
@@ -19115,18 +19984,18 @@ var init_heavy_rules = __esm(() => {
19115
19984
  // packages/workit-core/src/core/heavy.ts
19116
19985
  import fs19 from "node:fs";
19117
19986
  import os7 from "node:os";
19118
- import path24 from "node:path";
19987
+ import path25 from "node:path";
19119
19988
  function stateDir(env, platform = process.platform) {
19120
19989
  if (env.WORKFLOW_TOOLKIT_STATE)
19121
19990
  return env.WORKFLOW_TOOLKIT_STATE;
19122
19991
  if (env.XDG_STATE_HOME)
19123
- return path24.join(env.XDG_STATE_HOME, "workit");
19992
+ return path25.join(env.XDG_STATE_HOME, "workit");
19124
19993
  const home = env.HOME || os7.homedir();
19125
19994
  if (platform === "darwin")
19126
- return path24.join(home, "Library", "Application Support", "workit");
19995
+ return path25.join(home, "Library", "Application Support", "workit");
19127
19996
  if (platform === "win32")
19128
- return path24.join(env.LOCALAPPDATA ?? path24.join(home, "AppData", "Local"), "workit");
19129
- return path24.join(home, ".local", "state", "workit");
19997
+ return path25.join(env.LOCALAPPDATA ?? path25.join(home, "AppData", "Local"), "workit");
19998
+ return path25.join(home, ".local", "state", "workit");
19130
19999
  }
19131
20000
  function positional2(words, from, values = PM_VALUES) {
19132
20001
  let index = from;
@@ -19266,7 +20135,7 @@ function machinePatterns(env) {
19266
20135
  return isRecord5(value) ? stringList(value.patterns) : [];
19267
20136
  }
19268
20137
  function repoPatterns(cwd) {
19269
- return repoHeavyCommands(gitTop(cwd) ?? path24.resolve(cwd));
20138
+ return repoHeavyCommands(gitTop(cwd) ?? path25.resolve(cwd));
19270
20139
  }
19271
20140
  function heavyLines(lines, options) {
19272
20141
  const found = [];
@@ -19335,12 +20204,12 @@ function heavyPlan(command, options) {
19335
20204
  }
19336
20205
  function workitOnPath2(env = process.env) {
19337
20206
  const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
19338
- for (const dir of (env.PATH ?? "").split(path24.delimiter))
20207
+ for (const dir of (env.PATH ?? "").split(path25.delimiter))
19339
20208
  for (const name of names) {
19340
20209
  if (!dir)
19341
20210
  continue;
19342
20211
  try {
19343
- fs19.accessSync(path24.join(dir, name), fs19.constants.X_OK);
20212
+ fs19.accessSync(path25.join(dir, name), fs19.constants.X_OK);
19344
20213
  return true;
19345
20214
  } catch {}
19346
20215
  }
@@ -19354,7 +20223,7 @@ function leaseRewrite(command) {
19354
20223
  return null;
19355
20224
  return `${prefix}workit check --heavy -- ${rest}`;
19356
20225
  }
19357
- var HEAVY_COMMANDS_FILE = "heavy-commands.json", heavyCommandsFile = (env) => path24.join(stateDir(env), HEAVY_COMMANDS_FILE), HEAVY_SCRIPT, INFO_FLAGS, WATCH_FLAG, WATCH_SCRIPT, sub = (...names) => new Set(names), TOOLS, PM_VALUES, baseName2 = (word) => (word?.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat|ps1)$/u, ""), WORKIT, PYTHONS, NPM_INSTALL, NPM_RUN, NPM_TEST, EXEC, globWord = (word) => new RegExp(`^${word.replace(/[.+?^${}()|[\]\\]/gu, "\\$&").replaceAll("*", ".*")}$`, "u"), matches = (matchers, words) => matchers.length <= words.length && matchers.every((matcher, index) => index === 0 ? matcher.test(words[0]) || matcher.test(baseName2(words[0])) : matcher.test(words[index])), readJson = (file) => {
20226
+ var HEAVY_COMMANDS_FILE = "heavy-commands.json", heavyCommandsFile = (env) => path25.join(stateDir(env), HEAVY_COMMANDS_FILE), HEAVY_SCRIPT, INFO_FLAGS, WATCH_FLAG, WATCH_SCRIPT, sub = (...names) => new Set(names), TOOLS, PM_VALUES, baseName2 = (word) => (word?.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat|ps1)$/u, ""), WORKIT, PYTHONS, NPM_INSTALL, NPM_RUN, NPM_TEST, EXEC, globWord = (word) => new RegExp(`^${word.replace(/[.+?^${}()|[\]\\]/gu, "\\$&").replaceAll("*", ".*")}$`, "u"), matches = (matchers, words) => matchers.length <= words.length && matchers.every((matcher, index) => index === 0 ? matcher.test(words[0]) || matcher.test(baseName2(words[0])) : matcher.test(words[index])), readJson = (file) => {
19358
20227
  try {
19359
20228
  return JSON.parse(fs19.readFileSync(file, "utf8"));
19360
20229
  } catch {
@@ -19500,7 +20369,7 @@ var init_heavy = __esm(() => {
19500
20369
  // packages/workit-core/src/core/heavy-words.ts
19501
20370
  import fs20 from "node:fs";
19502
20371
  import os8 from "node:os";
19503
- import path25 from "node:path";
20372
+ import path26 from "node:path";
19504
20373
  function maybeHeavy(command, cwd, env = process.env) {
19505
20374
  try {
19506
20375
  const words = wordsOf(command);
@@ -19516,13 +20385,13 @@ var HEAVY_WORDS, wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((w
19516
20385
  if (env.WORKFLOW_TOOLKIT_STATE)
19517
20386
  return env.WORKFLOW_TOOLKIT_STATE;
19518
20387
  if (env.XDG_STATE_HOME)
19519
- return path25.join(env.XDG_STATE_HOME, "workit");
20388
+ return path26.join(env.XDG_STATE_HOME, "workit");
19520
20389
  const home = env.HOME || os8.homedir();
19521
20390
  if (process.platform === "darwin")
19522
- return path25.join(home, "Library", "Application Support", "workit");
20391
+ return path26.join(home, "Library", "Application Support", "workit");
19523
20392
  if (process.platform === "win32")
19524
- return path25.join(env.LOCALAPPDATA ?? path25.join(home, "AppData", "Local"), "workit");
19525
- return path25.join(home, ".local", "state", "workit");
20393
+ return path26.join(env.LOCALAPPDATA ?? path26.join(home, "AppData", "Local"), "workit");
20394
+ return path26.join(home, ".local", "state", "workit");
19526
20395
  }, readJson2 = (file) => {
19527
20396
  try {
19528
20397
  return JSON.parse(fs20.readFileSync(file, "utf8"));
@@ -19531,15 +20400,15 @@ var HEAVY_WORDS, wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((w
19531
20400
  }
19532
20401
  }, patternWords = (cwd, env) => {
19533
20402
  const patterns = [];
19534
- const machine = readJson2(path25.join(stateDir2(env), "heavy-commands.json"));
20403
+ const machine = readJson2(path26.join(stateDir2(env), "heavy-commands.json"));
19535
20404
  if (Array.isArray(machine?.patterns))
19536
20405
  patterns.push(...machine.patterns);
19537
- for (let dir = path25.resolve(cwd);; dir = path25.dirname(dir)) {
19538
- if (fs20.existsSync(path25.join(dir, ".git"))) {
20406
+ for (let dir = path26.resolve(cwd);; dir = path26.dirname(dir)) {
20407
+ if (fs20.existsSync(path26.join(dir, ".git"))) {
19539
20408
  patterns.push(...repoHeavyCommands(dir));
19540
20409
  break;
19541
20410
  }
19542
- if (path25.dirname(dir) === dir)
20411
+ if (path26.dirname(dir) === dir)
19543
20412
  break;
19544
20413
  }
19545
20414
  const words = new Set;
@@ -19609,7 +20478,7 @@ import { spawnSync as spawnSync13 } from "node:child_process";
19609
20478
  import { createHash as createHash16 } from "node:crypto";
19610
20479
  import fs21 from "node:fs";
19611
20480
  import os9 from "node:os";
19612
- import path26 from "node:path";
20481
+ import path27 from "node:path";
19613
20482
  var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session, familyOf = (host) => host.replace(/_(?:cli|desktop)$/u, ""), processAlive = (pid) => {
19614
20483
  try {
19615
20484
  process.kill(pid, 0);
@@ -19667,15 +20536,15 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
19667
20536
  pid: () => hostPid(transport)
19668
20537
  };
19669
20538
  }, checkoutOf = (target) => {
19670
- let dir = path26.resolve(target);
19671
- while (!fs21.existsSync(dir) && path26.dirname(dir) !== dir)
19672
- dir = path26.dirname(dir);
20539
+ let dir = path27.resolve(target);
20540
+ while (!fs21.existsSync(dir) && path27.dirname(dir) !== dir)
20541
+ dir = path27.dirname(dir);
19673
20542
  if (fs21.existsSync(dir) && !fs21.statSync(dir).isDirectory())
19674
- dir = path26.dirname(dir);
19675
- const parts = dir.split(path26.sep);
20543
+ dir = path27.dirname(dir);
20544
+ const parts = dir.split(path27.sep);
19676
20545
  const dotGit = parts.lastIndexOf(".git");
19677
20546
  if (dotGit > 0)
19678
- dir = parts.slice(0, dotGit).join(path26.sep) || path26.sep;
20547
+ dir = parts.slice(0, dotGit).join(path27.sep) || path27.sep;
19679
20548
  const location = resolveStore(dir);
19680
20549
  if (location instanceof Error || !location.shared || !location.top)
19681
20550
  return null;
@@ -19684,8 +20553,8 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
19684
20553
  top = fs21.realpathSync.native(top);
19685
20554
  } catch {}
19686
20555
  return top;
19687
- }, markerFile2 = (top) => path26.join(resolveStateDir(), "live-sessions", `${createHash16("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`), checkoutKey = (top, platform = process.platform) => {
19688
- let key = path26.resolve(top);
20556
+ }, markerFile2 = (top) => path27.join(resolveStateDir(), "live-sessions", `${createHash16("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`), checkoutKey = (top, platform = process.platform) => {
20557
+ let key = path27.resolve(top);
19689
20558
  try {
19690
20559
  key = fs21.realpathSync.native(key);
19691
20560
  } catch {}
@@ -19773,7 +20642,7 @@ var LIVE_SESSION_TTL_MS, HEARTBEAT_EVERY_MS = 30000, liveProbe, leadOf = (sessio
19773
20642
  checkout,
19774
20643
  heartbeatAt: now.toISOString()
19775
20644
  };
19776
- fs21.mkdirSync(path26.dirname(file), { recursive: true });
20645
+ fs21.mkdirSync(path27.dirname(file), { recursive: true });
19777
20646
  const temp = `${file}.${process.pid}.tmp`;
19778
20647
  fs21.writeFileSync(temp, JSON.stringify(marker));
19779
20648
  fs21.renameSync(temp, file);
@@ -19794,9 +20663,9 @@ var init_live_session = __esm(() => {
19794
20663
  });
19795
20664
 
19796
20665
  // packages/workit-core/src/hooks/write-gate.ts
19797
- import { realpathSync as realpathSync5 } from "node:fs";
20666
+ import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
19798
20667
  import os10 from "node:os";
19799
- import path27 from "node:path";
20668
+ import path28 from "node:path";
19800
20669
  function writeGate(cwd, paths, owner, deps) {
19801
20670
  const forged = paths?.length ? issuedTarget(cwd, paths) : null;
19802
20671
  if (forged)
@@ -19820,16 +20689,8 @@ function shellGate(cwd, command, owner, dialect = "posix", deps) {
19820
20689
  } catch {}
19821
20690
  const writes = shellWrites(command);
19822
20691
  let targets = [];
19823
- if (owner) {
19824
- try {
19825
- targets = [
19826
- ...segmentTargets(cwd, command, dialect),
19827
- ...gitMutationDirs(cwd, command, dialect).map((dir) => path27.join(dir, DIR_TARGET))
19828
- ];
19829
- } catch {
19830
- targets = [];
19831
- }
19832
- }
20692
+ if (owner)
20693
+ targets = shellMutationTargets(cwd, command, dialect);
19833
20694
  if (owner && targets.length) {
19834
20695
  const live = liveGate(owner, targets, deps);
19835
20696
  if (live.kind !== "none") {
@@ -19862,21 +20723,21 @@ function beforeWriteGate(cwd, paths) {
19862
20723
  }
19863
20724
  var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
19864
20725
  let rest = "";
19865
- for (let dir = path27.resolve(file);; dir = path27.dirname(dir)) {
20726
+ for (let dir = path28.resolve(file);; dir = path28.dirname(dir)) {
19866
20727
  try {
19867
- return path27.join(realpathSync5.native(dir), rest);
20728
+ return path28.join(realpathSync5.native(dir), rest);
19868
20729
  } catch {
19869
- if (path27.dirname(dir) === dir)
19870
- return path27.resolve(file);
19871
- rest = rest ? path27.join(path27.basename(dir), rest) : path27.basename(dir);
20730
+ if (path28.dirname(dir) === dir)
20731
+ return path28.resolve(file);
20732
+ rest = rest ? path28.join(path28.basename(dir), rest) : path28.basename(dir);
19872
20733
  }
19873
20734
  }
19874
20735
  }, exempt = (cwd, root, file, plans) => {
19875
20736
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
19876
- const relative = path27.relative(fold(canonical2(root)), fold(canonical2(path27.resolve(cwd, file))));
19877
- if (relative.startsWith("..") || path27.isAbsolute(relative))
20737
+ const relative = path28.relative(fold(canonical2(root)), fold(canonical2(path28.resolve(cwd, file))));
20738
+ if (relative.startsWith("..") || path28.isAbsolute(relative))
19878
20739
  return true;
19879
- const posix = relative.split(path27.sep).join("/");
20740
+ const posix = relative.split(path28.sep).join("/");
19880
20741
  const segments = posix.split("/");
19881
20742
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
19882
20743
  }, PREFIXES2, operands = (words) => words.filter((item) => !item.startsWith("-")), SCRIPT_SHELLS, wrappedScript = (input) => {
@@ -19888,9 +20749,9 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
19888
20749
  return null;
19889
20750
  if ("script" in tail)
19890
20751
  return tail.script;
19891
- const [shell, flag, script] = tail.argv;
19892
- if (SCRIPT_SHELLS.has((shell.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && /^-[a-z]*c$/.test(flag ?? ""))
19893
- return script ?? null;
20752
+ const script = shellScript2(tail.argv);
20753
+ if (SCRIPT_SHELLS.has((tail.argv[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && script)
20754
+ return script;
19894
20755
  return tail.argv.map(quoteWord2).join(" ");
19895
20756
  }, editsOf = (input) => {
19896
20757
  let words = input;
@@ -19967,6 +20828,89 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
19967
20828
  default:
19968
20829
  return [];
19969
20830
  }
20831
+ }, shellScript2 = (words) => {
20832
+ if (!SCRIPT_SHELLS.has((words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase()))
20833
+ return null;
20834
+ for (let index = 1;index < words.length; index++) {
20835
+ const word = words[index];
20836
+ if (/^-[a-z]*c$/.test(word))
20837
+ return words[index + 1] ?? null;
20838
+ if (/^[-+][oO]$/.test(word))
20839
+ index++;
20840
+ else if (!/^[-+]/.test(word))
20841
+ return null;
20842
+ }
20843
+ return null;
20844
+ }, XARGS_VALUE, runsWriter = (words) => {
20845
+ const filled = [...words.filter((word) => word !== "{}" && word !== ";" && word !== "+"), "x"];
20846
+ const edits = editsOf(filled);
20847
+ return edits === null || edits.length > 0 || opaqueWrites(filled) !== null;
20848
+ }, opaqueWrites = (input) => {
20849
+ let words = input;
20850
+ while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0]))) {
20851
+ if (words[0] === "xargs") {
20852
+ let at = 1;
20853
+ while (words[at]?.startsWith("-"))
20854
+ at += XARGS_VALUE.test(words[at]) ? 2 : 1;
20855
+ return runsWriter(words.slice(at)) ? ["."] : null;
20856
+ }
20857
+ words = words.slice(1);
20858
+ }
20859
+ const [verb, ...args] = words;
20860
+ const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
20861
+ const files = operands(args);
20862
+ const has = (pattern) => args.some((arg) => pattern.test(arg));
20863
+ const listed = (named) => named.length ? named : ["."];
20864
+ if (name === "unlink" || name === "shred")
20865
+ return listed(files);
20866
+ if (/^(?:chmod|chown|chgrp|chattr)$/.test(name))
20867
+ return listed(has(/^--reference/) ? files : files.slice(1));
20868
+ if (name === "rsync") {
20869
+ const dest = files.at(-1);
20870
+ const remote = /^[\w.@-]*:/.test(dest ?? "") && !/^[A-Za-z]:[\\/]/.test(dest ?? "");
20871
+ return dest === undefined ? ["."] : remote ? [] : [dest];
20872
+ }
20873
+ if (name === "find") {
20874
+ const exec = args.findIndex((arg) => /^-(?:exec|execdir|ok|okdir)$/.test(arg));
20875
+ const writes = has(/^-(?:delete|fprint\w*|fls)$/) || exec >= 0 && runsWriter(args.slice(exec + 1));
20876
+ const end = args.findIndex((arg) => /^[-(!]/.test(arg));
20877
+ return writes ? listed(end < 0 ? args : args.slice(0, end)) : null;
20878
+ }
20879
+ if (/^g?awk$/.test(name)) {
20880
+ if (!has(/^-i|^--include/))
20881
+ return null;
20882
+ const named = [];
20883
+ let program = !has(/^-f$|^--file/);
20884
+ for (let index = 0;index < args.length; index++)
20885
+ if (/^(?:-[ifvF]|--(?:include|file|assign|field-separator))$/.test(args[index]))
20886
+ index++;
20887
+ else if (args[index].startsWith("-"))
20888
+ continue;
20889
+ else if (program)
20890
+ program = false;
20891
+ else
20892
+ named.push(args[index]);
20893
+ return listed(named);
20894
+ }
20895
+ if (name === "perl")
20896
+ return has(/^-[a-zA-Z]*[eE]/) ? ["."] : null;
20897
+ if (/^(?:python[\d.]*|node|bun|deno|ruby|php)$/.test(name))
20898
+ return has(/^-[a-zA-Z]*[ce]$|^--eval$|^-p$/) ? ["."] : null;
20899
+ return shellScript2(words) === null ? null : ["."];
20900
+ }, linkSources = (args, at) => {
20901
+ const names = operands([...args]);
20902
+ const into = args.indexOf("-t");
20903
+ const dest = into >= 0 ? args[into + 1] : names.length > 1 ? names.at(-1) : ".";
20904
+ const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
20905
+ const target = dest === undefined ? null : at(dest);
20906
+ const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
20907
+ const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync8(target) || /[\\/]$/.test(dest ?? "") ? target : path28.dirname(target);
20908
+ return sources.flatMap((source) => {
20909
+ if (symbolic && base !== null && !path28.isAbsolute(homeExpanded(source)))
20910
+ return [path28.resolve(base, source)];
20911
+ const resolved = at(source);
20912
+ return resolved ? [resolved] : [];
20913
+ });
19970
20914
  }, shellWrites = (command, depth = 0) => {
19971
20915
  const targets = [];
19972
20916
  let unknown = false;
@@ -19989,7 +20933,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
19989
20933
  if (!unknown && targets.length === 0)
19990
20934
  return { writes: false, targets: null };
19991
20935
  return { writes: true, targets: unknown ? null : targets };
19992
- }, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path27.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
20936
+ }, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path28.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
19993
20937
  const store = new TaskStore(cwd);
19994
20938
  const listed = store.listTaskIndex();
19995
20939
  if (!listed.ok)
@@ -20042,25 +20986,28 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
20042
20986
  return [];
20043
20987
  if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
20044
20988
  return [];
20045
- return [path27.resolve(cwd, invocation.dir ?? ".")];
20989
+ return [path28.resolve(cwd, invocation.dir ?? ".")];
20046
20990
  });
20047
- }, CD2, PUSHD2, POPD2, cdInto = (dir, words) => {
20991
+ }, CD2, PUSHD2, POPD2, UNKNOWN_DIR, cdInto = (dir, words, strict = false) => {
20992
+ const lost = strict ? UNKNOWN_DIR : undefined;
20048
20993
  const named = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
20049
20994
  const target = named === undefined ? undefined : homeExpanded(named);
20050
- if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
20051
- return;
20052
- return target === undefined ? os10.homedir() : path27.resolve(dir, target);
20995
+ if (target === "-" || target && /^~|[$`*?]/.test(target))
20996
+ return lost;
20997
+ if (dir === undefined)
20998
+ return lost;
20999
+ return target === undefined ? os10.homedir() : path28.resolve(dir, target);
20053
21000
  }, quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`, homeExpanded = (file) => {
20054
21001
  const match = /^(?:~|\$HOME|\$\{HOME\})(?=\/|$)/.exec(file);
20055
21002
  return match ? (process.env.HOME || os10.homedir()) + file.slice(match[0].length) : file;
20056
- }, segmentTargets = (cwd, command, dialect, depth = 0) => {
21003
+ }, segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
20057
21004
  const targets = [];
20058
21005
  let dir = cwd;
20059
21006
  let stack = [];
20060
21007
  const groups = [];
20061
21008
  const at = (file) => {
20062
21009
  const home = homeExpanded(file);
20063
- return path27.isAbsolute(home) ? home : dir === undefined ? null : path27.resolve(dir, home);
21010
+ return path28.isAbsolute(home) ? home : dir === undefined ? null : path28.resolve(dir, home);
20064
21011
  };
20065
21012
  for (const segment of segmentsOf(command, dialect)) {
20066
21013
  if (segment.group === "open") {
@@ -20077,21 +21024,35 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
20077
21024
  const words = lead < 0 ? [] : segment.words.slice(lead);
20078
21025
  const name = (words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
20079
21026
  if (CD2.has(name)) {
20080
- dir = cdInto(dir, words);
21027
+ dir = cdInto(dir, words, strict);
20081
21028
  continue;
20082
21029
  }
20083
21030
  if (PUSHD2.has(name)) {
20084
21031
  stack.push(dir);
20085
- dir = cdInto(dir, words);
21032
+ dir = cdInto(dir, words, strict);
20086
21033
  continue;
20087
21034
  }
20088
21035
  if (POPD2.has(name)) {
20089
- dir = stack.length ? stack.pop() : undefined;
21036
+ dir = stack.length ? stack.pop() : strict ? UNKNOWN_DIR : undefined;
20090
21037
  continue;
20091
21038
  }
20092
- const inner = depth < 4 ? wrappedScript(words) : null;
21039
+ const inner = depth < 4 ? wrappedScript(words) ?? (strict ? shellScript2(words) : null) : null;
20093
21040
  if (inner !== null && dir !== undefined)
20094
- targets.push(...segmentTargets(dir, inner, "posix", depth + 1));
21041
+ targets.push(...segmentTargets(dir, inner, "posix", depth + 1, strict));
21042
+ if (strict && dir !== undefined) {
21043
+ const runIn = dir;
21044
+ for (const sub of depth < 4 ? segment.subs ?? [] : [])
21045
+ targets.push(...segmentTargets(runIn, sub, dialect, depth + 1, true));
21046
+ const opaque = segment.unanalyzable ? ["."] : inner === null ? opaqueWrites(words) : null;
21047
+ for (const file of opaque ?? []) {
21048
+ const resolved = at(file);
21049
+ if (resolved)
21050
+ targets.push(file === "." ? path28.join(resolved, DIR_TARGET) : resolved);
21051
+ }
21052
+ const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
21053
+ if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
21054
+ targets.push(...linkSources(words.slice(ln + 1), at));
21055
+ }
20095
21056
  for (const file of segment.redirects)
20096
21057
  if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
20097
21058
  const resolved = at(file);
@@ -20101,17 +21062,33 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
20101
21062
  const edits = editsOf(segment.words);
20102
21063
  if (edits === null) {
20103
21064
  if (dir !== undefined)
20104
- targets.push(path27.join(dir, DIR_TARGET));
21065
+ targets.push(path28.join(dir, DIR_TARGET));
20105
21066
  } else
20106
21067
  for (const file of edits) {
20107
21068
  const resolved = at(file);
20108
21069
  if (resolved)
20109
21070
  targets.push(resolved);
20110
21071
  }
20111
- if (dir !== undefined && workitInvocations(segment.words.map(quoteWord2).join(" "), "posix").some((args) => args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), "")))
20112
- targets.push(path27.join(dir, DIR_TARGET));
21072
+ if (dir !== undefined)
21073
+ for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
21074
+ const { at: runsIn, args } = workitCwd(dir, invocation);
21075
+ if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
21076
+ targets.push(path28.join(runsIn, DIR_TARGET));
21077
+ }
20113
21078
  }
20114
21079
  return targets;
21080
+ }, workitCwd = (dir, args) => {
21081
+ const { rest, cwd } = skipWorkitGlobals(args);
21082
+ return { at: cwd === null ? dir : path28.resolve(dir, homeExpanded(cwd)), args: rest };
21083
+ }, shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
21084
+ try {
21085
+ return [
21086
+ ...segmentTargets(cwd, command, dialect, 0, strict),
21087
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path28.join(dir, DIR_TARGET))
21088
+ ];
21089
+ } catch {
21090
+ return [];
21091
+ }
20115
21092
  }, overridden = (command, dialect) => {
20116
21093
  const segments = segmentsOf(command, dialect);
20117
21094
  if (segments.length !== 1 || segments[0].group || segments[0].subs?.length)
@@ -20132,7 +21109,7 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
20132
21109
  }, guarded = (targets) => {
20133
21110
  const seen = new Set;
20134
21111
  return targets.filter((target) => {
20135
- const dir = path27.dirname(target);
21112
+ const dir = path28.dirname(target);
20136
21113
  if (seen.has(dir))
20137
21114
  return false;
20138
21115
  seen.add(dir);
@@ -20150,13 +21127,13 @@ var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
20150
21127
  }, claimAll = (owner, targets, deps) => {
20151
21128
  for (const target of guarded(targets))
20152
21129
  claimLiveSession(target, owner, deps);
20153
- }, liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path27.resolve(cwd, file)) : [path27.join(cwd, DIR_TARGET)], issuedTarget = (cwd, targets) => {
21130
+ }, liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path28.resolve(cwd, file)) : [path28.join(cwd, DIR_TARGET)], issuedTarget = (cwd, targets) => {
20154
21131
  const state = resolveStateDir();
20155
21132
  const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
20156
21133
  return found ?? null;
20157
21134
  }, underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
20158
21135
  const win = platform === "win32";
20159
- const p = win ? path27.win32 : path27.posix;
21136
+ const p = win ? path28.win32 : path28.posix;
20160
21137
  const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
20161
21138
  const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
20162
21139
  const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
@@ -20199,8 +21176,9 @@ var init_write_gate = __esm(() => {
20199
21176
  init_shell_words();
20200
21177
  NONE3 = { kind: "none" };
20201
21178
  CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
20202
- PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
21179
+ PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs", "busybox"]);
20203
21180
  SCRIPT_SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
21181
+ XARGS_VALUE = /^-[IinLPdsEa]$/;
20204
21182
  cache2 = new Map;
20205
21183
  MUTATING_GIT = new Set([
20206
21184
  "add",
@@ -20226,6 +21204,7 @@ var init_write_gate = __esm(() => {
20226
21204
  CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
20227
21205
  PUSHD2 = new Set(["pushd", "push-location"]);
20228
21206
  POPD2 = new Set(["popd", "pop-location"]);
21207
+ UNKNOWN_DIR = path28.resolve(path28.parse(process.cwd()).root, ".workit-unknown-dir");
20229
21208
  HOST_LABELS = {
20230
21209
  claude_code: "Claude Code",
20231
21210
  opencode: "OpenCode",
@@ -20236,6 +21215,26 @@ var init_write_gate = __esm(() => {
20236
21215
  };
20237
21216
  });
20238
21217
 
21218
+ // packages/workit-core/src/core/policy/current-judgment.ts
21219
+ function currentJudgment(cwd) {
21220
+ try {
21221
+ const store = new TaskStore(cwd);
21222
+ const listed = store.listTaskIndex();
21223
+ const key = store.currentKey();
21224
+ if (!listed.ok || !key.ok)
21225
+ return null;
21226
+ const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
21227
+ const task = entry ? store.readTask(entry.id) : null;
21228
+ return task?.ok === true ? latestJudgment(task.data) : null;
21229
+ } catch {
21230
+ return null;
21231
+ }
21232
+ }
21233
+ var init_current_judgment = __esm(() => {
21234
+ init_task_store();
21235
+ init_derive();
21236
+ });
21237
+
20239
21238
  // packages/workit-core/src/hooks/stop.ts
20240
21239
  import { spawnSync as spawnSync14 } from "node:child_process";
20241
21240
  function stopObligation(input, descriptor) {
@@ -20274,7 +21273,7 @@ function stopObligation(input, descriptor) {
20274
21273
  }
20275
21274
  if (!delivered || endpoint !== "green" && endpoint !== "merged")
20276
21275
  return null;
20277
- const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
21276
+ const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
20278
21277
  return left ? { ...left, head } : null;
20279
21278
  } catch {
20280
21279
  return null;
@@ -20377,28 +21376,31 @@ var NONE4, QUESTION, OFFER, YES_NO, LIST_ITEM, asksUser = (message) => {
20377
21376
  kind: "checks",
20378
21377
  reason: `PR #${pr}'s checks were last seen ${state} at ${short(head)}, and this workspace's endpoint is \`${endpoint}\`: run \`workit ci wait --pr ${pr}\`${state === "failing" ? " after fixing the failures `workit pr status` lists (then push)" : ""}.`
20379
21378
  };
20380
- }, taskJudgment = (cwd) => {
20381
- try {
20382
- const store = new TaskStore(cwd);
20383
- const listed = store.listTaskIndex();
20384
- const key = store.currentKey();
20385
- if (!listed.ok || !key.ok)
20386
- return null;
20387
- const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
20388
- const task = entry ? store.readTask(entry.id) : null;
20389
- return task?.ok === true ? latestJudgment(task.data) : null;
20390
- } catch {
20391
- return null;
20392
- }
20393
- }, unverified = (cwd, branch, all, descriptor, verification) => {
21379
+ }, prBase = (rows) => {
21380
+ const seen = rows.findLast((row) => (row.type === "pr.status" || row.type === "pr.created") && row.observer === "workit_cli" && typeof row.base === "string");
21381
+ return typeof seen?.base === "string" ? seen.base : null;
21382
+ }, unverified = (cwd, branch, all, descriptor, verification, head) => {
20394
21383
  const check = checkVerdicts(cwd, branch, all);
20395
21384
  if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
20396
21385
  return null;
20397
- const judgment = taskJudgment(cwd);
21386
+ const judgment = currentJudgment(cwd);
20398
21387
  const high = judgment?.riskTier === "high";
20399
- if (judgment && !judgment.behaviorChange && !high)
20400
- return null;
20401
- const independent = verification === "independent" || high;
21388
+ let independent;
21389
+ if (verification === "proportional") {
21390
+ const lane = branchLane(cwd, {
21391
+ base: prBase(all.filter((row) => row.branch === branch)),
21392
+ head,
21393
+ judgment
21394
+ });
21395
+ const need = laneNeed(lane.lane);
21396
+ if (need === "none")
21397
+ return null;
21398
+ independent = need === "non-author";
21399
+ } else {
21400
+ if (judgment && !judgment.behaviorChange && !high)
21401
+ return null;
21402
+ independent = verification === "independent" || high;
21403
+ }
20402
21404
  if (!independent && check.review === "self-reviewed")
20403
21405
  return null;
20404
21406
  const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
@@ -20411,8 +21413,8 @@ var NONE4, QUESTION, OFFER, YES_NO, LIST_ITEM, asksUser = (message) => {
20411
21413
  };
20412
21414
  var init_stop = __esm(() => {
20413
21415
  init_autonomy();
20414
- init_derive();
20415
- init_task_store();
21416
+ init_current_judgment();
21417
+ init_lane();
20416
21418
  init_rev();
20417
21419
  init_ledger();
20418
21420
  init_raw_git();
@@ -20653,10 +21655,10 @@ var init_skill_nudge = __esm(() => {
20653
21655
  });
20654
21656
 
20655
21657
  // packages/workit-core/src/hooks/hosts/fields.ts
20656
- import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
20657
- import path28 from "node:path";
21658
+ import { existsSync as existsSync9, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
21659
+ import path29 from "node:path";
20658
21660
  var isRecord6 = (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) => {
20659
- if (!nonEmpty2(value) || !path28.isAbsolute(value) || !existsSync7(value))
21661
+ if (!nonEmpty2(value) || !path29.isAbsolute(value) || !existsSync9(value))
20660
21662
  return null;
20661
21663
  try {
20662
21664
  return statSync2(value).isDirectory() ? realpathSync6(value) : null;
@@ -20722,9 +21724,9 @@ var init_fields = __esm(() => {
20722
21724
 
20723
21725
  // packages/workit-core/src/hooks/standing.ts
20724
21726
  import fs22 from "node:fs";
20725
- import path29 from "node:path";
21727
+ import path30 from "node:path";
20726
21728
  var readPlans = (store) => {
20727
- const dir = path29.join(store, "fanouts");
21729
+ const dir = path30.join(store, "fanouts");
20728
21730
  let names;
20729
21731
  try {
20730
21732
  names = fs22.readdirSync(dir).filter((name) => name.endsWith(".json"));
@@ -20734,7 +21736,7 @@ var readPlans = (store) => {
20734
21736
  const plans = [];
20735
21737
  for (const name of names) {
20736
21738
  try {
20737
- const raw = JSON.parse(fs22.readFileSync(path29.join(dir, name), "utf8"));
21739
+ const raw = JSON.parse(fs22.readFileSync(path30.join(dir, name), "utf8"));
20738
21740
  if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
20739
21741
  continue;
20740
21742
  const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
@@ -20857,6 +21859,252 @@ var init_cwd_normalize = __esm(() => {
20857
21859
  FILTERS = new Set(["tail", "head", "grep"]);
20858
21860
  });
20859
21861
 
21862
+ // packages/workit-core/src/hooks/worktree-confine.ts
21863
+ import { createHash as createHash17 } from "node:crypto";
21864
+ import fs23 from "node:fs";
21865
+ import path31 from "node:path";
21866
+ function confineImplementer(input, targets) {
21867
+ try {
21868
+ if (!input.session.agentId || hostIsolated(input.cwd, input.session.agentId))
21869
+ return NONE5;
21870
+ if (targets.some((target) => within(path31.resolve(target), UNKNOWN_DIR)))
21871
+ return unknownDirDeny(input, boundWorktree(input));
21872
+ const bindings = canonical3(bindingDir());
21873
+ const judged = targets.map((named) => ({ named, real: canonical3(named) })).filter(({ real }) => within(real, bindings) || checkoutOf2(real) !== null);
21874
+ if (judged.length === 0)
21875
+ return NONE5;
21876
+ let worktree = boundWorktree(input);
21877
+ if (!worktree) {
21878
+ for (const { real } of judged) {
21879
+ worktree = within(real, bindings) ? null : workitWorktreeOf(real);
21880
+ if (worktree)
21881
+ break;
21882
+ }
21883
+ if (!worktree)
21884
+ return unboundDeny(input, judged[0].named);
21885
+ bind(input, worktree);
21886
+ }
21887
+ const root = canonical3(worktree);
21888
+ const outside = judged.find(({ real }) => !within(real, root) || within(real, bindings));
21889
+ return outside ? outsideDeny(input, worktree, outside.named) : NONE5;
21890
+ } catch {
21891
+ return NONE5;
21892
+ }
21893
+ }
21894
+ function confineImplementerShell(input, command, dialect = "posix") {
21895
+ try {
21896
+ if (!input.session.agentId || hostIsolated(input.cwd, input.session.agentId))
21897
+ return NONE5;
21898
+ if (rawInvocations(command, dialect).some((invocation) => invocation.tool === "git" && leadOwnedGit(invocation.args)) || workitInvocations(command, dialect).some(leadOwnedWorkit))
21899
+ return leadOwnedDeny(input, command);
21900
+ } catch {}
21901
+ return confineImplementer(input, shellMutationTargets(input.cwd, command, dialect, true));
21902
+ }
21903
+ var FANOUT_TOKEN_FILE = "workit-fanout-token", NONE5, CASE_INSENSITIVE2, canonical3 = (file) => {
21904
+ let rest = "";
21905
+ for (let dir = path31.resolve(file);; dir = path31.dirname(dir)) {
21906
+ try {
21907
+ return path31.join(fs23.realpathSync.native(dir), rest);
21908
+ } catch {
21909
+ if (path31.dirname(dir) === dir)
21910
+ return path31.resolve(file);
21911
+ rest = rest ? path31.join(path31.basename(dir), rest) : path31.basename(dir);
21912
+ }
21913
+ }
21914
+ }, within = (child, parent) => {
21915
+ const fold = (value) => CASE_INSENSITIVE2 ? value.toLowerCase() : value;
21916
+ const relative = path31.relative(fold(parent), fold(child));
21917
+ return relative === "" || !relative.startsWith("..") && !path31.isAbsolute(relative);
21918
+ }, checkoutOf2 = (file) => {
21919
+ for (let dir = canonical3(file);; dir = path31.dirname(dir)) {
21920
+ const dotGit = path31.join(dir, ".git");
21921
+ if (fs23.existsSync(dotGit)) {
21922
+ if (!fs23.statSync(dotGit).isFile())
21923
+ return { root: dir, workit: false };
21924
+ const admin = /^gitdir:\s*(.+)$/mu.exec(fs23.readFileSync(dotGit, "utf8"))?.[1]?.trim();
21925
+ return {
21926
+ root: dir,
21927
+ workit: Boolean(admin) && fs23.existsSync(path31.join(path31.resolve(dir, admin), FANOUT_TOKEN_FILE))
21928
+ };
21929
+ }
21930
+ if (path31.dirname(dir) === dir)
21931
+ return null;
21932
+ }
21933
+ }, workitWorktreeOf = (file) => {
21934
+ try {
21935
+ const checkout = checkoutOf2(file);
21936
+ return checkout?.workit ? checkout.root : null;
21937
+ } catch {
21938
+ return null;
21939
+ }
21940
+ }, bindingDir = () => path31.join(resolveStateDir(), "worktree-bind"), bindingFile = (session, agentId) => path31.join(bindingDir(), `${createHash17("sha256").update(`${session}\x00${agentId}`).digest("hex").slice(0, 32)}.json`), readBinding = (file) => {
21941
+ try {
21942
+ const value = JSON.parse(fs23.readFileSync(file, "utf8"));
21943
+ return typeof value.worktree === "string" ? value : null;
21944
+ } catch {
21945
+ return null;
21946
+ }
21947
+ }, boundWorktree = (input) => {
21948
+ const agentId = input.session.agentId;
21949
+ if (!agentId)
21950
+ return null;
21951
+ const binding = readBinding(bindingFile(input.session.id, agentId));
21952
+ return binding && workitWorktreeOf(binding.worktree) === binding.worktree ? binding.worktree : null;
21953
+ }, bind = (input, worktree) => {
21954
+ const agentId = input.session.agentId;
21955
+ const file = bindingFile(input.session.id, agentId);
21956
+ const binding = {
21957
+ worktree,
21958
+ session: input.session.id,
21959
+ agentId,
21960
+ at: new Date().toISOString()
21961
+ };
21962
+ fs23.mkdirSync(path31.dirname(file), { recursive: true });
21963
+ fs23.writeFileSync(file, JSON.stringify(binding));
21964
+ try {
21965
+ if (inWorkitWorkspace(input.cwd))
21966
+ appendHookObserved(input.cwd, {
21967
+ type: "worktree.bound",
21968
+ actor: { host: input.host, session: input.session.id, agentId },
21969
+ agentType: input.session.agentType,
21970
+ worktree
21971
+ });
21972
+ } catch {}
21973
+ }, hostIsolated = (cwd, agentId) => {
21974
+ try {
21975
+ if (agentId === null)
21976
+ return false;
21977
+ const checkout = checkoutOf2(cwd);
21978
+ if (!checkout)
21979
+ return false;
21980
+ const dotGit = path31.join(checkout.root, ".git");
21981
+ if (!fs23.statSync(dotGit).isFile())
21982
+ return false;
21983
+ const admin = /^gitdir:\s*(.+)$/mu.exec(fs23.readFileSync(dotGit, "utf8"))?.[1]?.trim();
21984
+ if (!admin)
21985
+ return false;
21986
+ const adminDir = path31.resolve(checkout.root, admin);
21987
+ const back = fs23.readFileSync(path31.join(adminDir, "gitdir"), "utf8").trim();
21988
+ if (path31.relative(canonical3(path31.resolve(adminDir, back)), canonical3(dotGit)) !== "")
21989
+ return false;
21990
+ const common = path31.join(adminDir, "commondir");
21991
+ const gitDir = fs23.existsSync(common) ? path31.resolve(adminDir, fs23.readFileSync(common, "utf8").trim()) : adminDir;
21992
+ const expected = path31.join(path31.dirname(canonical3(gitDir)), ".claude", "worktrees", `agent-${agentId}`);
21993
+ return path31.relative(canonical3(expected), checkout.root) === "";
21994
+ } catch {
21995
+ return false;
21996
+ }
21997
+ }, shown = (target) => path31.basename(target) === ".workit-live-target" ? path31.dirname(target) : target, unboundDeny = (input, target) => {
21998
+ const unblock = "Work only in the worktree your brief names, which the lead made with `workit fanout worktree create <slice>`: edit files under it and run one verb per call against it (`workit --cwd <worktree> git commit -m … -- <paths>`, `workit --cwd <worktree> check <name>`). If your brief names no worktree, stop and report that to the lead.";
21999
+ return {
22000
+ kind: "deny",
22001
+ reason: `workit worktree confinement: implementer ${input.session.agentId} is not working in a Workit worktree yet, and ${shown(target)} is outside every worktree \`workit fanout worktree create\` made, so it would change the lead's checkout. ${unblock}`,
22002
+ unblock
22003
+ };
22004
+ }, outsideDeny = (input, worktree, target) => {
22005
+ const unblock = `Edit only under ${worktree} and run git there, one verb per call: \`workit --cwd ${worktree} git commit -m … -- <paths>\`, \`workit --cwd ${worktree} check <name>\` (or \`git -C ${worktree} …\`); a command whose writes Workit cannot see is judged by the directory it runs in. Anything outside it is a follow-up for your report.`;
22006
+ return {
22007
+ kind: "deny",
22008
+ reason: `workit worktree confinement: implementer ${input.session.agentId} is bound to the worktree ${worktree}, and ${shown(target)} is outside it. ${unblock}`,
22009
+ unblock
22010
+ };
22011
+ }, unknownDirDeny = (input, worktree) => {
22012
+ const wt = worktree ?? "<worktree>";
22013
+ const unblock = `Workit cannot resolve \`cd -\`, \`cd "$X"\` or \`popd\`: write by absolute path, \`git -C ${wt}\` or \`workit --cwd ${wt}\`.`;
22014
+ return {
22015
+ kind: "deny",
22016
+ reason: `workit worktree confinement: implementer ${input.session.agentId} writes after an unknown directory change. ${unblock}`,
22017
+ unblock
22018
+ };
22019
+ }, MUTATING_REMOTE, CONFIG_READ, CONFIG_WRITE, CONFIG_VALUE, leadOwnedGit = (args) => {
22020
+ const [sub = "", ...rest] = args;
22021
+ if (rest.some((arg) => arg === "--help" || arg === "-h"))
22022
+ return false;
22023
+ const names = rest.filter((arg) => !arg.startsWith("-"));
22024
+ const flag = (pattern) => rest.some((arg) => pattern.test(arg));
22025
+ switch (sub) {
22026
+ case "worktree":
22027
+ return names[0] !== undefined && names[0] !== "list";
22028
+ case "update-ref":
22029
+ return true;
22030
+ case "symbolic-ref":
22031
+ return names.length > 1 || flag(/^(?:-d|--delete)$/);
22032
+ case "branch":
22033
+ return flag(/^-[a-zA-Z]*[dDfmMcC]|^--(?:delete|force|move|copy)$/);
22034
+ case "tag":
22035
+ return flag(/^-[a-zA-Z]*[df]|^--(?:delete|force)$/);
22036
+ case "push": {
22037
+ if (flag(/^(?:-d|--delete|--mirror|--prune)$/))
22038
+ return true;
22039
+ const refspecs = names.slice(1);
22040
+ return refspecs.some((name) => name.includes(":") || name.startsWith("+")) || refspecs.length > 0 && flag(/^-[a-zA-Z]*f|^--force/);
22041
+ }
22042
+ case "replace":
22043
+ case "filter-branch":
22044
+ case "gc":
22045
+ case "prune":
22046
+ case "repack":
22047
+ case "sparse-checkout":
22048
+ return true;
22049
+ case "notes":
22050
+ case "reflog":
22051
+ case "submodule":
22052
+ return !/^(?:list|show|status|exists)$/.test(names[0] ?? "list");
22053
+ case "stash":
22054
+ return names[0] === "drop" || names[0] === "clear";
22055
+ case "remote":
22056
+ return MUTATING_REMOTE.has(names[0] ?? "");
22057
+ case "config": {
22058
+ if (rest.some((arg) => CONFIG_READ.test(arg)))
22059
+ return false;
22060
+ if (rest.some((arg) => CONFIG_WRITE.test(arg)))
22061
+ return true;
22062
+ let operands = 0;
22063
+ for (let index = 0;index < rest.length; index++)
22064
+ if (CONFIG_VALUE.test(rest[index]))
22065
+ index++;
22066
+ else if (!rest[index].startsWith("-"))
22067
+ operands++;
22068
+ return operands > 1;
22069
+ }
22070
+ default:
22071
+ return false;
22072
+ }
22073
+ }, leadOwnedWorkit = (args) => !args.includes("--help") && args.some((_, start) => {
22074
+ if (start > 0 && !/(?:^|[\\/])workit(?:\.exe|\.cmd)?$/.test(args[start - 1]))
22075
+ return false;
22076
+ const [verb, sub] = skipWorkitGlobals(args.slice(start)).rest;
22077
+ return verb === "fanout" && /^(?:plan|worktree)$/.test(sub ?? "");
22078
+ }), leadOwnedDeny = (input, command) => {
22079
+ const unblock = "Worktrees, branches other than your own, refs, tags, remotes, git config, the shared stash and the fan-out plan belong to the lead: report what you need changed as a follow-up instead.";
22080
+ return {
22081
+ kind: "deny",
22082
+ reason: `workit worktree confinement: implementer ${input.session.agentId} may not run \`${command.slice(0, 160)}\`. ${unblock}`,
22083
+ unblock
22084
+ };
22085
+ };
22086
+ var init_worktree_confine = __esm(() => {
22087
+ init_logger();
22088
+ init_ledger();
22089
+ init_raw_git();
22090
+ init_write_gate();
22091
+ NONE5 = { kind: "none" };
22092
+ CASE_INSENSITIVE2 = process.platform === "darwin" || process.platform === "win32";
22093
+ MUTATING_REMOTE = new Set([
22094
+ "add",
22095
+ "remove",
22096
+ "rm",
22097
+ "rename",
22098
+ "set-url",
22099
+ "set-head",
22100
+ "set-branches",
22101
+ "prune"
22102
+ ]);
22103
+ CONFIG_READ = /^(?:--get(?:-all|-regexp|-urlmatch)?|--list|-l|get|list)$/;
22104
+ CONFIG_WRITE = /^(?:--(?:unset(?:-all)?|add|replace-all|rename-section|remove-section|edit)|-e|set|unset|rename-section|remove-section|edit)$/;
22105
+ CONFIG_VALUE = /^(?:--file|-f|--blob|--type|--default|--comment)$/;
22106
+ });
22107
+
20860
22108
  // packages/workit-core/src/hooks/handle.ts
20861
22109
  function handleHook(input, deps) {
20862
22110
  const decision = decide(input, deps);
@@ -20874,7 +22122,7 @@ function decide(input, deps) {
20874
22122
  switch (event.kind) {
20875
22123
  case "session.start": {
20876
22124
  if (!usable2(descriptor.context.sessionStart))
20877
- return NONE5;
22125
+ return NONE6;
20878
22126
  const key = input.session.id ? `${input.host}\x00${input.session.id}` : null;
20879
22127
  const offer = event.source === "startup" && (key === null || !offered.has(key));
20880
22128
  if (offer && key !== null)
@@ -20889,26 +22137,31 @@ function decide(input, deps) {
20889
22137
  }
20890
22138
  case "context.turn": {
20891
22139
  if (!usable2(descriptor.context.perTurn))
20892
- return NONE5;
22140
+ return NONE6;
20893
22141
  const current = turnContextText(input, descriptor);
20894
22142
  const text = resendsOnChange(descriptor) ? changedTurnContext(input, current) : current;
20895
- return withContextLine(text ? { kind: "context", text } : NONE5, promptNudge(input, event.prompt));
22143
+ return withContextLine(text ? { kind: "context", text } : NONE6, promptNudge(input, event.prompt));
20896
22144
  }
20897
22145
  case "shell.pre": {
20898
22146
  const skillRead = skillReadIn(event.command, event.dialect);
20899
22147
  if (skillRead)
20900
22148
  recordSkillLoad(input, skillRead, "read");
20901
22149
  const canDeny = usable2(descriptor.shellPolicy.deny);
20902
- const policy = canDeny ? shellPolicy(input.cwd, event.command, event.dialect) : NONE5;
22150
+ const policy = canDeny ? shellPolicy(input.cwd, event.command, event.dialect) : NONE6;
20903
22151
  if (policy.kind !== "none")
20904
22152
  return policy;
20905
22153
  const postTool = usable2(descriptor.events["shell.post"].support);
20906
22154
  if (!postTool)
20907
22155
  settlePendingCommit(input);
20908
- const raw = canDeny ? rawGitPre(input, event.command, { nudge: false }) : NONE5;
22156
+ const raw = canDeny ? rawGitPre(input, event.command, { nudge: false }) : NONE6;
20909
22157
  if (raw.kind === "deny")
20910
22158
  return raw;
20911
- const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE5;
22159
+ if (canDeny && implementerAgent(input, descriptor)) {
22160
+ const confined = confineImplementerShell(input, event.command, event.dialect);
22161
+ if (confined.kind === "deny")
22162
+ return confined;
22163
+ }
22164
+ const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE6;
20912
22165
  if (gate.kind === "deny")
20913
22166
  return gate;
20914
22167
  const plan = heavyGate(input.cwd, event.command, {
@@ -20931,10 +22184,10 @@ function decide(input, deps) {
20931
22184
  command: rewrittenTo,
20932
22185
  input: event.toolInput ? { ...event.toolInput, command: rewrittenTo } : null,
20933
22186
  text
20934
- } : NONE5;
22187
+ } : NONE6;
20935
22188
  if (descriptor.transport !== "hook-process")
20936
22189
  return rewritten(null);
20937
- const nudged = withContextLine(withContextLine(canDeny ? rawGitPre(input, event.command) : NONE5, aside), shipNudge(input, event.command));
22190
+ const nudged = withContextLine(withContextLine(canDeny ? rawGitPre(input, event.command) : NONE6, aside), shipNudge(input, event.command));
20938
22191
  if (!lease)
20939
22192
  return cwdCommand && nudged.kind !== "deny" ? rewritten(nudged.kind === "context" ? nudged.text : null) : nudged;
20940
22193
  return rewritten([rewriteNote(lease), nudged.kind === "context" ? nudged.text : null].filter(Boolean).join(`
@@ -20943,9 +22196,17 @@ function decide(input, deps) {
20943
22196
  case "shell.post":
20944
22197
  if (usable2(descriptor.events["shell.post"].support))
20945
22198
  rawGitPost(input, event.command, callKey(event.toolUseId, event.command));
20946
- return NONE5;
20947
- case "write.pre":
20948
- return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths, liveOwner(input, descriptor.transport)) : NONE5;
22199
+ return NONE6;
22200
+ case "write.pre": {
22201
+ if (!usable2(descriptor.events["write.pre"].support))
22202
+ return NONE6;
22203
+ if (implementerAgent(input, descriptor)) {
22204
+ const confined = confineImplementer(input, liveTargets(input.cwd, event.paths));
22205
+ if (confined.kind === "deny")
22206
+ return confined;
22207
+ }
22208
+ return writeGate(input.cwd, event.paths, liveOwner(input, descriptor.transport));
22209
+ }
20949
22210
  case "subagent.start":
20950
22211
  if (workitRole(descriptor, event.agentType) === "judge")
20951
22212
  trackJudge(input, event.agentId, true);
@@ -20962,25 +22223,25 @@ function decide(input, deps) {
20962
22223
  case "tool.pre":
20963
22224
  if (event.skill)
20964
22225
  recordSkillLoad(input, event.skill, "tool");
20965
- return NONE5;
22226
+ return NONE6;
20966
22227
  case "stop":
20967
- return usable2(descriptor.events.stop.support) ? stopDecision(input, descriptor) : NONE5;
22228
+ return usable2(descriptor.events.stop.support) ? stopDecision(input, descriptor) : NONE6;
20968
22229
  case "subagent.stop":
20969
22230
  trackJudge(input, event.agentId, false);
20970
- return NONE5;
22231
+ return NONE6;
20971
22232
  case "prompt.submit":
20972
- return NONE5;
22233
+ return NONE6;
20973
22234
  }
20974
22235
  }
20975
- var usable2 = (value) => value === "native" || value === "partial", NONE5, resendsOnChange = (descriptor) => descriptor.context.turnResend === "on-change" && usable2(descriptor.context.perTurn), offered, ROLES, workitRole = (descriptor, agentType) => {
22236
+ var usable2 = (value) => value === "native" || value === "partial", NONE6, resendsOnChange = (descriptor) => descriptor.context.turnResend === "on-change" && usable2(descriptor.context.perTurn), offered, ROLES, workitRole = (descriptor, agentType) => {
20976
22237
  const prefix = descriptor.subagents.agentPrefix;
20977
22238
  const name = agentType.startsWith(prefix) ? agentType.slice(prefix.length) : "";
20978
22239
  return Object.hasOwn(ROLES, name) ? ROLES[name] : null;
20979
- }, subagentSession = (session, agentId) => [session, agentId].filter((part) => Boolean(part)).join(":").replace(/[^A-Za-z0-9_.:@/+-]/g, "-").slice(0, 128), CLAUDE_WORKTREE, isolatedGit = (cwd) => `Run git as plain, separate commands inside your worktree (\`git switch -c <branch> <base>\`, ${workspaceTrailers(cwd).enabled ? '`git commit --trailer "Workit-Session=<your session>"`, `git push`; `workit doctor --fix` can install a prepare-commit-msg hook that adds the trailer' : "`git commit` (this workspace sets trailers: false: add no Workit-Session trailer), `git push`"}; add Co-Authored-By with \`git commit --trailer\` in the same commit, never by amending), never \`workit git …\`: Claude Code's worktree isolation refuses a command that wraps git and cannot be turned off (https://code.claude.com/docs/en/worktrees#how-claude-code-enforces-isolation). Never dodge that check by renaming or re-wrapping git. Use workit for the non-git verbs (\`workit check\`, \`workit ledger\`, \`workit pr\`).`, subagentStartText = (input, descriptor, event) => {
22240
+ }, implementerAgent = (input, descriptor) => Boolean(input.session.agentId) && input.session.agentType !== null && workitRole(descriptor, input.session.agentType) === "implementer", subagentSession = (session, agentId) => [session, agentId].filter((part) => Boolean(part)).join(":").replace(/[^A-Za-z0-9_.:@/+-]/g, "-").slice(0, 128), CLAUDE_WORKTREE, isolatedGit = (cwd) => `Run git as plain, separate commands inside your worktree (\`git switch -c <branch> <base>\`, ${workspaceTrailers(cwd).enabled ? '`git commit --trailer "Workit-Session=<your session>"`, `git push`; `workit doctor --fix` can install a prepare-commit-msg hook that adds the trailer' : "`git commit` (this workspace sets trailers: false: add no Workit-Session trailer), `git push`"}; add Co-Authored-By with \`git commit --trailer\` in the same commit, never by amending), never \`workit git …\`: Claude Code's worktree isolation refuses a command that wraps git and cannot be turned off (https://code.claude.com/docs/en/worktrees#how-claude-code-enforces-isolation). Never dodge that check by renaming or re-wrapping git. Use workit for the non-git verbs (\`workit check\`, \`workit ledger\`, \`workit pr\`).`, subagentStartText = (input, descriptor, event) => {
20980
22241
  const session = input.session.id ?? null;
20981
22242
  const role = workitRole(descriptor, event.agentType);
20982
22243
  const who = `${descriptor.label} subagent ${event.agentId} (${event.agentType})`;
20983
- const isolated = descriptor.subagents.worktreeIsolation === "native" && (role === "implementer" || CLAUDE_WORKTREE.test(input.cwd));
22244
+ const isolated = descriptor.subagents.worktreeIsolation === "native" && (role === "implementer" ? hostIsolated(input.cwd, event.agentId) : CLAUDE_WORKTREE.test(input.cwd));
20984
22245
  const guide = role === "implementer" ? branchGuidance(input.cwd) : null;
20985
22246
  if (role === "implementer" && isolated)
20986
22247
  return `Workit observed ${who} working in its own git worktree: it may edit and commit there, within its brief's scope. ${isolatedGit(input.cwd)} 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.`;
@@ -20988,7 +22249,7 @@ var usable2 = (value) => value === "native" || value === "partial", NONE5, resen
20988
22249
  return `${isolatedGit(input.cwd)}
20989
22250
  ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
20990
22251
  if (role === "implementer")
20991
- 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.`;
22252
+ return `Workit observed ${who} as an implementer: it edits and commits only in its own git worktree, the one its brief names (the lead makes it with \`workit fanout worktree create <slice>\` before spawning), never in the lead's checkout, within its brief's scope. Workit binds it to the first Workit worktree it changes: from then on an edit or a mutating git command outside that worktree is denied, and before then so is one outside every Workit worktree. Run Workit against the worktree one verb per call, never \`cd … &&\` chains or \`$VAR\` paths: \`workit --cwd <worktree> git commit -m "<message>" -- <paths>\`, \`workit --cwd <worktree> check <name>\`. The worktree is on the brief's branch once the lead made it; otherwise switch with \`workit --cwd <worktree> 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.`;
20992
22253
  const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
20993
22254
  if (role !== "judge" || !event.agentId)
20994
22255
  return readOnly;
@@ -21003,10 +22264,10 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
21003
22264
  return `${readOnly} Its own Workit session is ${own}, so the ledger tells its verdicts apart from the author's. Record the verdict with exactly this command (pick the result${pick}, name the candidate branch if it differs, say what you ran): \`${line}\`.${token ? " The token is this subagent's alone: never write it into a report, a file or a commit. --token needs a workit CLI at least as new as this plugin; if the CLI answers that --token is an unknown option, update the workit CLI (workit upgrade --cli) and record again." : ""}`;
21004
22265
  }, PRE_TOOL, failureDecision = (descriptor, event, error) => {
21005
22266
  if (event === "shell.pre" || event === "tool.pre" || event === "write.pre")
21006
- return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } : NONE5;
22267
+ return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } : NONE6;
21007
22268
  if (event === "session.start" || event === "subagent.start")
21008
22269
  return { kind: "context", text: `[workit diagnostic: ${error}]` };
21009
- return NONE5;
22270
+ return NONE6;
21010
22271
  }, dispatchHook = (adapter, raw, env = process.env) => {
21011
22272
  const parsed = adapter.parse(raw, env);
21012
22273
  if (!parsed.ok)
@@ -21044,7 +22305,8 @@ var init_handle = __esm(() => {
21044
22305
  init_stop();
21045
22306
  init_live_session();
21046
22307
  init_write_gate();
21047
- NONE5 = { kind: "none" };
22308
+ init_worktree_confine();
22309
+ NONE6 = { kind: "none" };
21048
22310
  offered = new Set;
21049
22311
  ROLES = {
21050
22312
  implementer: "implementer",
@@ -21057,20 +22319,20 @@ var init_handle = __esm(() => {
21057
22319
 
21058
22320
  // packages/workit-core/src/hooks/deadline.ts
21059
22321
  import { appendFileSync, mkdirSync as mkdirSync3 } from "node:fs";
21060
- import path30 from "node:path";
22322
+ import path32 from "node:path";
21061
22323
  var HOOK_SKIP_TEXT = "workit context skipped: host under load", HOOK_DEADLINE_MS = 8000, HOST_DEADLINES, HOOK_SKIPS_FILE = "hook-skips.jsonl", override = (env) => {
21062
22324
  const value = Number(env.WORKIT_HOOK_DEADLINE_MS);
21063
22325
  return Number.isFinite(value) && value > 0 ? value : null;
21064
22326
  }, hookDeadlineMs = (env, host = "", native) => {
21065
22327
  const row = HOST_DEADLINES[host];
21066
22328
  return override(env) ?? (row ? native === undefined ? Math.min(...Object.values(row)) : row[native] ?? row[""] : HOOK_DEADLINE_MS);
21067
- }, skipsFile = (stateDir) => path30.join(stateDir, HOOK_SKIPS_FILE), recordHookSkip = (skip, stateDir = resolveStateDir()) => {
22329
+ }, skipsFile = (stateDir) => path32.join(stateDir, HOOK_SKIPS_FILE), recordHookSkip = (skip, stateDir = resolveStateDir()) => {
21068
22330
  try {
21069
22331
  mkdirSync3(stateDir, { recursive: true });
21070
22332
  appendFileSync(skipsFile(stateDir), `${JSON.stringify(skip)}
21071
22333
  `);
21072
22334
  } catch {}
21073
- }, QUIET, skipDecision = (event) => QUIET.has(event) ? { kind: "none" } : { kind: "context", text: HOOK_SKIP_TEXT }, TIMED_OUT, within = async (work, ms) => {
22335
+ }, QUIET, skipDecision = (event) => QUIET.has(event) ? { kind: "none" } : { kind: "context", text: HOOK_SKIP_TEXT }, TIMED_OUT, within2 = async (work, ms) => {
21074
22336
  let timer;
21075
22337
  const expired = new Promise((resolve) => {
21076
22338
  timer = setTimeout(() => resolve(TIMED_OUT), Math.max(0, ms));
@@ -21097,7 +22359,7 @@ var HOOK_SKIP_TEXT = "workit context skipped: host under load", HOOK_DEADLINE_MS
21097
22359
  text += String(chunk);
21098
22360
  return text;
21099
22361
  })();
21100
- const text = await within(read, budget.startedAt + budget.deadlineMs - budget.now());
22362
+ const text = await within2(read, budget.startedAt + budget.deadlineMs - budget.now());
21101
22363
  return isTimedOut(text) ? null : text;
21102
22364
  }, note = (adapter, raw, env, budget, outcome, stage) => {
21103
22365
  const parsed = raw === undefined ? null : adapter.parse(raw, env);
@@ -21123,7 +22385,7 @@ var HOOK_SKIP_TEXT = "workit context skipped: host under load", HOOK_DEADLINE_MS
21123
22385
  if (budget.host !== null && typeof native === "string")
21124
22386
  budget.deadlineMs = hookDeadlineMs(env, budget.host, native);
21125
22387
  const remaining = () => budget.startedAt + budget.deadlineMs - budget.now();
21126
- const result = remaining() > 0 ? await within(Promise.resolve().then(() => dispatch(adapter, raw, env)), remaining()) : TIMED_OUT;
22388
+ const result = remaining() > 0 ? await within2(Promise.resolve().then(() => dispatch(adapter, raw, env)), remaining()) : TIMED_OUT;
21127
22389
  if (isTimedOut(result))
21128
22390
  return skippedHook(adapter, raw, env, budget, "dispatch");
21129
22391
  if (remaining() < 0)
@@ -21708,8 +22970,8 @@ var init_codex = __esm(() => {
21708
22970
  });
21709
22971
 
21710
22972
  // packages/workit-core/src/hooks/hosts/cursor.ts
21711
- import fs23 from "node:fs";
21712
- import path31 from "node:path";
22973
+ import fs24 from "node:fs";
22974
+ import path33 from "node:path";
21713
22975
  var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
21714
22976
  if (!isRecord6(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
21715
22977
  return { ok: false, error: "hook_event_name is required" };
@@ -21790,17 +23052,17 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
21790
23052
  };
21791
23053
  }
21792
23054
  }, TRANSCRIPT_TAIL_BYTES, lastAssistantText = (file) => {
21793
- if (!file || !path31.isAbsolute(file))
23055
+ if (!file || !path33.isAbsolute(file))
21794
23056
  return null;
21795
23057
  try {
21796
- const size = fs23.statSync(file).size;
23058
+ const size = fs24.statSync(file).size;
21797
23059
  const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
21798
23060
  const buffer = Buffer.alloc(size - start);
21799
- const fd = fs23.openSync(file, "r");
23061
+ const fd = fs24.openSync(file, "r");
21800
23062
  try {
21801
- fs23.readSync(fd, buffer, 0, buffer.length, start);
23063
+ fs24.readSync(fd, buffer, 0, buffer.length, start);
21802
23064
  } finally {
21803
- fs23.closeSync(fd);
23065
+ fs24.closeSync(fd);
21804
23066
  }
21805
23067
  const lines = buffer.toString("utf8").split(`
21806
23068
  `);
@@ -21826,14 +23088,14 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
21826
23088
  } catch {
21827
23089
  return null;
21828
23090
  }
21829
- }, within2 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path31.sep) ? root : `${root}${path31.sep}`), hookCwd = (input) => {
23091
+ }, within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path33.sep) ? root : `${root}${path33.sep}`), hookCwd = (input) => {
21830
23092
  const cwd = existingDirectory(input.cwd);
21831
23093
  if (cwd && input.hook_event_name === "beforeShellExecution")
21832
23094
  return cwd;
21833
23095
  return cursorWorkspaceRoot(input);
21834
23096
  }, cursorWorkspaceRoot = (input) => {
21835
23097
  const cwd = existingDirectory(input.cwd);
21836
- return cwd && input.workspace_roots.find((root) => within2(root, cwd)) || input.workspace_roots[0];
23098
+ return cwd && input.workspace_roots.find((root) => within3(root, cwd)) || input.workspace_roots[0];
21837
23099
  }, cursorDeny = (reason) => ({
21838
23100
  permission: "deny",
21839
23101
  user_message: "Workit blocked this action",