@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.
@@ -6739,8 +6739,8 @@ function requirementId(input) {
6739
6739
  import { realpathSync as realpathSync4 } from "node:fs";
6740
6740
 
6741
6741
  // packages/workit-core/src/core/policy/derive.ts
6742
- import { existsSync as existsSync2 } from "node:fs";
6743
- import path10 from "node:path";
6742
+ import { existsSync as existsSync3 } from "node:fs";
6743
+ import path11 from "node:path";
6744
6744
 
6745
6745
  // packages/workit-core/src/git/rev.ts
6746
6746
  import { spawnSync } from "node:child_process";
@@ -6903,6 +6903,124 @@ function resolveRef(cwd, ref) {
6903
6903
  return null;
6904
6904
  return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
6905
6905
  }
6906
+ var LANE_DIFF = [
6907
+ "-c",
6908
+ "core.quotePath=false",
6909
+ "diff",
6910
+ "--no-color",
6911
+ "--no-ext-diff",
6912
+ "--no-textconv",
6913
+ "--no-renames"
6914
+ ];
6915
+ var laneRange = (fork, head) => {
6916
+ if (!safeArg(fork) || head !== null && !safeArg(head))
6917
+ return null;
6918
+ return head === null ? [fork] : [fork, head];
6919
+ };
6920
+ function diffNumstat(cwd, fork, head) {
6921
+ const range = laneRange(fork, head);
6922
+ if (!range)
6923
+ return null;
6924
+ const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
6925
+ if (!run.ok)
6926
+ return null;
6927
+ const out = [];
6928
+ for (const record of run.stdout.split("\x00")) {
6929
+ const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
6930
+ if (!match)
6931
+ continue;
6932
+ const binary = match[1] === "-";
6933
+ out.push({
6934
+ path: match[3],
6935
+ added: binary ? 0 : Number(match[1]),
6936
+ deleted: binary ? 0 : Number(match[2]),
6937
+ binary
6938
+ });
6939
+ }
6940
+ return out;
6941
+ }
6942
+ function diffRaw(cwd, fork, head) {
6943
+ const range = laneRange(fork, head);
6944
+ if (!range)
6945
+ return null;
6946
+ const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
6947
+ if (!run.ok)
6948
+ return null;
6949
+ const fields = run.stdout.split("\x00");
6950
+ const out = [];
6951
+ for (let index = 0;index + 1 < fields.length; index += 2) {
6952
+ const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
6953
+ if (!meta)
6954
+ return null;
6955
+ out.push({
6956
+ path: fields[index + 1],
6957
+ oldMode: meta[1],
6958
+ newMode: meta[2],
6959
+ oldBlob: meta[3],
6960
+ newBlob: meta[4]
6961
+ });
6962
+ }
6963
+ return out;
6964
+ }
6965
+ function readBlobs(cwd, ids) {
6966
+ const out = new Map;
6967
+ const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
6968
+ if (!wanted.length)
6969
+ return out;
6970
+ const run = spawnSync("git", ["cat-file", "--batch"], {
6971
+ cwd,
6972
+ input: `${wanted.join(`
6973
+ `)}
6974
+ `,
6975
+ maxBuffer: MAX_BUFFER,
6976
+ timeout: GIT_TIMEOUTS.local,
6977
+ killSignal: "SIGKILL",
6978
+ windowsHide: true
6979
+ });
6980
+ if (run.status !== 0 || !run.stdout)
6981
+ return null;
6982
+ const bytes = run.stdout;
6983
+ let at = 0;
6984
+ for (const id of wanted) {
6985
+ const end = bytes.indexOf(10, at);
6986
+ if (end < 0)
6987
+ return null;
6988
+ const header = bytes.subarray(at, end).toString("utf8").split(" ");
6989
+ at = end + 1;
6990
+ if (header[1] === "missing")
6991
+ continue;
6992
+ const size = Number(header[2]);
6993
+ if (header[0] !== id || !Number.isSafeInteger(size))
6994
+ return null;
6995
+ out.set(id, bytes.subarray(at, at + size));
6996
+ at += size + 1;
6997
+ }
6998
+ return out;
6999
+ }
7000
+ function treeFilesNamed(cwd, rev, name) {
7001
+ if (!safeArg(rev))
7002
+ return null;
7003
+ const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
7004
+ if (!run.ok)
7005
+ return null;
7006
+ const out = new Map;
7007
+ for (const record of run.stdout.split("\x00")) {
7008
+ const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
7009
+ if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
7010
+ out.set(match[2], match[1]);
7011
+ }
7012
+ return out;
7013
+ }
7014
+ function fileAt(cwd, rev, file) {
7015
+ if (!safeArg(rev))
7016
+ return null;
7017
+ const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
7018
+ return run.ok ? run.stdout : null;
7019
+ }
7020
+ function untrackedFiles(cwd) {
7021
+ const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
7022
+ return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
7023
+ }
6906
7024
  var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
6907
7025
  function currentBranch(cwd) {
6908
7026
  return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
@@ -7716,7 +7834,7 @@ var workspaceConfigSchema = object({
7716
7834
  }).passthrough().optional(),
7717
7835
  autoApprove: union([boolean2(), array(nonBlank)]).optional(),
7718
7836
  defaultEndpoint: unknown().optional(),
7719
- verification: _enum(["self", "independent"]).optional(),
7837
+ verification: _enum(["proportional", "self", "independent"]).optional(),
7720
7838
  delegation: unknown().optional(),
7721
7839
  trailers: boolean2().optional(),
7722
7840
  liveSessionGuard: boolean2().optional(),
@@ -10011,7 +10129,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
10011
10129
  verdict: failing?.verdict ?? null,
10012
10130
  reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
10013
10131
  };
10014
- const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
10132
+ 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))));
10133
+ const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
10015
10134
  return {
10016
10135
  branch,
10017
10136
  head,
@@ -10055,12 +10174,682 @@ var OBSERVED_ONLY = new Set([
10055
10174
  ...PR_ROW_TYPES
10056
10175
  ]);
10057
10176
 
10177
+ // packages/workit-core/src/core/policy/lane.ts
10178
+ import { existsSync as existsSync2, readFileSync as readFileSync3 } from "node:fs";
10179
+ import path10 from "node:path";
10180
+ // packages/workit-core/src/forge/redact.ts
10181
+ var 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_.-]*`;
10182
+ var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*["']?)((?:bearer|basic|token|digest)\s+)?([^\s"',;&]+)`, "giu");
10183
+ var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
10184
+ var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
10185
+
10186
+ // packages/workit-core/src/git/ops.ts
10187
+ var COMMIT_TIMEOUT_MS = 10 * 60000;
10188
+
10189
+ // packages/workit-core/src/core/policy/attributes.ts
10190
+ function patternRegex(dir, pattern) {
10191
+ if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
10192
+ return null;
10193
+ const anchored = pattern.includes("/");
10194
+ const body = pattern.replace(/^\//u, "");
10195
+ let source = "";
10196
+ for (let index = 0;index < body.length; index += 1) {
10197
+ const char = body[index];
10198
+ if (char === "*" && body[index + 1] === "*") {
10199
+ const slash = body[index + 2] === "/";
10200
+ source += slash ? "(?:.*/)?" : ".*";
10201
+ index += slash ? 2 : 1;
10202
+ } else if (char === "*")
10203
+ source += "[^/]*";
10204
+ else if (char === "?")
10205
+ source += "[^/]";
10206
+ else if (char === "[") {
10207
+ const end = body.indexOf("]", index + 2);
10208
+ if (end < 0)
10209
+ return null;
10210
+ const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
10211
+ source += `[${set}]`;
10212
+ index = end;
10213
+ } else if (char === "\\" && index + 1 < body.length) {
10214
+ source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
10215
+ index += 1;
10216
+ } else
10217
+ source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
10218
+ }
10219
+ const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
10220
+ try {
10221
+ return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
10222
+ } catch {
10223
+ return null;
10224
+ }
10225
+ }
10226
+ function rulesOf(dir, text) {
10227
+ const out = [];
10228
+ for (const raw of text.split(`
10229
+ `)) {
10230
+ const line = raw.trim();
10231
+ if (!line || line.startsWith("#") || line.startsWith("[attr]"))
10232
+ continue;
10233
+ const [pattern, ...attributes] = line.split(/\s+/u);
10234
+ let generated = null;
10235
+ for (const attribute of attributes) {
10236
+ if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
10237
+ generated = true;
10238
+ else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
10239
+ generated = false;
10240
+ }
10241
+ if (generated === null)
10242
+ continue;
10243
+ const regex = patternRegex(dir, pattern);
10244
+ if (regex)
10245
+ out.push({ regex, generated });
10246
+ }
10247
+ return out;
10248
+ }
10249
+ function generatedByAttributes(files, paths) {
10250
+ const rules = [...files].map(([file, text]) => {
10251
+ const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
10252
+ return {
10253
+ depth: dir ? dir.split("/").length : 0,
10254
+ rules: rulesOf(dir, text)
10255
+ };
10256
+ }).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
10257
+ const out = new Set;
10258
+ for (const file of paths) {
10259
+ const last = rules.findLast((rule) => rule.regex.test(file));
10260
+ if (last?.generated)
10261
+ out.add(file);
10262
+ }
10263
+ return out;
10264
+ }
10265
+
10266
+ // packages/workit-core/src/core/policy/substance.ts
10267
+ var SLASH_BLOCK = [["/*", "*/"]];
10268
+ var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
10269
+ var SYNTAXES = Object.fromEntries([
10270
+ ...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
10271
+ ...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
10272
+ ext,
10273
+ { ...C_LIKE, triple: true }
10274
+ ]),
10275
+ ["go", { ...C_LIKE, rawBacktick: true }],
10276
+ ...["css", "scss", "less"].map((ext) => [
10277
+ ext,
10278
+ { line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
10279
+ ]),
10280
+ ...["py", "pyi"].map((ext) => [
10281
+ ext,
10282
+ { line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
10283
+ ]),
10284
+ ["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
10285
+ ...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
10286
+ ext,
10287
+ { line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
10288
+ ])
10289
+ ]);
10290
+ var 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;
10291
+ var extensionOf = (file) => {
10292
+ const name = file.slice(file.lastIndexOf("/") + 1);
10293
+ const dot = name.lastIndexOf(".");
10294
+ return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
10295
+ };
10296
+ var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
10297
+ var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
10298
+ var SPACE = /[ \t\r\f\v]/u;
10299
+ var WORD = /[\w$]/u;
10300
+ var NO_SPACE_AFTER = "([{,;";
10301
+ var NO_SPACE_BEFORE = ")]},;";
10302
+ var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
10303
+ var REGEX_KEYWORDS = new Set([
10304
+ "return",
10305
+ "typeof",
10306
+ "instanceof",
10307
+ "case",
10308
+ "do",
10309
+ "else",
10310
+ "in",
10311
+ "of",
10312
+ "new",
10313
+ "delete",
10314
+ "void",
10315
+ "throw",
10316
+ "yield",
10317
+ "await"
10318
+ ]);
10319
+ function substanceOf(file, text) {
10320
+ const syntax = syntaxOf(file);
10321
+ if (!syntax)
10322
+ return plainSubstance(text);
10323
+ const markup = syntax.block.some(([open]) => open === "<!--");
10324
+ if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
10325
+ return plainSubstance(text);
10326
+ return lex(syntax, text);
10327
+ }
10328
+ function plainSubstance(text) {
10329
+ return text.split(`
10330
+ `).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
10331
+ `);
10332
+ }
10333
+ function lex(syntax, text) {
10334
+ const collapse = !syntax.indented;
10335
+ let out = "";
10336
+ let lineStart = 0;
10337
+ let kept = 0;
10338
+ let pendingSpace = false;
10339
+ const frames = [{ kind: "code", braces: 0 }];
10340
+ let index = 0;
10341
+ if (text.startsWith("#!")) {
10342
+ const end = text.indexOf(`
10343
+ `);
10344
+ out = end < 0 ? text : text.slice(0, end);
10345
+ kept = out.length;
10346
+ index = end < 0 ? text.length : end;
10347
+ }
10348
+ const emitCode = (chunk) => {
10349
+ if (collapse && pendingSpace && out.length > lineStart) {
10350
+ const before = out[out.length - 1];
10351
+ if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
10352
+ out += " ";
10353
+ }
10354
+ pendingSpace = false;
10355
+ out += chunk;
10356
+ };
10357
+ const emitVerbatim = (chunk) => {
10358
+ emitCode(chunk);
10359
+ kept = out.length;
10360
+ };
10361
+ const newline = () => {
10362
+ if (!collapse) {
10363
+ let end = out.length;
10364
+ while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
10365
+ end -= 1;
10366
+ out = out.slice(0, end);
10367
+ }
10368
+ pendingSpace = false;
10369
+ if (out.length > lineStart) {
10370
+ out += `
10371
+ `;
10372
+ lineStart = out.length;
10373
+ }
10374
+ };
10375
+ const regexAllowed = () => {
10376
+ let end = out.length;
10377
+ while (end > 0 && /\s/u.test(out[end - 1]))
10378
+ end -= 1;
10379
+ if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
10380
+ return true;
10381
+ let start = end;
10382
+ while (start > 0 && WORD.test(out[start - 1]))
10383
+ start -= 1;
10384
+ return REGEX_KEYWORDS.has(out.slice(start, end));
10385
+ };
10386
+ const scanString = (quote, escapes, multiline) => {
10387
+ const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
10388
+ const fstring = syntax.fstring === true && /f/iu.test(prefix);
10389
+ let end = index + quote.length;
10390
+ while (end < text.length) {
10391
+ const char = text[end];
10392
+ if (escapes && char === "\\") {
10393
+ end += 2;
10394
+ continue;
10395
+ }
10396
+ if (text.startsWith(quote, end)) {
10397
+ if (fstring && unbalanced(text.slice(index + quote.length, end)))
10398
+ return false;
10399
+ emitVerbatim(text.slice(index, end + quote.length));
10400
+ index = end + quote.length;
10401
+ return true;
10402
+ }
10403
+ if (char === `
10404
+ ` && !multiline)
10405
+ return false;
10406
+ end += 1;
10407
+ }
10408
+ return false;
10409
+ };
10410
+ while (index < text.length) {
10411
+ const frame = frames[frames.length - 1];
10412
+ const char = text[index];
10413
+ if (frame.kind === "template") {
10414
+ if (char === "\\") {
10415
+ emitVerbatim(text.slice(index, index + 2));
10416
+ index += 2;
10417
+ } else if (char === "`") {
10418
+ emitVerbatim(char);
10419
+ frames.pop();
10420
+ index += 1;
10421
+ } else if (text.startsWith("${", index)) {
10422
+ emitVerbatim("${");
10423
+ frames.push({ kind: "code", braces: 0 });
10424
+ index += 2;
10425
+ } else {
10426
+ out += char;
10427
+ kept = out.length;
10428
+ index += 1;
10429
+ }
10430
+ continue;
10431
+ }
10432
+ if (char === `
10433
+ `) {
10434
+ newline();
10435
+ index += 1;
10436
+ continue;
10437
+ }
10438
+ if (SPACE.test(char)) {
10439
+ if (collapse)
10440
+ pendingSpace = true;
10441
+ else
10442
+ out += char;
10443
+ index += 1;
10444
+ continue;
10445
+ }
10446
+ const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
10447
+ if (lineOpener) {
10448
+ const end = text.indexOf(`
10449
+ `, index);
10450
+ const comment = text.slice(index, end < 0 ? text.length : end);
10451
+ if (DIRECTIVE.test(comment.slice(lineOpener.length)))
10452
+ emitVerbatim(comment);
10453
+ else
10454
+ pendingSpace = true;
10455
+ index = end < 0 ? text.length : end;
10456
+ continue;
10457
+ }
10458
+ const block = syntax.block.find(([open]) => text.startsWith(open, index));
10459
+ if (block) {
10460
+ const end = text.indexOf(block[1], index + block[0].length);
10461
+ if (end < 0)
10462
+ return null;
10463
+ const comment = text.slice(index, end + block[1].length);
10464
+ if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
10465
+ emitVerbatim(comment);
10466
+ else
10467
+ pendingSpace = true;
10468
+ if (comment.includes(`
10469
+ `))
10470
+ newline();
10471
+ index = end + block[1].length;
10472
+ continue;
10473
+ }
10474
+ if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
10475
+ if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
10476
+ return null;
10477
+ continue;
10478
+ }
10479
+ if (syntax.quotes.includes(char)) {
10480
+ if (!scanString(char, true, Boolean(syntax.multiline)))
10481
+ return null;
10482
+ continue;
10483
+ }
10484
+ if (syntax.literal?.includes(char)) {
10485
+ if (!scanString(char, false, false))
10486
+ return null;
10487
+ continue;
10488
+ }
10489
+ if (syntax.rawBacktick && char === "`") {
10490
+ if (!scanString("`", false, true))
10491
+ return null;
10492
+ continue;
10493
+ }
10494
+ if (syntax.js && char === "`") {
10495
+ emitVerbatim(char);
10496
+ frames.push({ kind: "template" });
10497
+ index += 1;
10498
+ continue;
10499
+ }
10500
+ if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
10501
+ return null;
10502
+ if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
10503
+ const lineEnd = text.indexOf(`
10504
+ `, index);
10505
+ if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
10506
+ return null;
10507
+ }
10508
+ if (syntax.js && char === "/" && regexAllowed()) {
10509
+ let end = index + 1;
10510
+ let inClass = false;
10511
+ while (end < text.length && (inClass || text[end] !== "/")) {
10512
+ if (text[end] === `
10513
+ `)
10514
+ return null;
10515
+ if (text[end] === "\\")
10516
+ end += 1;
10517
+ else if (text[end] === "[")
10518
+ inClass = true;
10519
+ else if (text[end] === "]")
10520
+ inClass = false;
10521
+ end += 1;
10522
+ }
10523
+ if (end >= text.length)
10524
+ return null;
10525
+ emitVerbatim(text.slice(index, end + 1));
10526
+ index = end + 1;
10527
+ continue;
10528
+ }
10529
+ if (frames.length > 1 && frame.kind === "code") {
10530
+ if (char === "{")
10531
+ frame.braces += 1;
10532
+ else if (char === "}") {
10533
+ if (frame.braces === 0) {
10534
+ emitVerbatim(char);
10535
+ frames.pop();
10536
+ index += 1;
10537
+ continue;
10538
+ }
10539
+ frame.braces -= 1;
10540
+ }
10541
+ }
10542
+ emitCode(char);
10543
+ index += 1;
10544
+ }
10545
+ if (frames.length !== 1)
10546
+ return null;
10547
+ newline();
10548
+ return out;
10549
+ }
10550
+ function unbalanced(body) {
10551
+ let depth = 0;
10552
+ for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
10553
+ if (part === "{")
10554
+ depth += 1;
10555
+ else if (part === "}")
10556
+ depth = Math.max(0, depth - 1);
10557
+ }
10558
+ return depth > 0;
10559
+ }
10560
+ function hasOpaqueLineBreak(text) {
10561
+ return /\r(?!\n)|[\u2028\u2029]/u.test(text);
10562
+ }
10563
+ var decoder = new TextDecoder("utf-8", { fatal: true });
10564
+ function textOf(bytes) {
10565
+ if (bytes.includes(0))
10566
+ return null;
10567
+ try {
10568
+ return decoder.decode(bytes);
10569
+ } catch {
10570
+ return null;
10571
+ }
10572
+ }
10573
+ function cosmeticChange(file, before, after) {
10574
+ const left = textOf(before);
10575
+ const right = textOf(after);
10576
+ if (left === null || right === null)
10577
+ return false;
10578
+ if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
10579
+ return false;
10580
+ const a = substanceOf(file, left);
10581
+ return a !== null && a === substanceOf(file, right);
10582
+ }
10583
+
10584
+ // packages/workit-core/src/core/policy/lane.ts
10585
+ var LANE_THRESHOLDS = {
10586
+ selfMaxLines: 100,
10587
+ selfMaxFiles: 5,
10588
+ fullMinLines: 500
10589
+ };
10590
+ var RISK_TOKENS = new Set([
10591
+ "auth",
10592
+ "authn",
10593
+ "authz",
10594
+ "oauth",
10595
+ "security",
10596
+ "permission",
10597
+ "permissions",
10598
+ "secret",
10599
+ "secrets",
10600
+ "crypto",
10601
+ "gate",
10602
+ "gates",
10603
+ "policy",
10604
+ "policies",
10605
+ "hook",
10606
+ "hooks",
10607
+ "husky",
10608
+ "lefthook",
10609
+ "release",
10610
+ "releases",
10611
+ "migration",
10612
+ "migrations",
10613
+ "migrate",
10614
+ "workflows"
10615
+ ]);
10616
+ var RISK_FILES = new Set([
10617
+ ".gitlab-ci.yml",
10618
+ "Jenkinsfile",
10619
+ "azure-pipelines.yml",
10620
+ ".pre-commit-config.yaml",
10621
+ ".workit/risk-paths"
10622
+ ]);
10623
+ var RISK_NAMES = new Set([".gitattributes"]);
10624
+ var RISK_PATHS_FILE = ".workit/risk-paths";
10625
+ var LOCKFILES = new Set([
10626
+ "bun.lock",
10627
+ "bun.lockb",
10628
+ "package-lock.json",
10629
+ "npm-shrinkwrap.json",
10630
+ "yarn.lock",
10631
+ "pnpm-lock.yaml",
10632
+ "Cargo.lock",
10633
+ "go.sum",
10634
+ "poetry.lock",
10635
+ "uv.lock",
10636
+ "Pipfile.lock",
10637
+ "Gemfile.lock",
10638
+ "composer.lock"
10639
+ ]);
10640
+ var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc|txt)$/iu;
10641
+ var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
10642
+ var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
10643
+ var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__|spec|specs|fixtures)\/|\.(?:test|spec)\.[^/]+$/iu;
10644
+ var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
10645
+ var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
10646
+ function kindOf(file) {
10647
+ if (file.binary)
10648
+ return "behavior";
10649
+ if (file.generated)
10650
+ return "generated";
10651
+ if (LOCKFILES.has(basename(file.path)))
10652
+ return "lockfile";
10653
+ if (isDoc(file.path))
10654
+ return "doc";
10655
+ if (file.cosmetic)
10656
+ return "cosmetic";
10657
+ return TEST_PATH.test(file.path) ? "test" : "behavior";
10658
+ }
10659
+ var globCache = new Map;
10660
+ function globMatch(pattern, file) {
10661
+ let regex = globCache.get(pattern);
10662
+ if (!regex) {
10663
+ let source = "";
10664
+ for (let index = 0;index < pattern.length; index += 1) {
10665
+ const char = pattern[index];
10666
+ if (char === "*" && pattern[index + 1] === "*") {
10667
+ source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
10668
+ index += pattern[index + 2] === "/" ? 2 : 1;
10669
+ } else if (char === "*")
10670
+ source += "[^/]*";
10671
+ else if (char === "?")
10672
+ source += "[^/]";
10673
+ else
10674
+ source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
10675
+ }
10676
+ regex = new RegExp(`^${source}$`, "u");
10677
+ globCache.set(pattern, regex);
10678
+ }
10679
+ return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
10680
+ }
10681
+ function isRiskPath(file, extra = []) {
10682
+ if (extra.some((pattern) => globMatch(pattern, file)))
10683
+ return true;
10684
+ if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
10685
+ return true;
10686
+ if (TEST_PATH.test(file) || isDoc(file))
10687
+ return false;
10688
+ 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)));
10689
+ }
10690
+ var parseRiskPaths = (text) => text.split(`
10691
+ `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
10692
+ function computeLane(input) {
10693
+ const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
10694
+ const risky = new Set(riskFiles);
10695
+ const kinds = input.files.map((file) => ({
10696
+ file,
10697
+ kind: risky.has(file.path) ? "behavior" : kindOf(file)
10698
+ }));
10699
+ const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
10700
+ const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
10701
+ const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
10702
+ const result = (lane, reasons) => ({
10703
+ lane,
10704
+ reasons,
10705
+ lines,
10706
+ files: behavior.length,
10707
+ riskFiles
10708
+ });
10709
+ const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
10710
+ if (input.riskTier === "high")
10711
+ return result("full", ["the task judgment records high risk"]);
10712
+ if (riskFiles.length)
10713
+ return result("full", [
10714
+ `touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
10715
+ ]);
10716
+ if (!behavior.length)
10717
+ return result("none", [
10718
+ input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
10719
+ ]);
10720
+ if (lines >= fullMinLines)
10721
+ return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
10722
+ if (lines > selfMaxLines)
10723
+ return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
10724
+ if (behavior.length > selfMaxFiles)
10725
+ return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
10726
+ if (opaque.length)
10727
+ return result("focused", [
10728
+ `a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
10729
+ ]);
10730
+ return result("self", [
10731
+ `small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
10732
+ ]);
10733
+ }
10734
+ function untrackedStat(cwd, file) {
10735
+ try {
10736
+ const bytes = readFileSync3(path10.join(cwd, file));
10737
+ if (bytes.includes(0))
10738
+ return { path: file, added: 0, deleted: 0, binary: true };
10739
+ const text = bytes.toString("utf8");
10740
+ if (hasOpaqueLineBreak(text))
10741
+ return { path: file, added: 0, deleted: 0, binary: true };
10742
+ const lines = text.split(`
10743
+ `).length - (text.endsWith(`
10744
+ `) ? 1 : 0);
10745
+ return { path: file, added: lines, deleted: 0 };
10746
+ } catch {
10747
+ return { path: file, added: 0, deleted: 0, binary: true };
10748
+ }
10749
+ }
10750
+ var REGULAR = new Set(["100644", "100755"]);
10751
+ var ABSENT = /^0+$/u;
10752
+ function cosmeticFiles(cwd, fork, head) {
10753
+ const raw = diffRaw(cwd, fork, head);
10754
+ if (!raw)
10755
+ return null;
10756
+ const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
10757
+ const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
10758
+ if (!blobs)
10759
+ return null;
10760
+ const out = new Set;
10761
+ const opaque = new Set;
10762
+ for (const entry of candidates) {
10763
+ const before = blobs.get(entry.oldBlob);
10764
+ let after = blobs.get(entry.newBlob);
10765
+ if (!after && head === null && ABSENT.test(entry.newBlob))
10766
+ try {
10767
+ after = readFileSync3(path10.join(cwd, entry.path));
10768
+ } catch {
10769
+ after = undefined;
10770
+ }
10771
+ if (before && after && cosmeticChange(entry.path, before, after))
10772
+ out.add(entry.path);
10773
+ else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
10774
+ opaque.add(entry.path);
10775
+ }
10776
+ return { cosmetic: out, opaque };
10777
+ }
10778
+ function generatedAt(cwd, fork, paths) {
10779
+ const files = treeFilesNamed(cwd, fork, ".gitattributes");
10780
+ const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
10781
+ if (!files || !blobs)
10782
+ return new Set;
10783
+ const texts = new Map;
10784
+ for (const [file, id] of files) {
10785
+ const bytes = blobs.get(id);
10786
+ if (bytes)
10787
+ texts.set(file, bytes.toString("utf8"));
10788
+ }
10789
+ return generatedByAttributes(texts, paths);
10790
+ }
10791
+ var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
10792
+ function laneFiles(cwd, fork, head) {
10793
+ const stats = diffNumstat(cwd, fork, head);
10794
+ const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
10795
+ if (!stats || !cosmetic)
10796
+ return null;
10797
+ const files = [
10798
+ ...stats.map((entry) => ({
10799
+ ...entry,
10800
+ ...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
10801
+ cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
10802
+ })),
10803
+ ...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
10804
+ ];
10805
+ const generated = generatedAt(cwd, fork, files.map((entry) => entry.path));
10806
+ return files.map((entry) => ({
10807
+ ...entry,
10808
+ generated: generated.has(entry.path)
10809
+ }));
10810
+ }
10811
+ function branchLane(cwd, options) {
10812
+ const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
10813
+ const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
10814
+ const head = options.workingTree ? null : options.head;
10815
+ const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
10816
+ const files = fork ? laneFiles(cwd, fork, head) : null;
10817
+ const riskTier = options.judgment?.riskTier ?? null;
10818
+ if (!fork || !files) {
10819
+ const high = riskTier === "high";
10820
+ return {
10821
+ lane: high ? "full" : "focused",
10822
+ reasons: [
10823
+ `the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
10824
+ ...high ? ["the task judgment records high risk"] : []
10825
+ ],
10826
+ lines: 0,
10827
+ files: 0,
10828
+ riskFiles: [],
10829
+ base,
10830
+ head: options.head
10831
+ };
10832
+ }
10833
+ const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
10834
+ return {
10835
+ ...computeLane({
10836
+ files,
10837
+ riskTier,
10838
+ riskPaths: [...repoRisk, ...options.riskPaths ?? []]
10839
+ }),
10840
+ base,
10841
+ head: options.head
10842
+ };
10843
+ }
10844
+ var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
10845
+
10058
10846
  // packages/workit-core/src/core/policy/derive.ts
10059
10847
  var RULES = {
10060
10848
  test: "check:test",
10061
10849
  self: "verdict:self",
10062
10850
  nonAuthor: "verdict:non-author",
10063
10851
  verified: "verdict:verified",
10852
+ lane: "verdict:lane",
10064
10853
  product: "decision:product",
10065
10854
  plan: "plan"
10066
10855
  };
@@ -10083,6 +10872,7 @@ var normalizedScope = (scope) => ({
10083
10872
  function deriveRequirements(judgment, scope, constraints, verification = "self") {
10084
10873
  const out = [];
10085
10874
  const base = { scope, dependentAction: null };
10875
+ const proportional = verification === "proportional";
10086
10876
  if (judgment.behaviorChange)
10087
10877
  out.push(derived2({
10088
10878
  ...base,
@@ -10093,7 +10883,17 @@ function deriveRequirements(judgment, scope, constraints, verification = "self")
10093
10883
  before: "close",
10094
10884
  acceptanceAllowed: false
10095
10885
  }));
10096
- if (judgment.behaviorChange && judgment.riskTier === "normal")
10886
+ if (proportional && judgment.riskTier === "normal")
10887
+ out.push(derived2({
10888
+ ...base,
10889
+ ruleId: RULES.lane,
10890
+ dimension: "review",
10891
+ 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.",
10892
+ 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.",
10893
+ before: "close",
10894
+ acceptanceAllowed: false
10895
+ }));
10896
+ if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
10097
10897
  out.push(verification === "independent" ? derived2({
10098
10898
  ...base,
10099
10899
  ruleId: RULES.nonAuthor,
@@ -10196,7 +10996,7 @@ var productDecision = (task, ledger) => {
10196
10996
  };
10197
10997
  };
10198
10998
  var planRecorded = (task, root) => {
10199
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync2(path10.resolve(root, ref.path)));
10999
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync3(path11.resolve(root, ref.path)));
10200
11000
  if (cited)
10201
11001
  return {
10202
11002
  met: true,
@@ -10210,14 +11010,30 @@ var planRecorded = (task, root) => {
10210
11010
  decisionIds: []
10211
11011
  };
10212
11012
  };
10213
- var verdict = (requirement, root, ledger) => {
11013
+ var workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) });
11014
+ var laneVerdict = (task, root, ledger) => {
11015
+ if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
11016
+ return {
11017
+ met: true,
11018
+ reason: "no branch to read a lane from, and the judgment records no behavior change",
11019
+ decisionIds: []
11020
+ };
11021
+ const lane = workingLane(task, root);
11022
+ const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
11023
+ const need = laneNeed(lane.lane);
11024
+ if (need === "none")
11025
+ return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
11026
+ const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
11027
+ return { ...status, reason: `${label}: ${status.reason}` };
11028
+ };
11029
+ var verdict = (ruleId, root, ledger) => {
10214
11030
  const branch = ledger.branch();
10215
11031
  if (!branch)
10216
11032
  return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
10217
11033
  const check = checkVerdicts(root, branch, ledger.rows());
10218
11034
  const failing = check.accepted.reasons.includes("failing_verdict");
10219
11035
  const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
10220
- 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)));
11036
+ 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)));
10221
11037
  if (met)
10222
11038
  return {
10223
11039
  met: true,
@@ -10225,7 +11041,7 @@ var verdict = (requirement, root, ledger) => {
10225
11041
  decisionIds: []
10226
11042
  };
10227
11043
  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, " ");
10228
- 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>\``;
11044
+ 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>\``;
10229
11045
  return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
10230
11046
  };
10231
11047
  function derivedRuleStatus(task, requirement, root, ledger) {
@@ -10237,7 +11053,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
10237
11053
  case RULES.self:
10238
11054
  case RULES.nonAuthor:
10239
11055
  case RULES.verified:
10240
- return verdict(requirement, root, ledger);
11056
+ return verdict(requirement.ruleId, root, ledger);
11057
+ case RULES.lane:
11058
+ return laneVerdict(task, root, ledger);
10241
11059
  default:
10242
11060
  return null;
10243
11061
  }
@@ -10812,7 +11630,7 @@ var judgeInputSummary = (raw) => {
10812
11630
 
10813
11631
  // packages/workit-core/src/autonomy.ts
10814
11632
  import os4 from "node:os";
10815
- import path11 from "node:path";
11633
+ import path12 from "node:path";
10816
11634
  var accountHome = () => {
10817
11635
  try {
10818
11636
  return os4.userInfo().homedir;
@@ -10821,10 +11639,10 @@ var accountHome = () => {
10821
11639
  }
10822
11640
  };
10823
11641
  var grantsHome = { resolve: accountHome };
10824
- var grantsDir = () => path11.join(grantsHome.resolve(), ".config", "workit");
11642
+ var grantsDir = () => path12.join(grantsHome.resolve(), ".config", "workit");
10825
11643
  var grantsOverride = () => {
10826
- const configured = path11.resolve(resolveConfigDir());
10827
- 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`;
11644
+ const configured = path12.resolve(resolveConfigDir());
11645
+ 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`;
10828
11646
  };
10829
11647
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
10830
11648
  var DEFAULT_GRANTS = Object.freeze({
@@ -10877,8 +11695,16 @@ function endpointView(entry, context) {
10877
11695
  ...issue ? { endpointIssue: issue } : {}
10878
11696
  };
10879
11697
  }
10880
- var DEFAULT_VERIFICATION = "self";
10881
- var verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self";
11698
+ var VERIFICATION_MODES = [
11699
+ "proportional",
11700
+ "self",
11701
+ "independent"
11702
+ ];
11703
+ var DEFAULT_VERIFICATION = "proportional";
11704
+ var verificationOf = (entry) => {
11705
+ const value = entry?.verification;
11706
+ return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
11707
+ };
10882
11708
  var isGrantValue = (value) => value === true || value === false || value === "verified";
10883
11709
  var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
10884
11710
  var fromAutoApprove = (value) => {
@@ -10950,7 +11776,7 @@ var grantHint = (workspace, kind) => `ask the user to run, in their own terminal
10950
11776
  // packages/workit-core/src/core/task-store.ts
10951
11777
  import { createHash as createHash11, randomUUID as randomUUID3 } from "node:crypto";
10952
11778
  import * as fs12 from "node:fs";
10953
- import path16 from "node:path";
11779
+ import path17 from "node:path";
10954
11780
 
10955
11781
  // node_modules/@openclaw/fs-safe/dist/errors.js
10956
11782
  var OPERATIONAL_CODES = new Set([
@@ -11001,7 +11827,7 @@ function sameFileIdentity(left, right, platform = process.platform) {
11001
11827
  // node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js
11002
11828
  import { randomBytes as randomBytes3 } from "node:crypto";
11003
11829
  import fsSync from "node:fs";
11004
- import path12 from "node:path";
11830
+ import path13 from "node:path";
11005
11831
 
11006
11832
  // node_modules/@openclaw/fs-safe/dist/bounded-read.js
11007
11833
  import fs8 from "node:fs";
@@ -11091,13 +11917,13 @@ ${ownershipToken}
11091
11917
  return { raw, ownershipToken };
11092
11918
  }
11093
11919
  function relativeSidecarLockPath(lockRoot, lockPath) {
11094
- const resolved = path12.resolve(lockPath);
11095
- const lexicalRelative = path12.relative(lockRoot.rootDir, resolved);
11096
- const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path12.sep}`) && !path12.isAbsolute(lexicalRelative) ? lexicalRelative : path12.relative(lockRoot.rootReal, resolved);
11097
- if (!relative || relative === ".." || relative.startsWith(`..${path12.sep}`) || path12.isAbsolute(relative)) {
11920
+ const resolved = path13.resolve(lockPath);
11921
+ const lexicalRelative = path13.relative(lockRoot.rootDir, resolved);
11922
+ const relative = lexicalRelative !== ".." && !lexicalRelative.startsWith(`..${path13.sep}`) && !path13.isAbsolute(lexicalRelative) ? lexicalRelative : path13.relative(lockRoot.rootReal, resolved);
11923
+ if (!relative || relative === ".." || relative.startsWith(`..${path13.sep}`) || path13.isAbsolute(relative)) {
11098
11924
  throw new FsSafeError("outside-workspace", "sidecar lock path is outside lockRoot");
11099
11925
  }
11100
- return relative.split(path12.sep).join(path12.posix.sep);
11926
+ return relative.split(path13.sep).join(path13.posix.sep);
11101
11927
  }
11102
11928
  function parseSidecarLockPayload(raw, parser) {
11103
11929
  if (parser) {
@@ -11244,7 +12070,7 @@ function getFsSafeLockConfig() {
11244
12070
 
11245
12071
  // node_modules/@openclaw/fs-safe/dist/file-lock-sync.js
11246
12072
  import fs10 from "node:fs";
11247
- import path13 from "node:path";
12073
+ import path14 from "node:path";
11248
12074
 
11249
12075
  // node_modules/@openclaw/fs-safe/dist/timing.js
11250
12076
  function sleepSync(ms) {
@@ -11335,26 +12161,26 @@ function canonicalLockParentSync(parent) {
11335
12161
  return process.platform === "win32" ? fs10.realpathSync.native(parent) : fs10.realpathSync(parent);
11336
12162
  }
11337
12163
  function normalizeTargetPath(targetPath) {
11338
- const resolved = path13.resolve(targetPath);
11339
- fs10.mkdirSync(path13.dirname(resolved), { recursive: true });
12164
+ const resolved = path14.resolve(targetPath);
12165
+ fs10.mkdirSync(path14.dirname(resolved), { recursive: true });
11340
12166
  try {
11341
- return path13.join(canonicalLockParentSync(path13.dirname(resolved)), path13.basename(resolved));
12167
+ return path14.join(canonicalLockParentSync(path14.dirname(resolved)), path14.basename(resolved));
11342
12168
  } catch {
11343
12169
  return resolved;
11344
12170
  }
11345
12171
  }
11346
12172
  function boundedLockPath(lockPath, lockRoot) {
11347
- const resolved = path13.resolve(lockPath);
12173
+ const resolved = path14.resolve(lockPath);
11348
12174
  if (!lockRoot)
11349
12175
  return resolved;
11350
12176
  relativeSidecarLockPath(lockRoot, resolved);
11351
- const parent = path13.dirname(resolved);
12177
+ const parent = path14.dirname(resolved);
11352
12178
  const parentReal = canonicalLockParentSync(parent);
11353
- const parentRelative = path13.relative(lockRoot.rootReal, parentReal);
11354
- if (parentRelative === ".." || parentRelative.startsWith(`..${path13.sep}`) || path13.isAbsolute(parentRelative)) {
12179
+ const parentRelative = path14.relative(lockRoot.rootReal, parentReal);
12180
+ if (parentRelative === ".." || parentRelative.startsWith(`..${path14.sep}`) || path14.isAbsolute(parentRelative)) {
11355
12181
  throw new FsSafeError("outside-workspace", "sidecar lock parent is outside lockRoot");
11356
12182
  }
11357
- return path13.join(parentReal, path13.basename(resolved));
12183
+ return path14.join(parentReal, path14.basename(resolved));
11358
12184
  }
11359
12185
  function defaultShouldReclaim(snapshot, staleMs, nowMs) {
11360
12186
  const createdAtMs = sidecarLockPayloadCreatedAtMs(snapshot.payload);
@@ -11554,14 +12380,14 @@ function acquireFileLockSync(targetPath, options) {
11554
12380
  }
11555
12381
  }
11556
12382
  // packages/workit-core/src/core/package-root.ts
11557
- import { existsSync as existsSync3 } from "node:fs";
11558
- import path14 from "node:path";
12383
+ import { existsSync as existsSync4 } from "node:fs";
12384
+ import path15 from "node:path";
11559
12385
  import { fileURLToPath } from "node:url";
11560
12386
  var packageRoot = () => {
11561
- let dir = path14.dirname(fileURLToPath(import.meta.url));
12387
+ let dir = path15.dirname(fileURLToPath(import.meta.url));
11562
12388
  while (true) {
11563
- const parent = path14.dirname(dir);
11564
- if (existsSync3(path14.join(dir, "package.json")) || parent === dir)
12389
+ const parent = path15.dirname(dir);
12390
+ if (existsSync4(path15.join(dir, "package.json")) || parent === dir)
11565
12391
  return dir;
11566
12392
  dir = parent;
11567
12393
  }
@@ -11570,7 +12396,7 @@ var packageRoot = () => {
11570
12396
  // packages/workit-core/src/store/event-log.ts
11571
12397
  import { randomUUID as randomUUID2 } from "node:crypto";
11572
12398
  import fs11 from "node:fs";
11573
- import path15 from "node:path";
12399
+ import path16 from "node:path";
11574
12400
  var EVENT_VERSION = 1;
11575
12401
 
11576
12402
  class LogDamage extends Error {
@@ -11681,7 +12507,7 @@ function appendEvent(file, event, validEnd) {
11681
12507
  fs11.closeSync(fd);
11682
12508
  }
11683
12509
  if (created)
11684
- fsyncDirectory(path15.dirname(file));
12510
+ fsyncDirectory(path16.dirname(file));
11685
12511
  return validEnd;
11686
12512
  }
11687
12513
  function replaceFile(file, bytes, durable) {
@@ -11697,7 +12523,7 @@ function replaceFile(file, bytes, durable) {
11697
12523
  }
11698
12524
  renameWithRetry(temporary, file);
11699
12525
  if (durable)
11700
- fsyncDirectory(path15.dirname(file));
12526
+ fsyncDirectory(path16.dirname(file));
11701
12527
  } catch (error) {
11702
12528
  try {
11703
12529
  fs11.unlinkSync(temporary);
@@ -12040,7 +12866,7 @@ var cachedRuntimeVersion = null;
12040
12866
  var runtimeVersion = () => {
12041
12867
  if (cachedRuntimeVersion === null) {
12042
12868
  try {
12043
- const pkg = JSON.parse(fs12.readFileSync(path16.join(packageRoot(), "package.json"), "utf8"));
12869
+ const pkg = JSON.parse(fs12.readFileSync(path17.join(packageRoot(), "package.json"), "utf8"));
12044
12870
  cachedRuntimeVersion = typeof pkg.version === "string" && pkg.version ? pkg.version : "0.0.0";
12045
12871
  } catch {
12046
12872
  cachedRuntimeVersion = "0.0.0";
@@ -12073,7 +12899,7 @@ var readText = (file) => {
12073
12899
  };
12074
12900
  var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
12075
12901
  var branchStart = (location, branch, aliases) => {
12076
- const first = readText(path16.join(path16.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
12902
+ const first = readText(path17.join(path17.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
12077
12903
  `, 1)[0];
12078
12904
  const created = REFLOG_LINE.exec(first);
12079
12905
  const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
@@ -12083,7 +12909,7 @@ var branchStart = (location, branch, aliases) => {
12083
12909
  return start;
12084
12910
  if (!location.git)
12085
12911
  return null;
12086
- for (const line of readText(path16.join(location.git.gitDir, "logs", "HEAD")).split(`
12912
+ for (const line of readText(path17.join(location.git.gitDir, "logs", "HEAD")).split(`
12087
12913
  `)) {
12088
12914
  const entry = REFLOG_LINE.exec(line);
12089
12915
  const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
@@ -12093,14 +12919,14 @@ var branchStart = (location, branch, aliases) => {
12093
12919
  }
12094
12920
  return null;
12095
12921
  };
12096
- var refExists = (common, ref) => fs12.existsSync(path16.join(common, ref)) || readText(path16.join(common, "packed-refs")).includes(` ${ref}
12922
+ var refExists = (common, ref) => fs12.existsSync(path17.join(common, ref)) || readText(path17.join(common, "packed-refs")).includes(` ${ref}
12097
12923
  `);
12098
12924
  var origins = new Map;
12099
12925
  var sameDirectoryIdentity = (left, right) => {
12100
- if (!path16.isAbsolute(left) || !path16.isAbsolute(right))
12926
+ if (!path17.isAbsolute(left) || !path17.isAbsolute(right))
12101
12927
  return false;
12102
- const normalizedLeft = path16.resolve(left);
12103
- const normalizedRight = path16.resolve(right);
12928
+ const normalizedLeft = path17.resolve(left);
12929
+ const normalizedRight = path17.resolve(right);
12104
12930
  try {
12105
12931
  const a = fs12.statSync(normalizedLeft, { bigint: true });
12106
12932
  const b = fs12.statSync(normalizedRight, { bigint: true });
@@ -12146,7 +12972,7 @@ class TaskStore {
12146
12972
  migrationChecked = false;
12147
12973
  compactDue = new Set;
12148
12974
  constructor(root, options = {}) {
12149
- const resolved = path16.resolve(root);
12975
+ const resolved = path17.resolve(root);
12150
12976
  this.origin = fs12.existsSync(resolved) ? fs12.realpathSync(resolved) : resolved;
12151
12977
  this.located = resolveStore(this.origin);
12152
12978
  this.root = checkoutRootOf(this.origin, this.located);
@@ -12155,7 +12981,7 @@ class TaskStore {
12155
12981
  this.migrateOnRead = options.migrateOnRead;
12156
12982
  }
12157
12983
  location() {
12158
- if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs12.existsSync(path16.join(this.root, ".git"))) {
12984
+ if (this.located !== undefined && !(this.located instanceof Error) && !this.located.shared && fs12.existsSync(path17.join(this.root, ".git"))) {
12159
12985
  this.located = undefined;
12160
12986
  this.migrationChecked = false;
12161
12987
  }
@@ -12311,7 +13137,7 @@ class TaskStore {
12311
13137
  const moved = this.bindLocked(again.data.id, key, input.now);
12312
13138
  return moved.ok ? success(moved.data.revision, null, { task: moved.data, created: false, key }) : moved;
12313
13139
  }
12314
- const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path16.basename(this.root) || this.root;
13140
+ const label = key.kind === "branch" ? `branch ${key.branch}` : key.kind === "detached" ? "a detached HEAD" : path17.basename(this.root) || this.root;
12315
13141
  const intent = {
12316
13142
  objective: input.objective?.trim() || `Work on ${label}`,
12317
13143
  scope: { description: `the checkout (${label})`, paths: ["."], exclusions: [] },
@@ -12377,14 +13203,14 @@ class TaskStore {
12377
13203
  const branch = key.ok ? key.data.branch : null;
12378
13204
  if (!branch || !location.ok)
12379
13205
  return success(null, null, null);
12380
- const common = path16.dirname(location.data.dir);
12381
- const heads = path16.join(common, "logs", "refs", "heads");
12382
- const headLog = location.data.git ? path16.join(location.data.git.gitDir, "logs", "HEAD") : "";
13206
+ const common = path17.dirname(location.data.dir);
13207
+ const heads = path17.join(common, "logs", "refs", "heads");
13208
+ const headLog = location.data.git ? path17.join(location.data.git.gitDir, "logs", "HEAD") : "";
12383
13209
  const signatures = [
12384
- path16.join(heads, branch),
13210
+ path17.join(heads, branch),
12385
13211
  headLog,
12386
13212
  heads,
12387
- path16.join(common, "packed-refs")
13213
+ path17.join(common, "packed-refs")
12388
13214
  ].map((file) => fileSignature(file) ?? "-");
12389
13215
  const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
12390
13216
  let resolved = origins.get(slot);
@@ -12396,7 +13222,7 @@ class TaskStore {
12396
13222
  aliases.push(rename.from);
12397
13223
  const start = branchStart(location.data, branch, aliases);
12398
13224
  const slash = start?.indexOf("/") ?? -1;
12399
- 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)}"]`));
13225
+ 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)}"]`));
12400
13226
  const name = start && (remote ? start.slice(slash + 1) : start);
12401
13227
  resolved = {
12402
13228
  signature: signatures.join("|"),
@@ -12634,13 +13460,13 @@ class TaskStore {
12634
13460
  if (!ready.ok)
12635
13461
  return ready;
12636
13462
  const dirs = this.paths();
12637
- const recovery = path16.join(this.root, ".workit", "recovery");
13463
+ const recovery = path17.join(this.root, ".workit", "recovery");
12638
13464
  if (fs12.existsSync(recovery)) {
12639
13465
  let files = 0;
12640
13466
  let bytes = 0;
12641
13467
  try {
12642
13468
  for (const name of fs12.readdirSync(recovery)) {
12643
- const stat = fs12.lstatSync(path16.join(recovery, name));
13469
+ const stat = fs12.lstatSync(path17.join(recovery, name));
12644
13470
  if (!stat.isFile())
12645
13471
  continue;
12646
13472
  files += 1;
@@ -12693,7 +13519,7 @@ class TaskStore {
12693
13519
  if (fs12.existsSync(dirs.blobs))
12694
13520
  for (const name of fs12.readdirSync(dirs.blobs)) {
12695
13521
  const digest = name.replace(/\.json$/, "");
12696
- const file = path16.join(dirs.blobs, name);
13522
+ const file = path17.join(dirs.blobs, name);
12697
13523
  let stat;
12698
13524
  try {
12699
13525
  stat = fs12.lstatSync(file);
@@ -12711,7 +13537,7 @@ class TaskStore {
12711
13537
  }
12712
13538
  return success(null, null, null);
12713
13539
  };
12714
- const swept = dryRun ? sweep() : this.withLock(path16.join(dirs.dir, "gc.lock"), sweep);
13540
+ const swept = dryRun ? sweep() : this.withLock(path17.join(dirs.dir, "gc.lock"), sweep);
12715
13541
  if (!swept.ok)
12716
13542
  return swept;
12717
13543
  const temporaryDirs = [
@@ -12719,7 +13545,7 @@ class TaskStore {
12719
13545
  dirs.tasks,
12720
13546
  dirs.blobs,
12721
13547
  dirs.checkout,
12722
- ...names.map((name) => path16.join(dirs.tasks, name))
13548
+ ...names.map((name) => path17.join(dirs.tasks, name))
12723
13549
  ];
12724
13550
  for (const directory of temporaryDirs) {
12725
13551
  if (!fs12.existsSync(directory))
@@ -12727,7 +13553,7 @@ class TaskStore {
12727
13553
  for (const name of fs12.readdirSync(directory)) {
12728
13554
  if (!name.endsWith(".tmp") && !name.endsWith(".probe"))
12729
13555
  continue;
12730
- const file = path16.join(directory, name);
13556
+ const file = path17.join(directory, name);
12731
13557
  try {
12732
13558
  const stat = fs12.lstatSync(file);
12733
13559
  if (!stat.isFile() || nowMs - stat.mtimeMs <= STALE_TEMPORARY_MS)
@@ -12744,30 +13570,30 @@ class TaskStore {
12744
13570
  }
12745
13571
  paths() {
12746
13572
  const location = this.location();
12747
- const dir = location.ok ? location.data.dir : path16.join(this.root, ".workit");
12748
- const checkout = path16.join(dir, "checkouts", checkoutSlug(this.root));
13573
+ const dir = location.ok ? location.data.dir : path17.join(this.root, ".workit");
13574
+ const checkout = path17.join(dir, "checkouts", checkoutSlug(this.root));
12749
13575
  return {
12750
13576
  dir,
12751
13577
  shared: location.ok ? location.data.shared : false,
12752
- marker: path16.join(dir, "store.json"),
13578
+ marker: path17.join(dir, "store.json"),
12753
13579
  checkout,
12754
- workspace: path16.join(checkout, "workspace.json"),
12755
- checkoutLock: path16.join(checkout, "metadata.lock"),
12756
- tasks: path16.join(dir, "tasks"),
12757
- index: path16.join(dir, "task-index.json"),
12758
- blobs: path16.join(dir, "blobs", "candidates"),
12759
- legacy: path16.join(dir, "legacy", checkoutSlug(this.root), "v2"),
12760
- v2: path16.join(this.root, ".workit")
13580
+ workspace: path17.join(checkout, "workspace.json"),
13581
+ checkoutLock: path17.join(checkout, "metadata.lock"),
13582
+ tasks: path17.join(dir, "tasks"),
13583
+ index: path17.join(dir, "task-index.json"),
13584
+ blobs: path17.join(dir, "blobs", "candidates"),
13585
+ legacy: path17.join(dir, "legacy", checkoutSlug(this.root), "v2"),
13586
+ v2: path17.join(this.root, ".workit")
12761
13587
  };
12762
13588
  }
12763
13589
  eventsPath(taskId) {
12764
- return path16.join(this.paths().tasks, taskId, "events.jsonl");
13590
+ return path17.join(this.paths().tasks, taskId, "events.jsonl");
12765
13591
  }
12766
13592
  snapshotPath(taskId) {
12767
- return path16.join(this.paths().tasks, taskId, "snapshot.json");
13593
+ return path17.join(this.paths().tasks, taskId, "snapshot.json");
12768
13594
  }
12769
13595
  taskLockPath(taskId) {
12770
- return path16.join(this.paths().tasks, taskId, "lock");
13596
+ return path17.join(this.paths().tasks, taskId, "lock");
12771
13597
  }
12772
13598
  ready(write = false) {
12773
13599
  const location = this.location();
@@ -12806,13 +13632,13 @@ class TaskStore {
12806
13632
  return {
12807
13633
  read: (digest) => {
12808
13634
  try {
12809
- return JSON.parse(fs12.readFileSync(path16.join(dir, `${digest}.json`), "utf8"));
13635
+ return JSON.parse(fs12.readFileSync(path17.join(dir, `${digest}.json`), "utf8"));
12810
13636
  } catch (error) {
12811
13637
  throw new LogDamage(`stored candidate ${digest} is missing or unreadable: ${String(error)}`);
12812
13638
  }
12813
13639
  },
12814
13640
  write: (digest, value) => {
12815
- const file = path16.join(dir, `${digest}.json`);
13641
+ const file = path17.join(dir, `${digest}.json`);
12816
13642
  if (fs12.existsSync(file)) {
12817
13643
  const stamp = new Date;
12818
13644
  try {
@@ -13004,7 +13830,7 @@ class TaskStore {
13004
13830
  }
13005
13831
  }
13006
13832
  openTask(record, type, key, legacy = null) {
13007
- const dir = path16.join(this.paths().tasks, record.id);
13833
+ const dir = path17.join(this.paths().tasks, record.id);
13008
13834
  fs12.mkdirSync(dir, { recursive: true });
13009
13835
  const blobs = this.blobStore();
13010
13836
  const bound = key ? { ...key, boundAt: key.boundAt ?? new Date().toISOString() } : null;
@@ -13097,7 +13923,7 @@ class TaskStore {
13097
13923
  }
13098
13924
  withKeyLock(key, operation) {
13099
13925
  const digest = createHash11("sha256").update(key).digest("hex").slice(0, 32);
13100
- return this.withLock(path16.join(this.paths().dir, "keys", `${digest}.lock`), operation);
13926
+ return this.withLock(path17.join(this.paths().dir, "keys", `${digest}.lock`), operation);
13101
13927
  }
13102
13928
  bindLocked(taskId, key, timestamp) {
13103
13929
  const current = this.readWorkspace();
@@ -13238,7 +14064,7 @@ class TaskStore {
13238
14064
  writeWorkspace(record) {
13239
14065
  const file = this.paths().workspace;
13240
14066
  try {
13241
- fs12.mkdirSync(path16.dirname(file), { recursive: true });
14067
+ fs12.mkdirSync(path17.dirname(file), { recursive: true });
13242
14068
  replaceFile(file, `${canonicalJson(record)}
13243
14069
  `, true);
13244
14070
  return success(null, null, null);
@@ -13316,23 +14142,23 @@ class TaskStore {
13316
14142
  return failure2("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", { expectedWorkspaceRevision: expected, actualWorkspaceRevision: actual });
13317
14143
  }
13318
14144
  v2Dirs() {
13319
- return [...new Set([this.root, this.origin])].map((dir) => path16.join(dir, ".workit"));
14145
+ return [...new Set([this.root, this.origin])].map((dir) => path17.join(dir, ".workit"));
13320
14146
  }
13321
14147
  pendingMigrations() {
13322
14148
  return this.v2Dirs().filter((dir) => {
13323
- const raw = this.readV2Workspace(path16.join(dir, "workspace.json"));
14149
+ const raw = this.readV2Workspace(path17.join(dir, "workspace.json"));
13324
14150
  return raw !== null && raw !== TOMBSTONE;
13325
14151
  });
13326
14152
  }
13327
14153
  migrateV2(v2) {
13328
14154
  const dirs = this.paths();
13329
- const v2Workspace = path16.join(v2, "workspace.json");
13330
- const v2Tasks = path16.join(v2, "tasks");
13331
- const legacy = path16.join(dirs.dir, "legacy", checkoutSlug(path16.dirname(v2)), "v2");
14155
+ const v2Workspace = path17.join(v2, "workspace.json");
14156
+ const v2Tasks = path17.join(v2, "tasks");
14157
+ const legacy = path17.join(dirs.dir, "legacy", checkoutSlug(path17.dirname(v2)), "v2");
13332
14158
  const raw = this.readV2Workspace(v2Workspace);
13333
14159
  if (raw === null || raw === TOMBSTONE)
13334
14160
  return success(null, null, null);
13335
- return this.withLock(dirs.checkoutLock, () => this.withLock(path16.join(v2, "metadata.lock"), () => {
14161
+ return this.withLock(dirs.checkoutLock, () => this.withLock(path17.join(v2, "metadata.lock"), () => {
13336
14162
  const workspace = this.readV2Workspace(v2Workspace);
13337
14163
  if (workspace === null || workspace === TOMBSTONE)
13338
14164
  return success(null, null, null);
@@ -13341,7 +14167,7 @@ class TaskStore {
13341
14167
  return failure2("recovery_required", `cannot migrate the 2.x store at ${v2}: ${parsedWorkspace.error}; move it aside to start fresh`, { path: v2Workspace });
13342
14168
  let files = [];
13343
14169
  try {
13344
- files = fs12.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path16.join(v2Tasks, name)));
14170
+ files = fs12.readdirSync(v2Tasks).filter((name) => name.endsWith(".json") && validId(name.slice(0, -5))).filter((name) => !this.isStub(path17.join(v2Tasks, name)));
13345
14171
  } catch {}
13346
14172
  const current = this.readWorkspace();
13347
14173
  if (!current.ok)
@@ -13359,21 +14185,21 @@ class TaskStore {
13359
14185
  let skipped = 0;
13360
14186
  const done = new Map;
13361
14187
  try {
13362
- fs12.mkdirSync(path16.join(legacy, "tasks"), { recursive: true });
14188
+ fs12.mkdirSync(path17.join(legacy, "tasks"), { recursive: true });
13363
14189
  for (const name of ["workspace.json", "index.json"])
13364
- if (fs12.existsSync(path16.join(v2, name)))
13365
- fs12.copyFileSync(path16.join(v2, name), path16.join(legacy, name));
14190
+ if (fs12.existsSync(path17.join(v2, name)))
14191
+ fs12.copyFileSync(path17.join(v2, name), path17.join(legacy, name));
13366
14192
  for (const name of files) {
13367
14193
  const id = name.slice(0, -5);
13368
- const file = path16.join(v2Tasks, name);
14194
+ const file = path17.join(v2Tasks, name);
13369
14195
  const bytes = fs12.readFileSync(file, "utf8");
13370
14196
  const digest = sha256(bytes);
13371
- fs12.writeFileSync(path16.join(legacy, "tasks", name), bytes);
14197
+ fs12.writeFileSync(path17.join(legacy, "tasks", name), bytes);
13372
14198
  const parsed = this.parseRecord(bytes, taskRecordSchema, "2.x task");
13373
14199
  if (!parsed.ok || parsed.data.id !== id)
13374
14200
  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 });
13375
14201
  const record = { ...parsed.data, workspaceId: target.id };
13376
- const origin = { path: path16.join(legacy, "tasks", name), digest };
14202
+ const origin = { path: path17.join(legacy, "tasks", name), digest };
13377
14203
  if (!fs12.existsSync(this.eventsPath(id))) {
13378
14204
  this.openTask(record, "migrated.from_v2", null, origin);
13379
14205
  migrated += 1;
@@ -13409,7 +14235,7 @@ class TaskStore {
13409
14235
  try {
13410
14236
  let complete = true;
13411
14237
  for (const [name, digest] of done) {
13412
- const file = path16.join(v2Tasks, name);
14238
+ const file = path17.join(v2Tasks, name);
13413
14239
  if (sha256(fs12.readFileSync(file, "utf8")) !== digest) {
13414
14240
  complete = false;
13415
14241
  continue;
@@ -13420,7 +14246,7 @@ class TaskStore {
13420
14246
  return failure2("busy", `the 2.x store at ${v2} changed during migration; retry`, {
13421
14247
  path: v2
13422
14248
  });
13423
- fs12.rmSync(path16.join(v2, "index.json"), { force: true });
14249
+ fs12.rmSync(path17.join(v2, "index.json"), { force: true });
13424
14250
  this.writeTombstone(v2, target.id);
13425
14251
  } catch (error) {
13426
14252
  return failure2("storage_error", `cannot finish the 2.x migration: ${String(error)}`, {
@@ -13440,7 +14266,7 @@ class TaskStore {
13440
14266
  }
13441
14267
  adoptLocalStore() {
13442
14268
  const dirs = this.paths();
13443
- const marker = path16.join(dirs.v2, "store.json");
14269
+ const marker = path17.join(dirs.v2, "store.json");
13444
14270
  if (!dirs.shared || !fs12.existsSync(marker))
13445
14271
  return success(null, null, null);
13446
14272
  return this.withLock(dirs.checkoutLock, () => {
@@ -13450,7 +14276,7 @@ class TaskStore {
13450
14276
  try {
13451
14277
  const merge = (from, to) => {
13452
14278
  if (!fs12.existsSync(to)) {
13453
- fs12.mkdirSync(path16.dirname(to), { recursive: true });
14279
+ fs12.mkdirSync(path17.dirname(to), { recursive: true });
13454
14280
  try {
13455
14281
  fs12.renameSync(from, to);
13456
14282
  } catch {
@@ -13461,24 +14287,24 @@ class TaskStore {
13461
14287
  }
13462
14288
  if (fs12.lstatSync(from).isDirectory() && fs12.lstatSync(to).isDirectory())
13463
14289
  for (const name of fs12.readdirSync(from))
13464
- merge(path16.join(from, name), path16.join(to, name));
14290
+ merge(path17.join(from, name), path17.join(to, name));
13465
14291
  };
13466
14292
  for (const sub of ["tasks", "checkouts", "blobs", "legacy"]) {
13467
- const from = path16.join(dirs.v2, sub);
14293
+ const from = path17.join(dirs.v2, sub);
13468
14294
  if (!fs12.existsSync(from))
13469
14295
  continue;
13470
14296
  if (sub === "tasks") {
13471
14297
  for (const name of fs12.readdirSync(from))
13472
- if (validId(name) && !fs12.existsSync(path16.join(dirs.dir, sub, name)))
14298
+ if (validId(name) && !fs12.existsSync(path17.join(dirs.dir, sub, name)))
13473
14299
  tasks += 1;
13474
14300
  }
13475
- merge(from, path16.join(dirs.dir, sub));
14301
+ merge(from, path17.join(dirs.dir, sub));
13476
14302
  }
13477
- const ledger = path16.join(dirs.v2, "ledger");
13478
- if (fs12.existsSync(ledger) && !fs12.existsSync(path16.join(dirs.dir, "ledger")))
13479
- merge(ledger, path16.join(dirs.dir, "ledger"));
13480
- fs12.rmSync(path16.join(dirs.v2, "task-index.json"), { force: true });
13481
- fs12.renameSync(marker, path16.join(dirs.v2, "store.moved.json"));
14303
+ const ledger = path17.join(dirs.v2, "ledger");
14304
+ if (fs12.existsSync(ledger) && !fs12.existsSync(path17.join(dirs.dir, "ledger")))
14305
+ merge(ledger, path17.join(dirs.dir, "ledger"));
14306
+ fs12.rmSync(path17.join(dirs.v2, "task-index.json"), { force: true });
14307
+ fs12.renameSync(marker, path17.join(dirs.v2, "store.moved.json"));
13482
14308
  } catch (error) {
13483
14309
  return failure2("storage_error", `cannot move ${dirs.v2} into ${dirs.dir}: ${String(error)}`, {
13484
14310
  path: dirs.v2
@@ -13516,7 +14342,7 @@ class TaskStore {
13516
14342
  schemaVersion: SCHEMA_VERSION,
13517
14343
  id: workspaceId ?? newId(),
13518
14344
  revision: newRevision(),
13519
- root: path16.dirname(v2),
14345
+ root: path17.dirname(v2),
13520
14346
  runtime: { createdWith: version, updatedWith: version },
13521
14347
  writer: null,
13522
14348
  store: {
@@ -13528,11 +14354,11 @@ class TaskStore {
13528
14354
  critical: ["store"]
13529
14355
  };
13530
14356
  fs12.mkdirSync(v2, { recursive: true });
13531
- const ignore = path16.join(v2, ".gitignore");
14357
+ const ignore = path17.join(v2, ".gitignore");
13532
14358
  if (!fs12.existsSync(ignore))
13533
14359
  fs12.writeFileSync(ignore, `*
13534
14360
  `);
13535
- replaceFile(path16.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
14361
+ replaceFile(path17.join(v2, "workspace.json"), `${JSON.stringify(marker, null, 2)}
13536
14362
  `, true);
13537
14363
  }
13538
14364
  stubFor(taskId) {
@@ -13556,8 +14382,8 @@ class TaskStore {
13556
14382
  }
13557
14383
  writeTombstoneIfMissing() {
13558
14384
  try {
13559
- const v2 = path16.join(this.root, ".workit");
13560
- if (fs12.existsSync(path16.join(v2, "workspace.json")))
14385
+ const v2 = path17.join(this.root, ".workit");
14386
+ if (fs12.existsSync(path17.join(v2, "workspace.json")))
13561
14387
  return;
13562
14388
  let id = null;
13563
14389
  try {
@@ -13574,7 +14400,7 @@ class TaskStore {
13574
14400
  replaceFile(dirs.marker, `${JSON.stringify({ format: "workit-store", version: STORE_FORMAT, createdWith: runtimeVersion() })}
13575
14401
  `, false);
13576
14402
  if (!dirs.shared) {
13577
- const ignore = path16.join(dirs.dir, ".gitignore");
14403
+ const ignore = path17.join(dirs.dir, ".gitignore");
13578
14404
  let current = null;
13579
14405
  try {
13580
14406
  current = fs12.readFileSync(ignore, "utf8");
@@ -13634,7 +14460,7 @@ class TaskStore {
13634
14460
  withLock(lockPath, operation) {
13635
14461
  try {
13636
14462
  this.initializeStorage();
13637
- fs12.mkdirSync(path16.dirname(lockPath), { recursive: true });
14463
+ fs12.mkdirSync(path17.dirname(lockPath), { recursive: true });
13638
14464
  } catch (error) {
13639
14465
  return failure2("storage_error", `unable to initialize store: ${String(error)}`, {
13640
14466
  path: this.paths().dir
@@ -13697,7 +14523,7 @@ class TaskStore {
13697
14523
  if (isTransientWindowsError(error) && !this.lockHolderPresent(lockPath) && !this.lockDirWritable(lockPath))
13698
14524
  return failure2("storage_error", `cannot create the workit lock (${code}); no other Workit call holds it`, {
13699
14525
  path: lockPath,
13700
- guidance: `Check permissions and the read-only attribute on ${path16.dirname(lockPath)}.`
14526
+ guidance: `Check permissions and the read-only attribute on ${path17.dirname(lockPath)}.`
13701
14527
  });
13702
14528
  if (code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || isTransientWindowsError(error)) {
13703
14529
  let lock = null;
@@ -13724,18 +14550,18 @@ var workerBlocksTransition = (transition, state) => transition === "pause" ? fal
13724
14550
  import { createHash as createHash12 } from "node:crypto";
13725
14551
  import * as fs14 from "node:fs";
13726
14552
  import { spawnSync as spawnSync10 } from "node:child_process";
13727
- import path18 from "node:path";
14553
+ import path19 from "node:path";
13728
14554
 
13729
14555
  // packages/workit-core/src/check-config.ts
13730
14556
  import { spawnSync as spawnSync9 } from "node:child_process";
13731
14557
  import fs13 from "node:fs";
13732
- import path17 from "node:path";
14558
+ import path18 from "node:path";
13733
14559
  var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
13734
14560
  var CHECKS_FILE = ".workit/checks.json";
13735
14561
  var LEGACY_CHECKS_FILE = "workit.checks.json";
13736
14562
  function checksConfigFile(root) {
13737
- const current = path17.join(root, CHECKS_FILE);
13738
- const legacy = path17.join(root, LEGACY_CHECKS_FILE);
14563
+ const current = path18.join(root, CHECKS_FILE);
14564
+ const legacy = path18.join(root, LEGACY_CHECKS_FILE);
13739
14565
  return !fs13.existsSync(current) && fs13.existsSync(legacy) ? legacy : current;
13740
14566
  }
13741
14567
  function repoHeavyCommands(root) {
@@ -13786,7 +14612,7 @@ function splitCommand(command) {
13786
14612
  argv.push(current);
13787
14613
  return argv.length ? argv : new Error("empty command");
13788
14614
  }
13789
- var git4 = (cwd, args) => {
14615
+ var git5 = (cwd, args) => {
13790
14616
  const run = spawnSync9("git", args, {
13791
14617
  cwd,
13792
14618
  encoding: "utf8",
@@ -13798,10 +14624,10 @@ var git4 = (cwd, args) => {
13798
14624
  return value || null;
13799
14625
  };
13800
14626
  function checkRoot(cwd) {
13801
- return path17.resolve(git4(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
14627
+ return path18.resolve(git5(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
13802
14628
  }
13803
14629
  var packageManager = (root) => {
13804
- const has = (file) => fs13.existsSync(path17.join(root, file));
14630
+ const has = (file) => fs13.existsSync(path18.join(root, file));
13805
14631
  if (has("bun.lock") || has("bun.lockb"))
13806
14632
  return "bun";
13807
14633
  if (has("pnpm-lock.yaml"))
@@ -13821,7 +14647,7 @@ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
13821
14647
  function detectedChecks(root) {
13822
14648
  const read = (file) => {
13823
14649
  try {
13824
- return fs13.readFileSync(path17.join(root, file), "utf8");
14650
+ return fs13.readFileSync(path18.join(root, file), "utf8");
13825
14651
  } catch {
13826
14652
  return null;
13827
14653
  }
@@ -13861,7 +14687,7 @@ var GATES = ["testing", "verification"];
13861
14687
  function loadCheckConfig(cwd) {
13862
14688
  const root = checkRoot(cwd);
13863
14689
  const file = checksConfigFile(root);
13864
- const legacy = path17.basename(file) === LEGACY_CHECKS_FILE;
14690
+ const legacy = path18.basename(file) === LEGACY_CHECKS_FILE;
13865
14691
  const label = legacy ? LEGACY_CHECKS_FILE : CHECKS_FILE;
13866
14692
  const note = legacy ? {
13867
14693
  note: `${LEGACY_CHECKS_FILE} is deprecated; move it to ${CHECKS_FILE} (workit doctor --fix)`
@@ -13939,10 +14765,10 @@ function workitAvailable(env = process.env) {
13939
14765
  return workitOnPath;
13940
14766
  const windows = process.platform === "win32";
13941
14767
  const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
13942
- const dirs = (env.PATH ?? env.Path ?? "").split(path17.delimiter).filter(Boolean);
14768
+ const dirs = (env.PATH ?? env.Path ?? "").split(path18.delimiter).filter(Boolean);
13943
14769
  workitOnPath = dirs.some((dir) => exts.some((ext) => {
13944
14770
  try {
13945
- return fs13.statSync(path17.join(dir, `workit${ext}`)).isFile();
14771
+ return fs13.statSync(path18.join(dir, `workit${ext}`)).isFile();
13946
14772
  } catch {
13947
14773
  return false;
13948
14774
  }
@@ -13955,14 +14781,14 @@ function checkCommand(args, version, onPath = workitAvailable()) {
13955
14781
 
13956
14782
  // packages/workit-core/src/core/task-evaluation.ts
13957
14783
  var digestBytes = (value) => createHash12("sha256").update(value).digest("hex");
13958
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path18.sep}`);
13959
- var relative = (root, candidate) => path18.relative(root, candidate).split(path18.sep).join("/") || ".";
14784
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path19.sep}`);
14785
+ var relative = (root, candidate) => path19.relative(root, candidate).split(path19.sep).join("/") || ".";
13960
14786
  var canonicalPath = (value) => {
13961
14787
  if (typeof value !== "string" || !value)
13962
14788
  return null;
13963
14789
  if (value.includes("\\") || value.split("/").includes(".."))
13964
14790
  return null;
13965
- const normalized = path18.posix.normalize(value);
14791
+ const normalized = path19.posix.normalize(value);
13966
14792
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
13967
14793
  return null;
13968
14794
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -14010,14 +14836,14 @@ var gitPaths = (root) => {
14010
14836
  git: !/not a git repository/i.test(stderr)
14011
14837
  };
14012
14838
  }
14013
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")) : [];
14839
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path19.sep).join("/")) : [];
14014
14840
  const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
14015
14841
  if (stagedDeleted.status !== 0) {
14016
14842
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
14017
14843
  if (!/not a git repository/i.test(stderr))
14018
14844
  return { paths, uncertain: true, git: true };
14019
14845
  } else if (stagedDeleted.stdout) {
14020
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")));
14846
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path19.sep).join("/")));
14021
14847
  }
14022
14848
  return { paths: [...new Set(paths)], uncertain: false, git: true };
14023
14849
  };
@@ -14032,7 +14858,7 @@ var walk = (root, directory, output) => {
14032
14858
  for (const name of names) {
14033
14859
  if (name === ".git" || name === ".workit")
14034
14860
  continue;
14035
- const target = path18.join(directory, name);
14861
+ const target = path19.join(directory, name);
14036
14862
  let stat;
14037
14863
  try {
14038
14864
  stat = fs14.lstatSync(target);
@@ -14050,12 +14876,12 @@ var walk = (root, directory, output) => {
14050
14876
  var scopeRoots = (root, scope) => {
14051
14877
  const roots = [];
14052
14878
  for (const item of scope.paths.length ? scope.paths : ["."]) {
14053
- const target = path18.resolve(root, item);
14879
+ const target = path19.resolve(root, item);
14054
14880
  if (!inside(root, target))
14055
14881
  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` }] });
14056
14882
  let ancestor = target;
14057
14883
  while (!fs14.existsSync(ancestor) && ancestor !== root)
14058
- ancestor = path18.dirname(ancestor);
14884
+ ancestor = path19.dirname(ancestor);
14059
14885
  try {
14060
14886
  if (!inside(root, fs14.realpathSync(ancestor)))
14061
14887
  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` }] });
@@ -14107,7 +14933,7 @@ function captureCandidate(root, scope, environment = []) {
14107
14933
  for (const item of [...names].toSorted()) {
14108
14934
  if (!scopeMatches(item, normalizedScope))
14109
14935
  continue;
14110
- const target = path18.join(checkout, item);
14936
+ const target = path19.join(checkout, item);
14111
14937
  try {
14112
14938
  const stat = fs14.lstatSync(target);
14113
14939
  if (stat.isSymbolicLink()) {
@@ -14291,23 +15117,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
14291
15117
  continue;
14292
15118
  if (!reference.digest)
14293
15119
  return failure2("invalid_input", "document references require a byte digest");
14294
- const target = path18.resolve(root, reference.path);
14295
- if (target !== root && !target.startsWith(`${root}${path18.sep}`))
15120
+ const target = path19.resolve(root, reference.path);
15121
+ if (target !== root && !target.startsWith(`${root}${path19.sep}`))
14296
15122
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
14297
15123
  fields: [
14298
15124
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
14299
15125
  ]
14300
15126
  });
14301
15127
  try {
14302
- const below = path18.relative(root, target);
15128
+ const below = path19.relative(root, target);
14303
15129
  let current = root;
14304
- for (const segment of below.split(path18.sep)) {
15130
+ for (const segment of below.split(path19.sep)) {
14305
15131
  if (!segment)
14306
15132
  continue;
14307
- current = path18.join(current, segment);
15133
+ current = path19.join(current, segment);
14308
15134
  const stat = fs14.lstatSync(current);
14309
15135
  const real = fs14.realpathSync(current);
14310
- if (real !== root && !real.startsWith(`${root}${path18.sep}`))
15136
+ if (real !== root && !real.startsWith(`${root}${path19.sep}`))
14311
15137
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
14312
15138
  fields: [
14313
15139
  {
@@ -14740,9 +15566,9 @@ Autonomy contract:
14740
15566
  Prose is a note: only a configured check the CLI ran (\`workit check <name>\`)
14741
15567
  satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
14742
15568
  - Author is not verifier: your own verdict is \`--self\`, shown as
14743
- self-reviewed, never verified. High risk, \`verification: independent\` and
14744
- merge need a verifier with its own host-derived session (a subagent's
14745
- \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
15569
+ self-reviewed, never verified. High risk, a focused or full lane and
15570
+ merge under \`verification: self|independent\` need a verifier with its
15571
+ own host-derived session (a subagent's \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
14746
15572
  - A local-commit endpoint does not imply PR readiness, and a local commit alone
14747
15573
  is not evidence of a requested remote push. Before reporting delivery,
14748
15574
  reconcile every requested item and observe it (\`workit verify-delivery\`).
@@ -16608,7 +17434,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
16608
17434
  // packages/workit-core/src/hooks/context.ts
16609
17435
  import { createHash as createHash13 } from "node:crypto";
16610
17436
  import fs15 from "node:fs";
16611
- import path19 from "node:path";
17437
+ import path20 from "node:path";
16612
17438
  var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
16613
17439
  var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
16614
17440
  var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
@@ -16693,7 +17519,7 @@ function entryCompactContext(store, entry, session, context) {
16693
17519
  return null;
16694
17520
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
16695
17521
  const observed = task?.data.evidence.some(isObservedCheck) ?? false;
16696
- const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path19.resolve(store.root, ref.path)] : []));
17522
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path20.resolve(store.root, ref.path)] : []));
16697
17523
  const observedFiles = filesKey(files);
16698
17524
  const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
16699
17525
  if (!compact.ok || !compact.data)
@@ -16771,14 +17597,14 @@ var turnMarker = (input) => {
16771
17597
  if (location instanceof Error)
16772
17598
  return null;
16773
17599
  const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
16774
- return path19.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
17600
+ return path20.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
16775
17601
  } catch {
16776
17602
  return null;
16777
17603
  }
16778
17604
  };
16779
17605
  var writeTurnDigest = (file, digest) => {
16780
17606
  try {
16781
- fs15.mkdirSync(path19.dirname(file), { recursive: true });
17607
+ fs15.mkdirSync(path20.dirname(file), { recursive: true });
16782
17608
  fs15.writeFileSync(file, `${JSON.stringify({ digest })}
16783
17609
  `);
16784
17610
  } catch {}
@@ -16793,7 +17619,7 @@ var pruneTurnDigests = (dir, now) => {
16793
17619
  for (const name of fs15.readdirSync(dir)) {
16794
17620
  if (!name.startsWith("turn-"))
16795
17621
  continue;
16796
- const file = path19.join(dir, name);
17622
+ const file = path20.join(dir, name);
16797
17623
  try {
16798
17624
  if (now - fs15.statSync(file).mtimeMs > TURN_TTL_MS)
16799
17625
  fs15.rmSync(file, { force: true });
@@ -16823,7 +17649,7 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
16823
17649
  const file = turnMarker(input);
16824
17650
  if (!file)
16825
17651
  return;
16826
- pruneTurnDigests(path19.dirname(file), Date.now());
17652
+ pruneTurnDigests(path20.dirname(file), Date.now());
16827
17653
  if (text)
16828
17654
  writeTurnDigest(file, digestOf(text));
16829
17655
  else
@@ -16831,20 +17657,20 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
16831
17657
  };
16832
17658
  // packages/workit-core/src/core/route-intent.ts
16833
17659
  import { spawnSync as spawnSync12 } from "node:child_process";
16834
- import { existsSync as existsSync6 } from "node:fs";
16835
- import path22 from "node:path";
17660
+ import { existsSync as existsSync7 } from "node:fs";
17661
+ import path23 from "node:path";
16836
17662
 
16837
17663
  // packages/workit-core/src/hooks/raw-git.ts
16838
17664
  import { spawnSync as spawnSync11 } from "node:child_process";
16839
17665
  import { createHash as createHash15 } from "node:crypto";
16840
17666
  import fs17 from "node:fs";
16841
17667
  import os5 from "node:os";
16842
- import path21 from "node:path";
17668
+ import path22 from "node:path";
16843
17669
 
16844
17670
  // packages/workit-core/src/hooks/session-marker.ts
16845
17671
  import { createHash as createHash14 } from "node:crypto";
16846
17672
  import fs16 from "node:fs";
16847
- import path20 from "node:path";
17673
+ import path21 from "node:path";
16848
17674
  var sessionMarkerFile = (input, session, store) => {
16849
17675
  let dir = store;
16850
17676
  if (dir === undefined) {
@@ -16854,7 +17680,7 @@ var sessionMarkerFile = (input, session, store) => {
16854
17680
  dir = location.dir;
16855
17681
  }
16856
17682
  const name = createHash14("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
16857
- return path20.join(dir, "hooks", `skills-${name}.json`);
17683
+ return path21.join(dir, "hooks", `skills-${name}.json`);
16858
17684
  };
16859
17685
  var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
16860
17686
  var EMPTY = { nudged: [], loaded: [], raw: [], stops: [], judges: [] };
@@ -16881,7 +17707,7 @@ var parseMarker = (text) => {
16881
17707
  };
16882
17708
  var readSessionMarker = (file) => parseMarker(readRaw(file));
16883
17709
  var writeAtomic = (file, marker) => {
16884
- fs16.mkdirSync(path20.dirname(file), { recursive: true });
17710
+ fs16.mkdirSync(path21.dirname(file), { recursive: true });
16885
17711
  const temp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
16886
17712
  try {
16887
17713
  fs16.writeFileSync(temp, JSON.stringify(marker));
@@ -17409,17 +18235,17 @@ var GIT_VALUE_OPTIONS = new Set([
17409
18235
  ]);
17410
18236
  var GIT_CONFIG_OPTIONS = new Set(["-c", "--config-env", "--git-dir"]);
17411
18237
  var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
17412
- var ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
18238
+ var ASSIGNMENT2 = /^([A-Za-z_]\w*)=([\s\S]*)$/;
17413
18239
  var MAX_DEPTH = 4;
17414
18240
  var MAX_EXPANSIONS = 16;
17415
- var isAbsolute = (value) => path21.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
18241
+ var isAbsolute = (value) => path22.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
17416
18242
  var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
17417
18243
  var joinDir = (base, raw) => {
17418
18244
  const next = raw.replace(HERE, ".");
17419
18245
  if (base === undefined || /[$`*?]/.test(next) || next === "-")
17420
18246
  return;
17421
- const expanded = next === "~" || next.startsWith("~/") ? path21.join(os5.homedir(), next.slice(1)) : next;
17422
- return base === null || isAbsolute(expanded) ? expanded : path21.join(base, expanded);
18247
+ const expanded = next === "~" || next.startsWith("~/") ? path22.join(os5.homedir(), next.slice(1)) : next;
18248
+ return base === null || isAbsolute(expanded) ? expanded : path22.join(base, expanded);
17423
18249
  };
17424
18250
  var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
17425
18251
  var cdTarget = (words) => {
@@ -17436,7 +18262,7 @@ var stripPrefixes = (input) => {
17436
18262
  const first = words[0];
17437
18263
  if (first === undefined)
17438
18264
  return { words, env };
17439
- const assignment = ASSIGNMENT.exec(first);
18265
+ const assignment = ASSIGNMENT2.exec(first);
17440
18266
  if (assignment) {
17441
18267
  if (GIT_CONFIG_ENV.test(assignment[1]))
17442
18268
  env[assignment[1]] = assignment[2];
@@ -17495,10 +18321,10 @@ var assignments = (words, vars) => {
17495
18321
  while (rest[0]?.startsWith("-"))
17496
18322
  rest = rest.slice(1);
17497
18323
  }
17498
- if (rest.length === 0 || !rest.every((word) => ASSIGNMENT.test(word)))
18324
+ if (rest.length === 0 || !rest.every((word) => ASSIGNMENT2.test(word)))
17499
18325
  return null;
17500
18326
  return rest.map((word) => {
17501
- const [, name, value] = ASSIGNMENT.exec(word);
18327
+ const [, name, value] = ASSIGNMENT2.exec(word);
17502
18328
  return [name, expandWord(value, vars)];
17503
18329
  });
17504
18330
  };
@@ -17686,7 +18512,7 @@ var splitString = (line) => {
17686
18512
  return line.slice(index).join(" ").slice(15);
17687
18513
  if (/^-S./.test(word))
17688
18514
  return [word.slice(2), ...line.slice(index + 1)].join(" ");
17689
- if (!word.startsWith("-") && !ASSIGNMENT.test(word))
18515
+ if (!word.startsWith("-") && !ASSIGNMENT2.test(word))
17690
18516
  return null;
17691
18517
  }
17692
18518
  return null;
@@ -17953,6 +18779,24 @@ function workitInvocations(command, dialect = "posix", depth = 0) {
17953
18779
  }
17954
18780
  return found;
17955
18781
  }
18782
+ var WORKIT_GLOBAL = /^--(?:cwd|session|token|as)(=|$)/;
18783
+ function skipWorkitGlobals(args) {
18784
+ let cwd = null;
18785
+ let at = 0;
18786
+ for (;at < args.length; at++) {
18787
+ const match = WORKIT_GLOBAL.exec(args[at]);
18788
+ if (match?.[1] === "=") {
18789
+ if (args[at].startsWith("--cwd="))
18790
+ cwd = args[at].slice(6);
18791
+ } else if (match && args[at + 1] !== undefined) {
18792
+ if (args[at] === "--cwd")
18793
+ cwd = args[at + 1];
18794
+ at++;
18795
+ } else if (args[at] !== "--json")
18796
+ break;
18797
+ }
18798
+ return { rest: args.slice(at), cwd };
18799
+ }
17956
18800
  var PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
17957
18801
  var CURRENT_BRANCH = /^\$\(\s*git\s+(?:branch\s+--show-current|rev-parse\s+--abbrev-ref\s+HEAD|symbolic-ref\s+--short\s+HEAD)\s*\)$/;
17958
18802
  var pushDestination = (destination) => {
@@ -18252,17 +19096,17 @@ var headSha2 = (repo) => {
18252
19096
  if (!git || git.reftable)
18253
19097
  return null;
18254
19098
  try {
18255
- const head = fs17.readFileSync(path21.join(git.gitDir, "HEAD"), "utf8").trim();
19099
+ const head = fs17.readFileSync(path22.join(git.gitDir, "HEAD"), "utf8").trim();
18256
19100
  if (/^[0-9a-f]{40,64}$/.test(head))
18257
19101
  return head;
18258
19102
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
18259
19103
  if (!ref)
18260
19104
  return null;
18261
- const common = path21.dirname(repo.location.dir);
19105
+ const common = path22.dirname(repo.location.dir);
18262
19106
  try {
18263
- return fs17.readFileSync(path21.join(common, ref), "utf8").trim() || null;
19107
+ return fs17.readFileSync(path22.join(common, ref), "utf8").trim() || null;
18264
19108
  } catch {}
18265
- const packed = fs17.readFileSync(path21.join(common, "packed-refs"), "utf8");
19109
+ const packed = fs17.readFileSync(path22.join(common, "packed-refs"), "utf8");
18266
19110
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
18267
19111
  } catch {
18268
19112
  return null;
@@ -18350,7 +19194,7 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
18350
19194
  const sub = invocation.args[0];
18351
19195
  if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
18352
19196
  return [invocation];
18353
- const dir = path21.resolve(root, invocation.dir ?? ".");
19197
+ const dir = path22.resolve(root, invocation.dir ?? ".");
18354
19198
  if (!fs17.existsSync(dir))
18355
19199
  return [invocation];
18356
19200
  const config = gitConfig(dir, invocation, cache);
@@ -18373,11 +19217,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
18373
19217
  var normalizedUrl = (url, dir) => {
18374
19218
  const trimmed = url.trim();
18375
19219
  if (/^file:\/\//i.test(trimmed))
18376
- return path21.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
19220
+ return path22.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
18377
19221
  const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
18378
19222
  const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
18379
19223
  if (!scheme && !scp)
18380
- return path21.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
19224
+ return path22.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
18381
19225
  const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
18382
19226
  return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
18383
19227
  };
@@ -18397,7 +19241,7 @@ var rewrittenUrl = (url, config) => {
18397
19241
  }
18398
19242
  return url;
18399
19243
  };
18400
- var pushUrl = (config, remote, dir) => {
19244
+ var pushUrl2 = (config, remote, dir) => {
18401
19245
  const configured = configValue(config, `remote.${remote}.pushurl`) ?? configValue(config, `remote.${remote}.url`);
18402
19246
  return normalizedUrl(rewrittenUrl(configured ?? remote, config), dir);
18403
19247
  };
@@ -18425,7 +19269,7 @@ var toPushRemote = (repo, remote, config) => {
18425
19269
  const pushRemote = pushRemoteOf(config, repo.branch);
18426
19270
  if (pushRemote === null || pushRemote === remote)
18427
19271
  return true;
18428
- return pushUrl(config, remote, repo.dir) === pushUrl(config, pushRemote, repo.dir);
19272
+ return pushUrl2(config, remote, repo.dir) === pushUrl2(config, pushRemote, repo.dir);
18429
19273
  };
18430
19274
  var quote = (text) => `\`${text}\``;
18431
19275
  var refusal = (action, branch, dir) => {
@@ -18601,7 +19445,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
18601
19445
  const out = [];
18602
19446
  const repos = new Map;
18603
19447
  const repoFor = (relative) => {
18604
- const dir = path21.resolve(cwd, relative ?? ".");
19448
+ const dir = path22.resolve(cwd, relative ?? ".");
18605
19449
  if (!repos.has(dir)) {
18606
19450
  const repo = repoAt(dir);
18607
19451
  repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
@@ -18687,7 +19531,7 @@ var analysisDenial = (error) => ({
18687
19531
  });
18688
19532
  var NEXT_COMMAND = "next";
18689
19533
  var MARKER_TTL_SECONDS = 3600;
18690
- var markerFile = (repo, session, key) => path21.join(repo.location.dir, "hooks", `commit-${createHash15("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
19534
+ var markerFile = (repo, session, key) => path22.join(repo.location.dir, "hooks", `commit-${createHash15("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
18691
19535
  var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash15("sha256").update(command).digest("hex")}`;
18692
19536
  function noteRawCommit(input, command, key) {
18693
19537
  try {
@@ -18698,7 +19542,7 @@ function noteRawCommit(input, command, key) {
18698
19542
  if (action.kind !== "commit")
18699
19543
  continue;
18700
19544
  const file = markerFile(repo, input.session.id, key);
18701
- fs17.mkdirSync(path21.dirname(file), { recursive: true });
19545
+ fs17.mkdirSync(path22.dirname(file), { recursive: true });
18702
19546
  fs17.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
18703
19547
  `);
18704
19548
  }
@@ -18929,8 +19773,8 @@ var judgedDir = (root, dir) => {
18929
19773
  return null;
18930
19774
  if (dir === null)
18931
19775
  return root;
18932
- const resolved = path22.resolve(root, dir);
18933
- return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
19776
+ const resolved = path23.resolve(root, dir);
19777
+ return existsSync7(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
18934
19778
  };
18935
19779
  var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
18936
19780
  let judged = false;
@@ -18966,7 +19810,7 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
18966
19810
  // packages/workit-core/src/core/heavy-rules.ts
18967
19811
  import fs18 from "node:fs";
18968
19812
  import os6 from "node:os";
18969
- import path23 from "node:path";
19813
+ import path24 from "node:path";
18970
19814
  var EMPTY2 = () => ({ deny: [], allow: [] });
18971
19815
  var readText2 = (file) => {
18972
19816
  try {
@@ -19007,19 +19851,19 @@ function parseJsonc(text) {
19007
19851
  return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
19008
19852
  }
19009
19853
  function gitTop(cwd) {
19010
- for (let dir = path23.resolve(cwd);; dir = path23.dirname(dir)) {
19011
- if (fs18.existsSync(path23.join(dir, ".git")))
19854
+ for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
19855
+ if (fs18.existsSync(path24.join(dir, ".git")))
19012
19856
  return dir;
19013
- if (path23.dirname(dir) === dir)
19857
+ if (path24.dirname(dir) === dir)
19014
19858
  return null;
19015
19859
  }
19016
19860
  }
19017
19861
  var projectDirs = (cwd) => {
19018
19862
  const top = gitTop(cwd);
19019
19863
  const dirs = [];
19020
- for (let dir = path23.resolve(cwd);; dir = path23.dirname(dir)) {
19864
+ for (let dir = path24.resolve(cwd);; dir = path24.dirname(dir)) {
19021
19865
  dirs.push(dir);
19022
- if (!top || dir === top || path23.dirname(dir) === dir)
19866
+ if (!top || dir === top || path24.dirname(dir) === dir)
19023
19867
  return dirs;
19024
19868
  }
19025
19869
  };
@@ -19031,12 +19875,12 @@ var CLAUDE_MANAGED = {
19031
19875
  };
19032
19876
  function claudeSettingsFiles(cwd, env, platform = process.platform) {
19033
19877
  const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
19034
- const user = env.CLAUDE_CONFIG_DIR || path23.join(homeOf(env), ".claude");
19878
+ const user = env.CLAUDE_CONFIG_DIR || path24.join(homeOf(env), ".claude");
19035
19879
  return [
19036
19880
  ...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
19037
- path23.join(user, "settings.json"),
19038
- path23.join(project, ".claude", "settings.json"),
19039
- path23.join(project, ".claude", "settings.local.json")
19881
+ path24.join(user, "settings.json"),
19882
+ path24.join(project, ".claude", "settings.json"),
19883
+ path24.join(project, ".claude", "settings.local.json")
19040
19884
  ];
19041
19885
  }
19042
19886
  var bashPattern = (entry) => {
@@ -19126,10 +19970,10 @@ var opencodeConfig = (value, rules) => {
19126
19970
  function opencodeRules(cwd, env = process.env) {
19127
19971
  const rules = EMPTY2();
19128
19972
  try {
19129
- const config = env.XDG_CONFIG_HOME || path23.join(homeOf(env), ".config");
19973
+ const config = env.XDG_CONFIG_HOME || path24.join(homeOf(env), ".config");
19130
19974
  const files = [
19131
- ...OPENCODE_NAMES.map((name) => path23.join(config, "opencode", name)),
19132
- ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path23.join(dir, name), path23.join(dir, ".opencode", name)])),
19975
+ ...OPENCODE_NAMES.map((name) => path24.join(config, "opencode", name)),
19976
+ ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path24.join(dir, name), path24.join(dir, ".opencode", name)])),
19133
19977
  ...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
19134
19978
  ];
19135
19979
  for (const file of files) {
@@ -19173,9 +20017,9 @@ var codexRule = (body) => {
19173
20017
  function codexRules(cwd, env = process.env) {
19174
20018
  const rules = EMPTY2();
19175
20019
  try {
19176
- const home = env.CODEX_HOME || path23.join(homeOf(env), ".codex");
20020
+ const home = env.CODEX_HOME || path24.join(homeOf(env), ".codex");
19177
20021
  const top = gitTop(cwd) ?? cwd;
19178
- for (const dir of [path23.join(home, "rules"), path23.join(top, ".codex", "rules")]) {
20022
+ for (const dir of [path24.join(home, "rules"), path24.join(top, ".codex", "rules")]) {
19179
20023
  let files;
19180
20024
  try {
19181
20025
  files = fs18.readdirSync(dir).filter((name) => name.endsWith(".rules"));
@@ -19185,7 +20029,7 @@ function codexRules(cwd, env = process.env) {
19185
20029
  return "unreadable";
19186
20030
  }
19187
20031
  for (const name of files) {
19188
- const text = readText2(path23.join(dir, name)) ?? "";
20032
+ const text = readText2(path24.join(dir, name)) ?? "";
19189
20033
  for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
19190
20034
  const rule = codexRule(part);
19191
20035
  if (!rule)
@@ -19204,8 +20048,8 @@ function cursorRules(cwd, env = process.env) {
19204
20048
  try {
19205
20049
  const top = gitTop(cwd) ?? cwd;
19206
20050
  for (const file of [
19207
- path23.join(homeOf(env), ".cursor", "cli-config.json"),
19208
- path23.join(top, ".cursor", "cli.json")
20051
+ path24.join(homeOf(env), ".cursor", "cli-config.json"),
20052
+ path24.join(top, ".cursor", "cli.json")
19209
20053
  ]) {
19210
20054
  const text = readText2(file);
19211
20055
  if (text === null)
@@ -19281,21 +20125,21 @@ function wrapAllowed(read, words, command, options = {}) {
19281
20125
  // packages/workit-core/src/core/heavy.ts
19282
20126
  import fs19 from "node:fs";
19283
20127
  import os7 from "node:os";
19284
- import path24 from "node:path";
20128
+ import path25 from "node:path";
19285
20129
  function stateDir(env, platform = process.platform) {
19286
20130
  if (env.WORKFLOW_TOOLKIT_STATE)
19287
20131
  return env.WORKFLOW_TOOLKIT_STATE;
19288
20132
  if (env.XDG_STATE_HOME)
19289
- return path24.join(env.XDG_STATE_HOME, "workit");
20133
+ return path25.join(env.XDG_STATE_HOME, "workit");
19290
20134
  const home = env.HOME || os7.homedir();
19291
20135
  if (platform === "darwin")
19292
- return path24.join(home, "Library", "Application Support", "workit");
20136
+ return path25.join(home, "Library", "Application Support", "workit");
19293
20137
  if (platform === "win32")
19294
- return path24.join(env.LOCALAPPDATA ?? path24.join(home, "AppData", "Local"), "workit");
19295
- return path24.join(home, ".local", "state", "workit");
20138
+ return path25.join(env.LOCALAPPDATA ?? path25.join(home, "AppData", "Local"), "workit");
20139
+ return path25.join(home, ".local", "state", "workit");
19296
20140
  }
19297
20141
  var HEAVY_COMMANDS_FILE = "heavy-commands.json";
19298
- var heavyCommandsFile = (env) => path24.join(stateDir(env), HEAVY_COMMANDS_FILE);
20142
+ var heavyCommandsFile = (env) => path25.join(stateDir(env), HEAVY_COMMANDS_FILE);
19299
20143
  var HEAVY_SCRIPT = /^(?:test|tests|t|tst|unit|integration|e2e|coverage|cov|lint|eslint|typecheck|type-check|types|tsc|check|verify|validate|build|compile|bundle|ci|sonar|cypress|playwright|jest|vitest|mocha|bench|benchmark|all)(?:$|[:_.-])/iu;
19300
20144
  var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
19301
20145
  var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
@@ -19570,7 +20414,7 @@ function machinePatterns(env) {
19570
20414
  return isRecord5(value) ? stringList(value.patterns) : [];
19571
20415
  }
19572
20416
  function repoPatterns(cwd) {
19573
- return repoHeavyCommands(gitTop(cwd) ?? path24.resolve(cwd));
20417
+ return repoHeavyCommands(gitTop(cwd) ?? path25.resolve(cwd));
19574
20418
  }
19575
20419
  function heavyLines(lines, options) {
19576
20420
  const found = [];
@@ -19648,12 +20492,12 @@ function heavyPlan(command, options) {
19648
20492
  }
19649
20493
  function workitOnPath2(env = process.env) {
19650
20494
  const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
19651
- for (const dir of (env.PATH ?? "").split(path24.delimiter))
20495
+ for (const dir of (env.PATH ?? "").split(path25.delimiter))
19652
20496
  for (const name of names) {
19653
20497
  if (!dir)
19654
20498
  continue;
19655
20499
  try {
19656
- fs19.accessSync(path24.join(dir, name), fs19.constants.X_OK);
20500
+ fs19.accessSync(path25.join(dir, name), fs19.constants.X_OK);
19657
20501
  return true;
19658
20502
  } catch {}
19659
20503
  }
@@ -19672,20 +20516,20 @@ function leaseRewrite(command) {
19672
20516
  // packages/workit-core/src/core/heavy-words.ts
19673
20517
  import fs20 from "node:fs";
19674
20518
  import os8 from "node:os";
19675
- import path25 from "node:path";
20519
+ import path26 from "node:path";
19676
20520
  var HEAVY_WORDS = new Set(("npm npx pnpm pnpx yarn bun bunx deno corepack make gmake docker podman nerdctl " + "python python3 py uv poetry pdm pipenv hatch rye bundle rake nx " + "jest vitest mocha ava tap karma jasmine nyc c8 tsc vue-tsc svelte-check eslint stylelint " + "tslint webpack webpack-cli rollup parcel tsup vite next nuxt nuxi astro ng react-scripts " + "playwright cypress wdio testcafe turbo lerna sonar-scanner sonarless sonarless-mcp go " + "golangci-lint gotestsum staticcheck cargo mvn mvnw gradle gradlew ant sbt bazel bazelisk " + "buck2 dotnet msbuild pytest py.test tox nox mypy pyright pylint coverage maturin pip pip3 " + "rspec rails phpunit pest phpstan psalm composer mix swift xcodebuild flutter dart pod cmake " + "ninja meson ctest scons docker-compose buildah earthly").split(" "));
19677
20521
  var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
19678
20522
  var stateDir2 = (env) => {
19679
20523
  if (env.WORKFLOW_TOOLKIT_STATE)
19680
20524
  return env.WORKFLOW_TOOLKIT_STATE;
19681
20525
  if (env.XDG_STATE_HOME)
19682
- return path25.join(env.XDG_STATE_HOME, "workit");
20526
+ return path26.join(env.XDG_STATE_HOME, "workit");
19683
20527
  const home = env.HOME || os8.homedir();
19684
20528
  if (process.platform === "darwin")
19685
- return path25.join(home, "Library", "Application Support", "workit");
20529
+ return path26.join(home, "Library", "Application Support", "workit");
19686
20530
  if (process.platform === "win32")
19687
- return path25.join(env.LOCALAPPDATA ?? path25.join(home, "AppData", "Local"), "workit");
19688
- return path25.join(home, ".local", "state", "workit");
20531
+ return path26.join(env.LOCALAPPDATA ?? path26.join(home, "AppData", "Local"), "workit");
20532
+ return path26.join(home, ".local", "state", "workit");
19689
20533
  };
19690
20534
  var readJson2 = (file) => {
19691
20535
  try {
@@ -19696,15 +20540,15 @@ var readJson2 = (file) => {
19696
20540
  };
19697
20541
  var patternWords = (cwd, env) => {
19698
20542
  const patterns = [];
19699
- const machine = readJson2(path25.join(stateDir2(env), "heavy-commands.json"));
20543
+ const machine = readJson2(path26.join(stateDir2(env), "heavy-commands.json"));
19700
20544
  if (Array.isArray(machine?.patterns))
19701
20545
  patterns.push(...machine.patterns);
19702
- for (let dir = path25.resolve(cwd);; dir = path25.dirname(dir)) {
19703
- if (fs20.existsSync(path25.join(dir, ".git"))) {
20546
+ for (let dir = path26.resolve(cwd);; dir = path26.dirname(dir)) {
20547
+ if (fs20.existsSync(path26.join(dir, ".git"))) {
19704
20548
  patterns.push(...repoHeavyCommands(dir));
19705
20549
  break;
19706
20550
  }
19707
- if (path25.dirname(dir) === dir)
20551
+ if (path26.dirname(dir) === dir)
19708
20552
  break;
19709
20553
  }
19710
20554
  const words = new Set;
@@ -19774,16 +20618,16 @@ var splitDenial = (plan) => ({
19774
20618
  unblock: plan.unblock
19775
20619
  });
19776
20620
  // packages/workit-core/src/hooks/write-gate.ts
19777
- import { realpathSync as realpathSync5 } from "node:fs";
20621
+ import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
19778
20622
  import os10 from "node:os";
19779
- import path27 from "node:path";
20623
+ import path28 from "node:path";
19780
20624
 
19781
20625
  // packages/workit-core/src/core/live-session.ts
19782
20626
  import { spawnSync as spawnSync13 } from "node:child_process";
19783
20627
  import { createHash as createHash16 } from "node:crypto";
19784
20628
  import fs21 from "node:fs";
19785
20629
  import os9 from "node:os";
19786
- import path26 from "node:path";
20630
+ import path27 from "node:path";
19787
20631
  var LIVE_SESSION_TTL_MS = 15 * 60000;
19788
20632
  var HEARTBEAT_EVERY_MS = 30000;
19789
20633
  var liveProbe = {
@@ -19857,15 +20701,15 @@ var liveOwner = (input, transport, env = process.env) => {
19857
20701
  };
19858
20702
  };
19859
20703
  var checkoutOf = (target) => {
19860
- let dir = path26.resolve(target);
19861
- while (!fs21.existsSync(dir) && path26.dirname(dir) !== dir)
19862
- dir = path26.dirname(dir);
20704
+ let dir = path27.resolve(target);
20705
+ while (!fs21.existsSync(dir) && path27.dirname(dir) !== dir)
20706
+ dir = path27.dirname(dir);
19863
20707
  if (fs21.existsSync(dir) && !fs21.statSync(dir).isDirectory())
19864
- dir = path26.dirname(dir);
19865
- const parts = dir.split(path26.sep);
20708
+ dir = path27.dirname(dir);
20709
+ const parts = dir.split(path27.sep);
19866
20710
  const dotGit = parts.lastIndexOf(".git");
19867
20711
  if (dotGit > 0)
19868
- dir = parts.slice(0, dotGit).join(path26.sep) || path26.sep;
20712
+ dir = parts.slice(0, dotGit).join(path27.sep) || path27.sep;
19869
20713
  const location = resolveStore(dir);
19870
20714
  if (location instanceof Error || !location.shared || !location.top)
19871
20715
  return null;
@@ -19875,9 +20719,9 @@ var checkoutOf = (target) => {
19875
20719
  } catch {}
19876
20720
  return top;
19877
20721
  };
19878
- var markerFile2 = (top) => path26.join(resolveStateDir(), "live-sessions", `${createHash16("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
20722
+ var markerFile2 = (top) => path27.join(resolveStateDir(), "live-sessions", `${createHash16("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
19879
20723
  var checkoutKey = (top, platform = process.platform) => {
19880
- let key = path26.resolve(top);
20724
+ let key = path27.resolve(top);
19881
20725
  try {
19882
20726
  key = fs21.realpathSync.native(key);
19883
20727
  } catch {}
@@ -19972,7 +20816,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
19972
20816
  checkout,
19973
20817
  heartbeatAt: now.toISOString()
19974
20818
  };
19975
- fs21.mkdirSync(path26.dirname(file), { recursive: true });
20819
+ fs21.mkdirSync(path27.dirname(file), { recursive: true });
19976
20820
  const temp = `${file}.${process.pid}.tmp`;
19977
20821
  fs21.writeFileSync(temp, JSON.stringify(marker));
19978
20822
  fs21.renameSync(temp, file);
@@ -19984,26 +20828,26 @@ var NONE3 = { kind: "none" };
19984
20828
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
19985
20829
  var canonical2 = (file) => {
19986
20830
  let rest = "";
19987
- for (let dir = path27.resolve(file);; dir = path27.dirname(dir)) {
20831
+ for (let dir = path28.resolve(file);; dir = path28.dirname(dir)) {
19988
20832
  try {
19989
- return path27.join(realpathSync5.native(dir), rest);
20833
+ return path28.join(realpathSync5.native(dir), rest);
19990
20834
  } catch {
19991
- if (path27.dirname(dir) === dir)
19992
- return path27.resolve(file);
19993
- rest = rest ? path27.join(path27.basename(dir), rest) : path27.basename(dir);
20835
+ if (path28.dirname(dir) === dir)
20836
+ return path28.resolve(file);
20837
+ rest = rest ? path28.join(path28.basename(dir), rest) : path28.basename(dir);
19994
20838
  }
19995
20839
  }
19996
20840
  };
19997
20841
  var exempt = (cwd, root, file, plans) => {
19998
20842
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
19999
- const relative = path27.relative(fold(canonical2(root)), fold(canonical2(path27.resolve(cwd, file))));
20000
- if (relative.startsWith("..") || path27.isAbsolute(relative))
20843
+ const relative = path28.relative(fold(canonical2(root)), fold(canonical2(path28.resolve(cwd, file))));
20844
+ if (relative.startsWith("..") || path28.isAbsolute(relative))
20001
20845
  return true;
20002
- const posix = relative.split(path27.sep).join("/");
20846
+ const posix = relative.split(path28.sep).join("/");
20003
20847
  const segments = posix.split("/");
20004
20848
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
20005
20849
  };
20006
- var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
20850
+ var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs", "busybox"]);
20007
20851
  var operands = (words) => words.filter((item) => !item.startsWith("-"));
20008
20852
  var SCRIPT_SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
20009
20853
  var wrappedScript = (input) => {
@@ -20015,9 +20859,9 @@ var wrappedScript = (input) => {
20015
20859
  return null;
20016
20860
  if ("script" in tail)
20017
20861
  return tail.script;
20018
- const [shell, flag, script] = tail.argv;
20019
- if (SCRIPT_SHELLS.has((shell.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && /^-[a-z]*c$/.test(flag ?? ""))
20020
- return script ?? null;
20862
+ const script = shellScript2(tail.argv);
20863
+ if (SCRIPT_SHELLS.has((tail.argv[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && script)
20864
+ return script;
20021
20865
  return tail.argv.map(quoteWord2).join(" ");
20022
20866
  };
20023
20867
  var editsOf = (input) => {
@@ -20096,6 +20940,94 @@ var editsOf = (input) => {
20096
20940
  return [];
20097
20941
  }
20098
20942
  };
20943
+ var shellScript2 = (words) => {
20944
+ if (!SCRIPT_SHELLS.has((words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase()))
20945
+ return null;
20946
+ for (let index = 1;index < words.length; index++) {
20947
+ const word = words[index];
20948
+ if (/^-[a-z]*c$/.test(word))
20949
+ return words[index + 1] ?? null;
20950
+ if (/^[-+][oO]$/.test(word))
20951
+ index++;
20952
+ else if (!/^[-+]/.test(word))
20953
+ return null;
20954
+ }
20955
+ return null;
20956
+ };
20957
+ var XARGS_VALUE = /^-[IinLPdsEa]$/;
20958
+ var runsWriter = (words) => {
20959
+ const filled = [...words.filter((word) => word !== "{}" && word !== ";" && word !== "+"), "x"];
20960
+ const edits = editsOf(filled);
20961
+ return edits === null || edits.length > 0 || opaqueWrites(filled) !== null;
20962
+ };
20963
+ var opaqueWrites = (input) => {
20964
+ let words = input;
20965
+ while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0]))) {
20966
+ if (words[0] === "xargs") {
20967
+ let at = 1;
20968
+ while (words[at]?.startsWith("-"))
20969
+ at += XARGS_VALUE.test(words[at]) ? 2 : 1;
20970
+ return runsWriter(words.slice(at)) ? ["."] : null;
20971
+ }
20972
+ words = words.slice(1);
20973
+ }
20974
+ const [verb, ...args] = words;
20975
+ const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
20976
+ const files = operands(args);
20977
+ const has = (pattern) => args.some((arg) => pattern.test(arg));
20978
+ const listed = (named) => named.length ? named : ["."];
20979
+ if (name === "unlink" || name === "shred")
20980
+ return listed(files);
20981
+ if (/^(?:chmod|chown|chgrp|chattr)$/.test(name))
20982
+ return listed(has(/^--reference/) ? files : files.slice(1));
20983
+ if (name === "rsync") {
20984
+ const dest = files.at(-1);
20985
+ const remote = /^[\w.@-]*:/.test(dest ?? "") && !/^[A-Za-z]:[\\/]/.test(dest ?? "");
20986
+ return dest === undefined ? ["."] : remote ? [] : [dest];
20987
+ }
20988
+ if (name === "find") {
20989
+ const exec = args.findIndex((arg) => /^-(?:exec|execdir|ok|okdir)$/.test(arg));
20990
+ const writes = has(/^-(?:delete|fprint\w*|fls)$/) || exec >= 0 && runsWriter(args.slice(exec + 1));
20991
+ const end = args.findIndex((arg) => /^[-(!]/.test(arg));
20992
+ return writes ? listed(end < 0 ? args : args.slice(0, end)) : null;
20993
+ }
20994
+ if (/^g?awk$/.test(name)) {
20995
+ if (!has(/^-i|^--include/))
20996
+ return null;
20997
+ const named = [];
20998
+ let program = !has(/^-f$|^--file/);
20999
+ for (let index = 0;index < args.length; index++)
21000
+ if (/^(?:-[ifvF]|--(?:include|file|assign|field-separator))$/.test(args[index]))
21001
+ index++;
21002
+ else if (args[index].startsWith("-"))
21003
+ continue;
21004
+ else if (program)
21005
+ program = false;
21006
+ else
21007
+ named.push(args[index]);
21008
+ return listed(named);
21009
+ }
21010
+ if (name === "perl")
21011
+ return has(/^-[a-zA-Z]*[eE]/) ? ["."] : null;
21012
+ if (/^(?:python[\d.]*|node|bun|deno|ruby|php)$/.test(name))
21013
+ return has(/^-[a-zA-Z]*[ce]$|^--eval$|^-p$/) ? ["."] : null;
21014
+ return shellScript2(words) === null ? null : ["."];
21015
+ };
21016
+ var linkSources = (args, at) => {
21017
+ const names = operands([...args]);
21018
+ const into = args.indexOf("-t");
21019
+ const dest = into >= 0 ? args[into + 1] : names.length > 1 ? names.at(-1) : ".";
21020
+ const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
21021
+ const target = dest === undefined ? null : at(dest);
21022
+ const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
21023
+ const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync8(target) || /[\\/]$/.test(dest ?? "") ? target : path28.dirname(target);
21024
+ return sources.flatMap((source) => {
21025
+ if (symbolic && base !== null && !path28.isAbsolute(homeExpanded(source)))
21026
+ return [path28.resolve(base, source)];
21027
+ const resolved = at(source);
21028
+ return resolved ? [resolved] : [];
21029
+ });
21030
+ };
20099
21031
  var shellWrites = (command, depth = 0) => {
20100
21032
  const targets = [];
20101
21033
  let unknown = false;
@@ -20120,7 +21052,7 @@ var shellWrites = (command, depth = 0) => {
20120
21052
  return { writes: true, targets: unknown ? null : targets };
20121
21053
  };
20122
21054
  var cache2 = new Map;
20123
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path27.resolve(root, file)) ?? "-").join("\x00");
21055
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path28.resolve(root, file)) ?? "-").join("\x00");
20124
21056
  var gateFor = (cwd) => {
20125
21057
  const store = new TaskStore(cwd);
20126
21058
  const listed = store.listTaskIndex();
@@ -20199,32 +21131,36 @@ var gitMutationDirs = (cwd, command, dialect) => {
20199
21131
  return [];
20200
21132
  if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
20201
21133
  return [];
20202
- return [path27.resolve(cwd, invocation.dir ?? ".")];
21134
+ return [path28.resolve(cwd, invocation.dir ?? ".")];
20203
21135
  });
20204
21136
  };
20205
21137
  var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
20206
21138
  var PUSHD2 = new Set(["pushd", "push-location"]);
20207
21139
  var POPD2 = new Set(["popd", "pop-location"]);
20208
- var cdInto = (dir, words) => {
21140
+ var UNKNOWN_DIR = path28.resolve(path28.parse(process.cwd()).root, ".workit-unknown-dir");
21141
+ var cdInto = (dir, words, strict = false) => {
21142
+ const lost = strict ? UNKNOWN_DIR : undefined;
20209
21143
  const named = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
20210
21144
  const target = named === undefined ? undefined : homeExpanded(named);
20211
- if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
20212
- return;
20213
- return target === undefined ? os10.homedir() : path27.resolve(dir, target);
21145
+ if (target === "-" || target && /^~|[$`*?]/.test(target))
21146
+ return lost;
21147
+ if (dir === undefined)
21148
+ return lost;
21149
+ return target === undefined ? os10.homedir() : path28.resolve(dir, target);
20214
21150
  };
20215
21151
  var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
20216
21152
  var homeExpanded = (file) => {
20217
21153
  const match = /^(?:~|\$HOME|\$\{HOME\})(?=\/|$)/.exec(file);
20218
21154
  return match ? (process.env.HOME || os10.homedir()) + file.slice(match[0].length) : file;
20219
21155
  };
20220
- var segmentTargets = (cwd, command, dialect, depth = 0) => {
21156
+ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
20221
21157
  const targets = [];
20222
21158
  let dir = cwd;
20223
21159
  let stack = [];
20224
21160
  const groups = [];
20225
21161
  const at = (file) => {
20226
21162
  const home = homeExpanded(file);
20227
- return path27.isAbsolute(home) ? home : dir === undefined ? null : path27.resolve(dir, home);
21163
+ return path28.isAbsolute(home) ? home : dir === undefined ? null : path28.resolve(dir, home);
20228
21164
  };
20229
21165
  for (const segment of segmentsOf(command, dialect)) {
20230
21166
  if (segment.group === "open") {
@@ -20241,21 +21177,35 @@ var segmentTargets = (cwd, command, dialect, depth = 0) => {
20241
21177
  const words = lead < 0 ? [] : segment.words.slice(lead);
20242
21178
  const name = (words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
20243
21179
  if (CD2.has(name)) {
20244
- dir = cdInto(dir, words);
21180
+ dir = cdInto(dir, words, strict);
20245
21181
  continue;
20246
21182
  }
20247
21183
  if (PUSHD2.has(name)) {
20248
21184
  stack.push(dir);
20249
- dir = cdInto(dir, words);
21185
+ dir = cdInto(dir, words, strict);
20250
21186
  continue;
20251
21187
  }
20252
21188
  if (POPD2.has(name)) {
20253
- dir = stack.length ? stack.pop() : undefined;
21189
+ dir = stack.length ? stack.pop() : strict ? UNKNOWN_DIR : undefined;
20254
21190
  continue;
20255
21191
  }
20256
- const inner = depth < 4 ? wrappedScript(words) : null;
21192
+ const inner = depth < 4 ? wrappedScript(words) ?? (strict ? shellScript2(words) : null) : null;
20257
21193
  if (inner !== null && dir !== undefined)
20258
- targets.push(...segmentTargets(dir, inner, "posix", depth + 1));
21194
+ targets.push(...segmentTargets(dir, inner, "posix", depth + 1, strict));
21195
+ if (strict && dir !== undefined) {
21196
+ const runIn = dir;
21197
+ for (const sub of depth < 4 ? segment.subs ?? [] : [])
21198
+ targets.push(...segmentTargets(runIn, sub, dialect, depth + 1, true));
21199
+ const opaque = segment.unanalyzable ? ["."] : inner === null ? opaqueWrites(words) : null;
21200
+ for (const file of opaque ?? []) {
21201
+ const resolved = at(file);
21202
+ if (resolved)
21203
+ targets.push(file === "." ? path28.join(resolved, DIR_TARGET) : resolved);
21204
+ }
21205
+ const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
21206
+ if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
21207
+ targets.push(...linkSources(words.slice(ln + 1), at));
21208
+ }
20259
21209
  for (const file of segment.redirects)
20260
21210
  if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
20261
21211
  const resolved = at(file);
@@ -20265,18 +21215,36 @@ var segmentTargets = (cwd, command, dialect, depth = 0) => {
20265
21215
  const edits = editsOf(segment.words);
20266
21216
  if (edits === null) {
20267
21217
  if (dir !== undefined)
20268
- targets.push(path27.join(dir, DIR_TARGET));
21218
+ targets.push(path28.join(dir, DIR_TARGET));
20269
21219
  } else
20270
21220
  for (const file of edits) {
20271
21221
  const resolved = at(file);
20272
21222
  if (resolved)
20273
21223
  targets.push(resolved);
20274
21224
  }
20275
- 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), "")))
20276
- targets.push(path27.join(dir, DIR_TARGET));
21225
+ if (dir !== undefined)
21226
+ for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
21227
+ const { at: runsIn, args } = workitCwd(dir, invocation);
21228
+ if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
21229
+ targets.push(path28.join(runsIn, DIR_TARGET));
21230
+ }
20277
21231
  }
20278
21232
  return targets;
20279
21233
  };
21234
+ var workitCwd = (dir, args) => {
21235
+ const { rest, cwd } = skipWorkitGlobals(args);
21236
+ return { at: cwd === null ? dir : path28.resolve(dir, homeExpanded(cwd)), args: rest };
21237
+ };
21238
+ var shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
21239
+ try {
21240
+ return [
21241
+ ...segmentTargets(cwd, command, dialect, 0, strict),
21242
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path28.join(dir, DIR_TARGET))
21243
+ ];
21244
+ } catch {
21245
+ return [];
21246
+ }
21247
+ };
20280
21248
  var overridden = (command, dialect) => {
20281
21249
  const segments = segmentsOf(command, dialect);
20282
21250
  if (segments.length !== 1 || segments[0].group || segments[0].subs?.length)
@@ -20308,7 +21276,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
20308
21276
  var guarded = (targets) => {
20309
21277
  const seen = new Set;
20310
21278
  return targets.filter((target) => {
20311
- const dir = path27.dirname(target);
21279
+ const dir = path28.dirname(target);
20312
21280
  if (seen.has(dir))
20313
21281
  return false;
20314
21282
  seen.add(dir);
@@ -20329,7 +21297,7 @@ var claimAll = (owner, targets, deps) => {
20329
21297
  for (const target of guarded(targets))
20330
21298
  claimLiveSession(target, owner, deps);
20331
21299
  };
20332
- var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path27.resolve(cwd, file)) : [path27.join(cwd, DIR_TARGET)];
21300
+ var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path28.resolve(cwd, file)) : [path28.join(cwd, DIR_TARGET)];
20333
21301
  var issuedTarget = (cwd, targets) => {
20334
21302
  const state = resolveStateDir();
20335
21303
  const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
@@ -20337,7 +21305,7 @@ var issuedTarget = (cwd, targets) => {
20337
21305
  };
20338
21306
  var underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
20339
21307
  const win = platform === "win32";
20340
- const p = win ? path27.win32 : path27.posix;
21308
+ const p = win ? path28.win32 : path28.posix;
20341
21309
  const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
20342
21310
  const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
20343
21311
  const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
@@ -20382,16 +21350,8 @@ function shellGate(cwd, command, owner, dialect = "posix", deps) {
20382
21350
  } catch {}
20383
21351
  const writes = shellWrites(command);
20384
21352
  let targets = [];
20385
- if (owner) {
20386
- try {
20387
- targets = [
20388
- ...segmentTargets(cwd, command, dialect),
20389
- ...gitMutationDirs(cwd, command, dialect).map((dir) => path27.join(dir, DIR_TARGET))
20390
- ];
20391
- } catch {
20392
- targets = [];
20393
- }
20394
- }
21353
+ if (owner)
21354
+ targets = shellMutationTargets(cwd, command, dialect);
20395
21355
  if (owner && targets.length) {
20396
21356
  const live = liveGate(owner, targets, deps);
20397
21357
  if (live.kind !== "none") {
@@ -20436,6 +21396,24 @@ function beforeWriteGate(cwd, paths) {
20436
21396
  }
20437
21397
  // packages/workit-core/src/hooks/stop.ts
20438
21398
  import { spawnSync as spawnSync14 } from "node:child_process";
21399
+
21400
+ // packages/workit-core/src/core/policy/current-judgment.ts
21401
+ function currentJudgment(cwd) {
21402
+ try {
21403
+ const store = new TaskStore(cwd);
21404
+ const listed = store.listTaskIndex();
21405
+ const key = store.currentKey();
21406
+ if (!listed.ok || !key.ok)
21407
+ return null;
21408
+ const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
21409
+ const task = entry ? store.readTask(entry.id) : null;
21410
+ return task?.ok === true ? latestJudgment(task.data) : null;
21411
+ } catch {
21412
+ return null;
21413
+ }
21414
+ }
21415
+
21416
+ // packages/workit-core/src/hooks/stop.ts
20439
21417
  var NONE4 = { kind: "none" };
20440
21418
  var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
20441
21419
  var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
@@ -20528,29 +21506,32 @@ var redChecks = (rows, pr, head, endpoint) => {
20528
21506
  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)" : ""}.`
20529
21507
  };
20530
21508
  };
20531
- var taskJudgment = (cwd) => {
20532
- try {
20533
- const store = new TaskStore(cwd);
20534
- const listed = store.listTaskIndex();
20535
- const key = store.currentKey();
20536
- if (!listed.ok || !key.ok)
20537
- return null;
20538
- const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
20539
- const task = entry ? store.readTask(entry.id) : null;
20540
- return task?.ok === true ? latestJudgment(task.data) : null;
20541
- } catch {
20542
- return null;
20543
- }
21509
+ var prBase = (rows) => {
21510
+ const seen = rows.findLast((row) => (row.type === "pr.status" || row.type === "pr.created") && row.observer === "workit_cli" && typeof row.base === "string");
21511
+ return typeof seen?.base === "string" ? seen.base : null;
20544
21512
  };
20545
- var unverified = (cwd, branch, all, descriptor, verification) => {
21513
+ var unverified = (cwd, branch, all, descriptor, verification, head) => {
20546
21514
  const check = checkVerdicts(cwd, branch, all);
20547
21515
  if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
20548
21516
  return null;
20549
- const judgment = taskJudgment(cwd);
21517
+ const judgment = currentJudgment(cwd);
20550
21518
  const high = judgment?.riskTier === "high";
20551
- if (judgment && !judgment.behaviorChange && !high)
20552
- return null;
20553
- const independent = verification === "independent" || high;
21519
+ let independent;
21520
+ if (verification === "proportional") {
21521
+ const lane = branchLane(cwd, {
21522
+ base: prBase(all.filter((row) => row.branch === branch)),
21523
+ head,
21524
+ judgment
21525
+ });
21526
+ const need = laneNeed(lane.lane);
21527
+ if (need === "none")
21528
+ return null;
21529
+ independent = need === "non-author";
21530
+ } else {
21531
+ if (judgment && !judgment.behaviorChange && !high)
21532
+ return null;
21533
+ independent = verification === "independent" || high;
21534
+ }
20554
21535
  if (!independent && check.review === "self-reviewed")
20555
21536
  return null;
20556
21537
  const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
@@ -20597,7 +21578,7 @@ function stopObligation(input, descriptor) {
20597
21578
  }
20598
21579
  if (!delivered || endpoint !== "green" && endpoint !== "merged")
20599
21580
  return null;
20600
- const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
21581
+ const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
20601
21582
  return left ? { ...left, head } : null;
20602
21583
  } catch {
20603
21584
  return null;
@@ -20858,13 +21839,13 @@ ${line}` };
20858
21839
  return decision;
20859
21840
  };
20860
21841
  // packages/workit-core/src/hooks/hosts/fields.ts
20861
- import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
20862
- import path28 from "node:path";
21842
+ import { existsSync as existsSync9, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
21843
+ import path29 from "node:path";
20863
21844
  var isRecord6 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
20864
21845
  var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
20865
21846
  var optionalText = (value) => typeof value === "string" ? value : null;
20866
21847
  var existingDirectory = (value) => {
20867
- if (!nonEmpty2(value) || !path28.isAbsolute(value) || !existsSync7(value))
21848
+ if (!nonEmpty2(value) || !path29.isAbsolute(value) || !existsSync9(value))
20868
21849
  return null;
20869
21850
  try {
20870
21851
  return statSync2(value).isDirectory() ? realpathSync6(value) : null;
@@ -20932,9 +21913,9 @@ var writeTargets = (input) => {
20932
21913
  };
20933
21914
  // packages/workit-core/src/hooks/standing.ts
20934
21915
  import fs22 from "node:fs";
20935
- import path29 from "node:path";
21916
+ import path30 from "node:path";
20936
21917
  var readPlans = (store) => {
20937
- const dir = path29.join(store, "fanouts");
21918
+ const dir = path30.join(store, "fanouts");
20938
21919
  let names;
20939
21920
  try {
20940
21921
  names = fs22.readdirSync(dir).filter((name) => name.endsWith(".json"));
@@ -20944,7 +21925,7 @@ var readPlans = (store) => {
20944
21925
  const plans = [];
20945
21926
  for (const name of names) {
20946
21927
  try {
20947
- const raw = JSON.parse(fs22.readFileSync(path29.join(dir, name), "utf8"));
21928
+ const raw = JSON.parse(fs22.readFileSync(path30.join(dir, name), "utf8"));
20948
21929
  if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
20949
21930
  continue;
20950
21931
  const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
@@ -21067,9 +22048,266 @@ var cwdRewrite = (command) => {
21067
22048
  return `workit --cwd ${dir} ${rest.slice("workit".length).trimStart()}`.trimEnd();
21068
22049
  };
21069
22050
 
22051
+ // packages/workit-core/src/hooks/worktree-confine.ts
22052
+ import { createHash as createHash17 } from "node:crypto";
22053
+ import fs23 from "node:fs";
22054
+ import path31 from "node:path";
22055
+ var FANOUT_TOKEN_FILE = "workit-fanout-token";
22056
+ var NONE5 = { kind: "none" };
22057
+ var CASE_INSENSITIVE2 = process.platform === "darwin" || process.platform === "win32";
22058
+ var canonical3 = (file) => {
22059
+ let rest = "";
22060
+ for (let dir = path31.resolve(file);; dir = path31.dirname(dir)) {
22061
+ try {
22062
+ return path31.join(fs23.realpathSync.native(dir), rest);
22063
+ } catch {
22064
+ if (path31.dirname(dir) === dir)
22065
+ return path31.resolve(file);
22066
+ rest = rest ? path31.join(path31.basename(dir), rest) : path31.basename(dir);
22067
+ }
22068
+ }
22069
+ };
22070
+ var within = (child, parent) => {
22071
+ const fold = (value) => CASE_INSENSITIVE2 ? value.toLowerCase() : value;
22072
+ const relative = path31.relative(fold(parent), fold(child));
22073
+ return relative === "" || !relative.startsWith("..") && !path31.isAbsolute(relative);
22074
+ };
22075
+ var checkoutOf2 = (file) => {
22076
+ for (let dir = canonical3(file);; dir = path31.dirname(dir)) {
22077
+ const dotGit = path31.join(dir, ".git");
22078
+ if (fs23.existsSync(dotGit)) {
22079
+ if (!fs23.statSync(dotGit).isFile())
22080
+ return { root: dir, workit: false };
22081
+ const admin = /^gitdir:\s*(.+)$/mu.exec(fs23.readFileSync(dotGit, "utf8"))?.[1]?.trim();
22082
+ return {
22083
+ root: dir,
22084
+ workit: Boolean(admin) && fs23.existsSync(path31.join(path31.resolve(dir, admin), FANOUT_TOKEN_FILE))
22085
+ };
22086
+ }
22087
+ if (path31.dirname(dir) === dir)
22088
+ return null;
22089
+ }
22090
+ };
22091
+ var workitWorktreeOf = (file) => {
22092
+ try {
22093
+ const checkout = checkoutOf2(file);
22094
+ return checkout?.workit ? checkout.root : null;
22095
+ } catch {
22096
+ return null;
22097
+ }
22098
+ };
22099
+ var bindingDir = () => path31.join(resolveStateDir(), "worktree-bind");
22100
+ var bindingFile = (session, agentId) => path31.join(bindingDir(), `${createHash17("sha256").update(`${session}\x00${agentId}`).digest("hex").slice(0, 32)}.json`);
22101
+ var readBinding = (file) => {
22102
+ try {
22103
+ const value = JSON.parse(fs23.readFileSync(file, "utf8"));
22104
+ return typeof value.worktree === "string" ? value : null;
22105
+ } catch {
22106
+ return null;
22107
+ }
22108
+ };
22109
+ var boundWorktree = (input) => {
22110
+ const agentId = input.session.agentId;
22111
+ if (!agentId)
22112
+ return null;
22113
+ const binding = readBinding(bindingFile(input.session.id, agentId));
22114
+ return binding && workitWorktreeOf(binding.worktree) === binding.worktree ? binding.worktree : null;
22115
+ };
22116
+ var bind = (input, worktree) => {
22117
+ const agentId = input.session.agentId;
22118
+ const file = bindingFile(input.session.id, agentId);
22119
+ const binding = {
22120
+ worktree,
22121
+ session: input.session.id,
22122
+ agentId,
22123
+ at: new Date().toISOString()
22124
+ };
22125
+ fs23.mkdirSync(path31.dirname(file), { recursive: true });
22126
+ fs23.writeFileSync(file, JSON.stringify(binding));
22127
+ try {
22128
+ if (inWorkitWorkspace(input.cwd))
22129
+ appendHookObserved(input.cwd, {
22130
+ type: "worktree.bound",
22131
+ actor: { host: input.host, session: input.session.id, agentId },
22132
+ agentType: input.session.agentType,
22133
+ worktree
22134
+ });
22135
+ } catch {}
22136
+ };
22137
+ var hostIsolated = (cwd, agentId) => {
22138
+ try {
22139
+ if (agentId === null)
22140
+ return false;
22141
+ const checkout = checkoutOf2(cwd);
22142
+ if (!checkout)
22143
+ return false;
22144
+ const dotGit = path31.join(checkout.root, ".git");
22145
+ if (!fs23.statSync(dotGit).isFile())
22146
+ return false;
22147
+ const admin = /^gitdir:\s*(.+)$/mu.exec(fs23.readFileSync(dotGit, "utf8"))?.[1]?.trim();
22148
+ if (!admin)
22149
+ return false;
22150
+ const adminDir = path31.resolve(checkout.root, admin);
22151
+ const back = fs23.readFileSync(path31.join(adminDir, "gitdir"), "utf8").trim();
22152
+ if (path31.relative(canonical3(path31.resolve(adminDir, back)), canonical3(dotGit)) !== "")
22153
+ return false;
22154
+ const common = path31.join(adminDir, "commondir");
22155
+ const gitDir = fs23.existsSync(common) ? path31.resolve(adminDir, fs23.readFileSync(common, "utf8").trim()) : adminDir;
22156
+ const expected = path31.join(path31.dirname(canonical3(gitDir)), ".claude", "worktrees", `agent-${agentId}`);
22157
+ return path31.relative(canonical3(expected), checkout.root) === "";
22158
+ } catch {
22159
+ return false;
22160
+ }
22161
+ };
22162
+ var shown = (target) => path31.basename(target) === ".workit-live-target" ? path31.dirname(target) : target;
22163
+ var unboundDeny = (input, target) => {
22164
+ 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.";
22165
+ return {
22166
+ kind: "deny",
22167
+ 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}`,
22168
+ unblock
22169
+ };
22170
+ };
22171
+ var outsideDeny = (input, worktree, target) => {
22172
+ 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.`;
22173
+ return {
22174
+ kind: "deny",
22175
+ reason: `workit worktree confinement: implementer ${input.session.agentId} is bound to the worktree ${worktree}, and ${shown(target)} is outside it. ${unblock}`,
22176
+ unblock
22177
+ };
22178
+ };
22179
+ var unknownDirDeny = (input, worktree) => {
22180
+ const wt = worktree ?? "<worktree>";
22181
+ const unblock = `Workit cannot resolve \`cd -\`, \`cd "$X"\` or \`popd\`: write by absolute path, \`git -C ${wt}\` or \`workit --cwd ${wt}\`.`;
22182
+ return {
22183
+ kind: "deny",
22184
+ reason: `workit worktree confinement: implementer ${input.session.agentId} writes after an unknown directory change. ${unblock}`,
22185
+ unblock
22186
+ };
22187
+ };
22188
+ function confineImplementer(input, targets) {
22189
+ try {
22190
+ if (!input.session.agentId || hostIsolated(input.cwd, input.session.agentId))
22191
+ return NONE5;
22192
+ if (targets.some((target) => within(path31.resolve(target), UNKNOWN_DIR)))
22193
+ return unknownDirDeny(input, boundWorktree(input));
22194
+ const bindings = canonical3(bindingDir());
22195
+ const judged = targets.map((named) => ({ named, real: canonical3(named) })).filter(({ real }) => within(real, bindings) || checkoutOf2(real) !== null);
22196
+ if (judged.length === 0)
22197
+ return NONE5;
22198
+ let worktree = boundWorktree(input);
22199
+ if (!worktree) {
22200
+ for (const { real } of judged) {
22201
+ worktree = within(real, bindings) ? null : workitWorktreeOf(real);
22202
+ if (worktree)
22203
+ break;
22204
+ }
22205
+ if (!worktree)
22206
+ return unboundDeny(input, judged[0].named);
22207
+ bind(input, worktree);
22208
+ }
22209
+ const root = canonical3(worktree);
22210
+ const outside = judged.find(({ real }) => !within(real, root) || within(real, bindings));
22211
+ return outside ? outsideDeny(input, worktree, outside.named) : NONE5;
22212
+ } catch {
22213
+ return NONE5;
22214
+ }
22215
+ }
22216
+ var MUTATING_REMOTE = new Set([
22217
+ "add",
22218
+ "remove",
22219
+ "rm",
22220
+ "rename",
22221
+ "set-url",
22222
+ "set-head",
22223
+ "set-branches",
22224
+ "prune"
22225
+ ]);
22226
+ var CONFIG_READ = /^(?:--get(?:-all|-regexp|-urlmatch)?|--list|-l|get|list)$/;
22227
+ var CONFIG_WRITE = /^(?:--(?:unset(?:-all)?|add|replace-all|rename-section|remove-section|edit)|-e|set|unset|rename-section|remove-section|edit)$/;
22228
+ var CONFIG_VALUE = /^(?:--file|-f|--blob|--type|--default|--comment)$/;
22229
+ var leadOwnedGit = (args) => {
22230
+ const [sub = "", ...rest] = args;
22231
+ if (rest.some((arg) => arg === "--help" || arg === "-h"))
22232
+ return false;
22233
+ const names = rest.filter((arg) => !arg.startsWith("-"));
22234
+ const flag = (pattern) => rest.some((arg) => pattern.test(arg));
22235
+ switch (sub) {
22236
+ case "worktree":
22237
+ return names[0] !== undefined && names[0] !== "list";
22238
+ case "update-ref":
22239
+ return true;
22240
+ case "symbolic-ref":
22241
+ return names.length > 1 || flag(/^(?:-d|--delete)$/);
22242
+ case "branch":
22243
+ return flag(/^-[a-zA-Z]*[dDfmMcC]|^--(?:delete|force|move|copy)$/);
22244
+ case "tag":
22245
+ return flag(/^-[a-zA-Z]*[df]|^--(?:delete|force)$/);
22246
+ case "push": {
22247
+ if (flag(/^(?:-d|--delete|--mirror|--prune)$/))
22248
+ return true;
22249
+ const refspecs = names.slice(1);
22250
+ return refspecs.some((name) => name.includes(":") || name.startsWith("+")) || refspecs.length > 0 && flag(/^-[a-zA-Z]*f|^--force/);
22251
+ }
22252
+ case "replace":
22253
+ case "filter-branch":
22254
+ case "gc":
22255
+ case "prune":
22256
+ case "repack":
22257
+ case "sparse-checkout":
22258
+ return true;
22259
+ case "notes":
22260
+ case "reflog":
22261
+ case "submodule":
22262
+ return !/^(?:list|show|status|exists)$/.test(names[0] ?? "list");
22263
+ case "stash":
22264
+ return names[0] === "drop" || names[0] === "clear";
22265
+ case "remote":
22266
+ return MUTATING_REMOTE.has(names[0] ?? "");
22267
+ case "config": {
22268
+ if (rest.some((arg) => CONFIG_READ.test(arg)))
22269
+ return false;
22270
+ if (rest.some((arg) => CONFIG_WRITE.test(arg)))
22271
+ return true;
22272
+ let operands = 0;
22273
+ for (let index = 0;index < rest.length; index++)
22274
+ if (CONFIG_VALUE.test(rest[index]))
22275
+ index++;
22276
+ else if (!rest[index].startsWith("-"))
22277
+ operands++;
22278
+ return operands > 1;
22279
+ }
22280
+ default:
22281
+ return false;
22282
+ }
22283
+ };
22284
+ var leadOwnedWorkit = (args) => !args.includes("--help") && args.some((_, start) => {
22285
+ if (start > 0 && !/(?:^|[\\/])workit(?:\.exe|\.cmd)?$/.test(args[start - 1]))
22286
+ return false;
22287
+ const [verb, sub] = skipWorkitGlobals(args.slice(start)).rest;
22288
+ return verb === "fanout" && /^(?:plan|worktree)$/.test(sub ?? "");
22289
+ });
22290
+ var leadOwnedDeny = (input, command) => {
22291
+ 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.";
22292
+ return {
22293
+ kind: "deny",
22294
+ reason: `workit worktree confinement: implementer ${input.session.agentId} may not run \`${command.slice(0, 160)}\`. ${unblock}`,
22295
+ unblock
22296
+ };
22297
+ };
22298
+ function confineImplementerShell(input, command, dialect = "posix") {
22299
+ try {
22300
+ if (!input.session.agentId || hostIsolated(input.cwd, input.session.agentId))
22301
+ return NONE5;
22302
+ if (rawInvocations(command, dialect).some((invocation) => invocation.tool === "git" && leadOwnedGit(invocation.args)) || workitInvocations(command, dialect).some(leadOwnedWorkit))
22303
+ return leadOwnedDeny(input, command);
22304
+ } catch {}
22305
+ return confineImplementer(input, shellMutationTargets(input.cwd, command, dialect, true));
22306
+ }
22307
+
21070
22308
  // packages/workit-core/src/hooks/handle.ts
21071
22309
  var usable2 = (value) => value === "native" || value === "partial";
21072
- var NONE5 = { kind: "none" };
22310
+ var NONE6 = { kind: "none" };
21073
22311
  var resendsOnChange = (descriptor) => descriptor.context.turnResend === "on-change" && usable2(descriptor.context.perTurn);
21074
22312
  var offered = new Set;
21075
22313
  var ROLES = {
@@ -21082,6 +22320,7 @@ var workitRole = (descriptor, agentType) => {
21082
22320
  const name = agentType.startsWith(prefix) ? agentType.slice(prefix.length) : "";
21083
22321
  return Object.hasOwn(ROLES, name) ? ROLES[name] : null;
21084
22322
  };
22323
+ var implementerAgent = (input, descriptor) => Boolean(input.session.agentId) && input.session.agentType !== null && workitRole(descriptor, input.session.agentType) === "implementer";
21085
22324
  var subagentSession = (session, agentId) => [session, agentId].filter((part) => Boolean(part)).join(":").replace(/[^A-Za-z0-9_.:@/+-]/g, "-").slice(0, 128);
21086
22325
  var CLAUDE_WORKTREE = /[\\/]\.claude[\\/]worktrees[\\/]/;
21087
22326
  var 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\`).`;
@@ -21089,7 +22328,7 @@ var subagentStartText = (input, descriptor, event) => {
21089
22328
  const session = input.session.id ?? null;
21090
22329
  const role = workitRole(descriptor, event.agentType);
21091
22330
  const who = `${descriptor.label} subagent ${event.agentId} (${event.agentType})`;
21092
- const isolated = descriptor.subagents.worktreeIsolation === "native" && (role === "implementer" || CLAUDE_WORKTREE.test(input.cwd));
22331
+ const isolated = descriptor.subagents.worktreeIsolation === "native" && (role === "implementer" ? hostIsolated(input.cwd, event.agentId) : CLAUDE_WORKTREE.test(input.cwd));
21093
22332
  const guide = role === "implementer" ? branchGuidance(input.cwd) : null;
21094
22333
  if (role === "implementer" && isolated)
21095
22334
  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.`;
@@ -21097,7 +22336,7 @@ var subagentStartText = (input, descriptor, event) => {
21097
22336
  return `${isolatedGit(input.cwd)}
21098
22337
  ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
21099
22338
  if (role === "implementer")
21100
- 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.`;
22339
+ 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.`;
21101
22340
  const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
21102
22341
  if (role !== "judge" || !event.agentId)
21103
22342
  return readOnly;
@@ -21128,7 +22367,7 @@ function decide(input, deps) {
21128
22367
  switch (event.kind) {
21129
22368
  case "session.start": {
21130
22369
  if (!usable2(descriptor.context.sessionStart))
21131
- return NONE5;
22370
+ return NONE6;
21132
22371
  const key = input.session.id ? `${input.host}\x00${input.session.id}` : null;
21133
22372
  const offer = event.source === "startup" && (key === null || !offered.has(key));
21134
22373
  if (offer && key !== null)
@@ -21143,26 +22382,31 @@ function decide(input, deps) {
21143
22382
  }
21144
22383
  case "context.turn": {
21145
22384
  if (!usable2(descriptor.context.perTurn))
21146
- return NONE5;
22385
+ return NONE6;
21147
22386
  const current = turnContextText(input, descriptor);
21148
22387
  const text = resendsOnChange(descriptor) ? changedTurnContext(input, current) : current;
21149
- return withContextLine(text ? { kind: "context", text } : NONE5, promptNudge(input, event.prompt));
22388
+ return withContextLine(text ? { kind: "context", text } : NONE6, promptNudge(input, event.prompt));
21150
22389
  }
21151
22390
  case "shell.pre": {
21152
22391
  const skillRead = skillReadIn(event.command, event.dialect);
21153
22392
  if (skillRead)
21154
22393
  recordSkillLoad(input, skillRead, "read");
21155
22394
  const canDeny = usable2(descriptor.shellPolicy.deny);
21156
- const policy = canDeny ? shellPolicy(input.cwd, event.command, event.dialect) : NONE5;
22395
+ const policy = canDeny ? shellPolicy(input.cwd, event.command, event.dialect) : NONE6;
21157
22396
  if (policy.kind !== "none")
21158
22397
  return policy;
21159
22398
  const postTool = usable2(descriptor.events["shell.post"].support);
21160
22399
  if (!postTool)
21161
22400
  settlePendingCommit(input);
21162
- const raw = canDeny ? rawGitPre(input, event.command, { nudge: false }) : NONE5;
22401
+ const raw = canDeny ? rawGitPre(input, event.command, { nudge: false }) : NONE6;
21163
22402
  if (raw.kind === "deny")
21164
22403
  return raw;
21165
- const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE5;
22404
+ if (canDeny && implementerAgent(input, descriptor)) {
22405
+ const confined = confineImplementerShell(input, event.command, event.dialect);
22406
+ if (confined.kind === "deny")
22407
+ return confined;
22408
+ }
22409
+ const gate = usable2(descriptor.events["write.pre"].support) ? shellGate(input.cwd, event.command, liveOwner(input, descriptor.transport), event.dialect) : NONE6;
21166
22410
  if (gate.kind === "deny")
21167
22411
  return gate;
21168
22412
  const plan = heavyGate(input.cwd, event.command, {
@@ -21185,10 +22429,10 @@ function decide(input, deps) {
21185
22429
  command: rewrittenTo,
21186
22430
  input: event.toolInput ? { ...event.toolInput, command: rewrittenTo } : null,
21187
22431
  text
21188
- } : NONE5;
22432
+ } : NONE6;
21189
22433
  if (descriptor.transport !== "hook-process")
21190
22434
  return rewritten(null);
21191
- const nudged = withContextLine(withContextLine(canDeny ? rawGitPre(input, event.command) : NONE5, aside), shipNudge(input, event.command));
22435
+ const nudged = withContextLine(withContextLine(canDeny ? rawGitPre(input, event.command) : NONE6, aside), shipNudge(input, event.command));
21192
22436
  if (!lease)
21193
22437
  return cwdCommand && nudged.kind !== "deny" ? rewritten(nudged.kind === "context" ? nudged.text : null) : nudged;
21194
22438
  return rewritten([rewriteNote(lease), nudged.kind === "context" ? nudged.text : null].filter(Boolean).join(`
@@ -21197,9 +22441,17 @@ function decide(input, deps) {
21197
22441
  case "shell.post":
21198
22442
  if (usable2(descriptor.events["shell.post"].support))
21199
22443
  rawGitPost(input, event.command, callKey(event.toolUseId, event.command));
21200
- return NONE5;
21201
- case "write.pre":
21202
- return usable2(descriptor.events["write.pre"].support) ? writeGate(input.cwd, event.paths, liveOwner(input, descriptor.transport)) : NONE5;
22444
+ return NONE6;
22445
+ case "write.pre": {
22446
+ if (!usable2(descriptor.events["write.pre"].support))
22447
+ return NONE6;
22448
+ if (implementerAgent(input, descriptor)) {
22449
+ const confined = confineImplementer(input, liveTargets(input.cwd, event.paths));
22450
+ if (confined.kind === "deny")
22451
+ return confined;
22452
+ }
22453
+ return writeGate(input.cwd, event.paths, liveOwner(input, descriptor.transport));
22454
+ }
21203
22455
  case "subagent.start":
21204
22456
  if (workitRole(descriptor, event.agentType) === "judge")
21205
22457
  trackJudge(input, event.agentId, true);
@@ -21216,22 +22468,22 @@ function decide(input, deps) {
21216
22468
  case "tool.pre":
21217
22469
  if (event.skill)
21218
22470
  recordSkillLoad(input, event.skill, "tool");
21219
- return NONE5;
22471
+ return NONE6;
21220
22472
  case "stop":
21221
- return usable2(descriptor.events.stop.support) ? stopDecision(input, descriptor) : NONE5;
22473
+ return usable2(descriptor.events.stop.support) ? stopDecision(input, descriptor) : NONE6;
21222
22474
  case "subagent.stop":
21223
22475
  trackJudge(input, event.agentId, false);
21224
- return NONE5;
22476
+ return NONE6;
21225
22477
  case "prompt.submit":
21226
- return NONE5;
22478
+ return NONE6;
21227
22479
  }
21228
22480
  }
21229
22481
  var failureDecision = (descriptor, event, error) => {
21230
22482
  if (event === "shell.pre" || event === "tool.pre" || event === "write.pre")
21231
- return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } : NONE5;
22483
+ return descriptor.shellPolicy.failClosed ? { kind: "deny", reason: error, unblock: null } : NONE6;
21232
22484
  if (event === "session.start" || event === "subagent.start")
21233
22485
  return { kind: "context", text: `[workit diagnostic: ${error}]` };
21234
- return NONE5;
22486
+ return NONE6;
21235
22487
  };
21236
22488
  var dispatchHook = (adapter, raw, env = process.env) => {
21237
22489
  const parsed = adapter.parse(raw, env);
@@ -21257,7 +22509,7 @@ var dispatchHook = (adapter, raw, env = process.env) => {
21257
22509
  };
21258
22510
  // packages/workit-core/src/hooks/deadline.ts
21259
22511
  import { appendFileSync, mkdirSync as mkdirSync3 } from "node:fs";
21260
- import path30 from "node:path";
22512
+ import path32 from "node:path";
21261
22513
  var HOOK_SKIP_TEXT = "workit context skipped: host under load";
21262
22514
  var HOOK_DEADLINE_MS = 8000;
21263
22515
  var HOST_DEADLINES = {
@@ -21275,7 +22527,7 @@ var hookDeadlineMs = (env, host = "", native) => {
21275
22527
  const row = HOST_DEADLINES[host];
21276
22528
  return override(env) ?? (row ? native === undefined ? Math.min(...Object.values(row)) : row[native] ?? row[""] : HOOK_DEADLINE_MS);
21277
22529
  };
21278
- var skipsFile = (stateDir) => path30.join(stateDir, HOOK_SKIPS_FILE);
22530
+ var skipsFile = (stateDir) => path32.join(stateDir, HOOK_SKIPS_FILE);
21279
22531
  var recordHookSkip = (skip, stateDir = resolveStateDir()) => {
21280
22532
  try {
21281
22533
  mkdirSync3(stateDir, { recursive: true });
@@ -21293,7 +22545,7 @@ var QUIET = new Set([
21293
22545
  ]);
21294
22546
  var skipDecision = (event) => QUIET.has(event) ? { kind: "none" } : { kind: "context", text: HOOK_SKIP_TEXT };
21295
22547
  var TIMED_OUT = Symbol("timed out");
21296
- var within = async (work, ms) => {
22548
+ var within2 = async (work, ms) => {
21297
22549
  let timer;
21298
22550
  const expired = new Promise((resolve) => {
21299
22551
  timer = setTimeout(() => resolve(TIMED_OUT), Math.max(0, ms));
@@ -21324,7 +22576,7 @@ var readWithin = async (stdin, budget) => {
21324
22576
  text += String(chunk);
21325
22577
  return text;
21326
22578
  })();
21327
- const text = await within(read, budget.startedAt + budget.deadlineMs - budget.now());
22579
+ const text = await within2(read, budget.startedAt + budget.deadlineMs - budget.now());
21328
22580
  return isTimedOut(text) ? null : text;
21329
22581
  };
21330
22582
  var note = (adapter, raw, env, budget, outcome, stage) => {
@@ -21353,7 +22605,7 @@ var dispatchWithin = async (adapter, raw, env, budget, dispatch = dispatchHook)
21353
22605
  if (budget.host !== null && typeof native === "string")
21354
22606
  budget.deadlineMs = hookDeadlineMs(env, budget.host, native);
21355
22607
  const remaining = () => budget.startedAt + budget.deadlineMs - budget.now();
21356
- const result = remaining() > 0 ? await within(Promise.resolve().then(() => dispatch(adapter, raw, env)), remaining()) : TIMED_OUT;
22608
+ const result = remaining() > 0 ? await within2(Promise.resolve().then(() => dispatch(adapter, raw, env)), remaining()) : TIMED_OUT;
21357
22609
  if (isTimedOut(result))
21358
22610
  return skippedHook(adapter, raw, env, budget, "dispatch");
21359
22611
  if (remaining() < 0)
@@ -21919,8 +23171,8 @@ var codexAdapter = {
21919
23171
  };
21920
23172
 
21921
23173
  // packages/workit-core/src/hooks/hosts/cursor.ts
21922
- import fs23 from "node:fs";
21923
- import path31 from "node:path";
23174
+ import fs24 from "node:fs";
23175
+ import path33 from "node:path";
21924
23176
  var undocumented = { support: "undocumented", native: null };
21925
23177
  var none = { support: "none", native: null };
21926
23178
  var CURSOR_DESCRIPTOR = {
@@ -22117,17 +23369,17 @@ var protocolEvent2 = (input) => {
22117
23369
  };
22118
23370
  var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
22119
23371
  var lastAssistantText = (file) => {
22120
- if (!file || !path31.isAbsolute(file))
23372
+ if (!file || !path33.isAbsolute(file))
22121
23373
  return null;
22122
23374
  try {
22123
- const size = fs23.statSync(file).size;
23375
+ const size = fs24.statSync(file).size;
22124
23376
  const start = Math.max(0, size - TRANSCRIPT_TAIL_BYTES);
22125
23377
  const buffer = Buffer.alloc(size - start);
22126
- const fd = fs23.openSync(file, "r");
23378
+ const fd = fs24.openSync(file, "r");
22127
23379
  try {
22128
- fs23.readSync(fd, buffer, 0, buffer.length, start);
23380
+ fs24.readSync(fd, buffer, 0, buffer.length, start);
22129
23381
  } finally {
22130
- fs23.closeSync(fd);
23382
+ fs24.closeSync(fd);
22131
23383
  }
22132
23384
  const lines = buffer.toString("utf8").split(`
22133
23385
  `);
@@ -22154,7 +23406,7 @@ var lastAssistantText = (file) => {
22154
23406
  return null;
22155
23407
  }
22156
23408
  };
22157
- var within2 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path31.sep) ? root : `${root}${path31.sep}`);
23409
+ var within3 = (root, dir) => dir === root || dir.startsWith(root.endsWith(path33.sep) ? root : `${root}${path33.sep}`);
22158
23410
  var hookCwd = (input) => {
22159
23411
  const cwd = existingDirectory(input.cwd);
22160
23412
  if (cwd && input.hook_event_name === "beforeShellExecution")
@@ -22163,7 +23415,7 @@ var hookCwd = (input) => {
22163
23415
  };
22164
23416
  var cursorWorkspaceRoot = (input) => {
22165
23417
  const cwd = existingDirectory(input.cwd);
22166
- return cwd && input.workspace_roots.find((root) => within2(root, cwd)) || input.workspace_roots[0];
23418
+ return cwd && input.workspace_roots.find((root) => within3(root, cwd)) || input.workspace_roots[0];
22167
23419
  };
22168
23420
  var cursorDeny = (reason) => ({
22169
23421
  permission: "deny",