@brainervirus/workit-pi 8.23.0 → 8.25.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.
package/dist/workit.js CHANGED
@@ -4,8 +4,8 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
4
4
 
5
5
  // packages/workit-core/src/core.ts
6
6
  import { spawnSync as spawnSync16 } from "node:child_process";
7
- import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
8
- import path25 from "node:path";
7
+ import { existsSync as existsSync9, realpathSync as realpathSync5 } from "node:fs";
8
+ import path26 from "node:path";
9
9
 
10
10
  // packages/workit-core/src/core/task-contract.ts
11
11
  import { createHash, randomUUID } from "node:crypto";
@@ -8402,7 +8402,7 @@ var workspaceConfigSchema = object({
8402
8402
  }).passthrough().optional(),
8403
8403
  autoApprove: union([boolean2(), array(nonBlank)]).optional(),
8404
8404
  defaultEndpoint: unknown().optional(),
8405
- verification: _enum(["self", "independent"]).optional(),
8405
+ verification: _enum(["proportional", "self", "independent"]).optional(),
8406
8406
  delegation: unknown().optional(),
8407
8407
  trailers: boolean2().optional(),
8408
8408
  liveSessionGuard: boolean2().optional(),
@@ -8965,6 +8965,144 @@ function resolveRef(cwd, ref) {
8965
8965
  return null;
8966
8966
  return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
8967
8967
  }
8968
+ function originHeadRef(cwd) {
8969
+ return line(git(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]));
8970
+ }
8971
+ function ownChangeDrift(cwd, range) {
8972
+ const before = diffRaw(cwd, range.fromFork, range.from);
8973
+ const after = diffRaw(cwd, range.toFork, range.to);
8974
+ if (!before || !after)
8975
+ return null;
8976
+ const judged = new Map(before.map((entry) => [entry.path, entry]));
8977
+ const current = new Map(after.map((entry) => [entry.path, entry]));
8978
+ const same = (a, b) => a !== undefined && b !== undefined && a.oldMode === b.oldMode && a.oldBlob === b.oldBlob && a.newMode === b.newMode && a.newBlob === b.newBlob;
8979
+ return [...new Set([...judged.keys(), ...current.keys()])].filter((file) => !same(judged.get(file), current.get(file))).toSorted();
8980
+ }
8981
+ function sameOwnChange(cwd, range) {
8982
+ const fromFork = mergeBase(cwd, range.fromBase, range.from);
8983
+ const toFork = mergeBase(cwd, range.toBase, range.to);
8984
+ if (!fromFork || !toFork)
8985
+ return false;
8986
+ return ownChangeDrift(cwd, { from: range.from, to: range.to, fromFork, toFork })?.length === 0;
8987
+ }
8988
+ var LANE_DIFF = [
8989
+ "-c",
8990
+ "core.quotePath=false",
8991
+ "diff",
8992
+ "--no-color",
8993
+ "--no-ext-diff",
8994
+ "--no-textconv",
8995
+ "--no-renames"
8996
+ ];
8997
+ var laneRange = (fork, head) => {
8998
+ if (!safeArg(fork) || head !== null && !safeArg(head))
8999
+ return null;
9000
+ return head === null ? [fork] : [fork, head];
9001
+ };
9002
+ function diffNumstat(cwd, fork, head) {
9003
+ const range = laneRange(fork, head);
9004
+ if (!range)
9005
+ return null;
9006
+ const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
9007
+ if (!run.ok)
9008
+ return null;
9009
+ const out = [];
9010
+ for (const record of run.stdout.split("\x00")) {
9011
+ const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
9012
+ if (!match)
9013
+ continue;
9014
+ const binary = match[1] === "-";
9015
+ out.push({
9016
+ path: match[3],
9017
+ added: binary ? 0 : Number(match[1]),
9018
+ deleted: binary ? 0 : Number(match[2]),
9019
+ binary
9020
+ });
9021
+ }
9022
+ return out;
9023
+ }
9024
+ function diffRaw(cwd, fork, head) {
9025
+ const range = laneRange(fork, head);
9026
+ if (!range)
9027
+ return null;
9028
+ const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
9029
+ if (!run.ok)
9030
+ return null;
9031
+ const fields = run.stdout.split("\x00");
9032
+ const out = [];
9033
+ for (let index = 0;index + 1 < fields.length; index += 2) {
9034
+ const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
9035
+ if (!meta)
9036
+ return null;
9037
+ out.push({
9038
+ path: fields[index + 1],
9039
+ oldMode: meta[1],
9040
+ newMode: meta[2],
9041
+ oldBlob: meta[3],
9042
+ newBlob: meta[4]
9043
+ });
9044
+ }
9045
+ return out;
9046
+ }
9047
+ function readBlobs(cwd, ids) {
9048
+ const out = new Map;
9049
+ const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
9050
+ if (!wanted.length)
9051
+ return out;
9052
+ const run = spawnSync3("git", ["cat-file", "--batch"], {
9053
+ cwd,
9054
+ input: `${wanted.join(`
9055
+ `)}
9056
+ `,
9057
+ maxBuffer: MAX_BUFFER,
9058
+ timeout: GIT_TIMEOUTS.local,
9059
+ killSignal: "SIGKILL",
9060
+ windowsHide: true
9061
+ });
9062
+ if (run.status !== 0 || !run.stdout)
9063
+ return null;
9064
+ const bytes = run.stdout;
9065
+ let at = 0;
9066
+ for (const id of wanted) {
9067
+ const end = bytes.indexOf(10, at);
9068
+ if (end < 0)
9069
+ return null;
9070
+ const header = bytes.subarray(at, end).toString("utf8").split(" ");
9071
+ at = end + 1;
9072
+ if (header[1] === "missing")
9073
+ continue;
9074
+ const size = Number(header[2]);
9075
+ if (header[0] !== id || !Number.isSafeInteger(size))
9076
+ return null;
9077
+ out.set(id, bytes.subarray(at, at + size));
9078
+ at += size + 1;
9079
+ }
9080
+ return out;
9081
+ }
9082
+ function treeFilesNamed(cwd, rev, name) {
9083
+ if (!safeArg(rev))
9084
+ return null;
9085
+ const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
9086
+ if (!run.ok)
9087
+ return null;
9088
+ const out = new Map;
9089
+ for (const record of run.stdout.split("\x00")) {
9090
+ const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
9091
+ if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
9092
+ out.set(match[2], match[1]);
9093
+ }
9094
+ return out;
9095
+ }
9096
+ function fileAt(cwd, rev, file) {
9097
+ if (!safeArg(rev))
9098
+ return null;
9099
+ const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
9100
+ return run.ok ? run.stdout : null;
9101
+ }
9102
+ function untrackedFiles(cwd) {
9103
+ const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
9104
+ return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
9105
+ }
8968
9106
  var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
8969
9107
  function currentBranch(cwd) {
8970
9108
  return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
@@ -12744,7 +12882,7 @@ var workerBlocksTransition = (transition, state) => transition === "pause" ? fal
12744
12882
  import { createHash as createHash12 } from "node:crypto";
12745
12883
  import * as fs14 from "node:fs";
12746
12884
  import { spawnSync as spawnSync10 } from "node:child_process";
12747
- import path17 from "node:path";
12885
+ import path18 from "node:path";
12748
12886
 
12749
12887
  // packages/workit-core/src/check-config.ts
12750
12888
  import { spawnSync as spawnSync8 } from "node:child_process";
@@ -12974,45 +13112,786 @@ function checkCommand(args, version, onPath = workitAvailable()) {
12974
13112
  }
12975
13113
 
12976
13114
  // packages/workit-core/src/core/policy/derive.ts
12977
- import { existsSync as existsSync4 } from "node:fs";
12978
- import path16 from "node:path";
13115
+ import { existsSync as existsSync5 } from "node:fs";
13116
+ import path17 from "node:path";
12979
13117
 
12980
13118
  // packages/workit-core/src/ledger.ts
12981
13119
  import { spawnSync as spawnSync9 } from "node:child_process";
12982
13120
  import { createHash as createHash11, randomBytes as randomBytes3 } from "node:crypto";
12983
13121
  import fs13 from "node:fs";
12984
- import path15 from "node:path";
13122
+ import path16 from "node:path";
13123
+
13124
+ // packages/workit-core/src/core/policy/lane.ts
13125
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
13126
+ import path14 from "node:path";
13127
+
13128
+ // packages/workit-core/src/git/default-base.ts
13129
+ var safeRef = (value) => value.length > 0 && !value.startsWith("-");
13130
+ var trackTrunk = (cwd) => {
13131
+ try {
13132
+ const resolved = vcsConfig("resolve", cwd);
13133
+ if (resolved.ok === false || !resolved.releaseTrack || resolved.releaseTrack.blocking)
13134
+ return null;
13135
+ const target = String(resolved.defaultTargetBranch ?? "").trim();
13136
+ return target && safeRef(target) ? target : null;
13137
+ } catch {
13138
+ return null;
13139
+ }
13140
+ };
13141
+ function defaultBase(cwd) {
13142
+ const trunk = trackTrunk(cwd);
13143
+ if (trunk) {
13144
+ for (const candidate of [`origin/${trunk}`, trunk])
13145
+ if (resolveRef(cwd, candidate))
13146
+ return candidate;
13147
+ }
13148
+ const originHead = originHeadRef(cwd);
13149
+ if (originHead && resolveRef(cwd, originHead))
13150
+ return originHead;
13151
+ for (const candidate of [
13152
+ "origin/main",
13153
+ "origin/master",
13154
+ "main",
13155
+ "master",
13156
+ "origin/develop",
13157
+ "develop"
13158
+ ])
13159
+ if (resolveRef(cwd, candidate))
13160
+ return candidate;
13161
+ return null;
13162
+ }
13163
+
13164
+ // packages/workit-core/src/core/policy/attributes.ts
13165
+ function patternRegex(dir, pattern) {
13166
+ if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
13167
+ return null;
13168
+ const anchored = pattern.includes("/");
13169
+ const body = pattern.replace(/^\//u, "");
13170
+ let source = "";
13171
+ for (let index = 0;index < body.length; index += 1) {
13172
+ const char = body[index];
13173
+ if (char === "*" && body[index + 1] === "*") {
13174
+ const slash = body[index + 2] === "/";
13175
+ source += slash ? "(?:.*/)?" : ".*";
13176
+ index += slash ? 2 : 1;
13177
+ } else if (char === "*")
13178
+ source += "[^/]*";
13179
+ else if (char === "?")
13180
+ source += "[^/]";
13181
+ else if (char === "[") {
13182
+ const end = body.indexOf("]", index + 2);
13183
+ if (end < 0)
13184
+ return null;
13185
+ const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
13186
+ source += `[${set}]`;
13187
+ index = end;
13188
+ } else if (char === "\\" && index + 1 < body.length) {
13189
+ source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
13190
+ index += 1;
13191
+ } else
13192
+ source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
13193
+ }
13194
+ const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
13195
+ try {
13196
+ return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
13197
+ } catch {
13198
+ return null;
13199
+ }
13200
+ }
13201
+ function rulesOf(dir, text) {
13202
+ const out = [];
13203
+ for (const raw of text.split(`
13204
+ `)) {
13205
+ const line = raw.trim();
13206
+ if (!line || line.startsWith("#") || line.startsWith("[attr]"))
13207
+ continue;
13208
+ const [pattern, ...attributes] = line.split(/\s+/u);
13209
+ let generated = null;
13210
+ for (const attribute of attributes) {
13211
+ if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
13212
+ generated = true;
13213
+ else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
13214
+ generated = false;
13215
+ }
13216
+ if (generated === null)
13217
+ continue;
13218
+ const regex = patternRegex(dir, pattern);
13219
+ if (regex)
13220
+ out.push({ regex, generated });
13221
+ }
13222
+ return out;
13223
+ }
13224
+ function generatedByAttributes(files, paths) {
13225
+ const rules = [...files].map(([file, text]) => {
13226
+ const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
13227
+ return {
13228
+ depth: dir ? dir.split("/").length : 0,
13229
+ rules: rulesOf(dir, text)
13230
+ };
13231
+ }).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
13232
+ const out = new Set;
13233
+ for (const file of paths) {
13234
+ const last = rules.findLast((rule) => rule.regex.test(file));
13235
+ if (last?.generated)
13236
+ out.add(file);
13237
+ }
13238
+ return out;
13239
+ }
13240
+
13241
+ // packages/workit-core/src/core/policy/substance.ts
13242
+ var SLASH_BLOCK = [["/*", "*/"]];
13243
+ var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
13244
+ var SYNTAXES = Object.fromEntries([
13245
+ ...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
13246
+ ...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
13247
+ ext,
13248
+ { ...C_LIKE, triple: true }
13249
+ ]),
13250
+ ["go", { ...C_LIKE, rawBacktick: true }],
13251
+ ...["css", "scss", "less"].map((ext) => [
13252
+ ext,
13253
+ { line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
13254
+ ]),
13255
+ ...["py", "pyi"].map((ext) => [
13256
+ ext,
13257
+ { line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
13258
+ ]),
13259
+ ["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
13260
+ ...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
13261
+ ext,
13262
+ { line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
13263
+ ])
13264
+ ]);
13265
+ 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;
13266
+ var extensionOf = (file) => {
13267
+ const name = file.slice(file.lastIndexOf("/") + 1);
13268
+ const dot = name.lastIndexOf(".");
13269
+ return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
13270
+ };
13271
+ var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
13272
+ var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
13273
+ var SPACE = /[ \t\r\f\v]/u;
13274
+ var WORD = /[\w$]/u;
13275
+ var NO_SPACE_AFTER = "([{,;";
13276
+ var NO_SPACE_BEFORE = ")]},;";
13277
+ var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
13278
+ var REGEX_KEYWORDS = new Set([
13279
+ "return",
13280
+ "typeof",
13281
+ "instanceof",
13282
+ "case",
13283
+ "do",
13284
+ "else",
13285
+ "in",
13286
+ "of",
13287
+ "new",
13288
+ "delete",
13289
+ "void",
13290
+ "throw",
13291
+ "yield",
13292
+ "await"
13293
+ ]);
13294
+ function substanceOf(file, text) {
13295
+ const syntax = syntaxOf(file);
13296
+ if (!syntax)
13297
+ return plainSubstance(text);
13298
+ const markup = syntax.block.some(([open]) => open === "<!--");
13299
+ if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
13300
+ return plainSubstance(text);
13301
+ return lex(syntax, text);
13302
+ }
13303
+ function plainSubstance(text) {
13304
+ return text.split(`
13305
+ `).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
13306
+ `);
13307
+ }
13308
+ function lex(syntax, text) {
13309
+ const collapse = !syntax.indented;
13310
+ let out = "";
13311
+ let lineStart = 0;
13312
+ let kept = 0;
13313
+ let pendingSpace = false;
13314
+ const frames = [{ kind: "code", braces: 0 }];
13315
+ let index = 0;
13316
+ if (text.startsWith("#!")) {
13317
+ const end = text.indexOf(`
13318
+ `);
13319
+ out = end < 0 ? text : text.slice(0, end);
13320
+ kept = out.length;
13321
+ index = end < 0 ? text.length : end;
13322
+ }
13323
+ const emitCode = (chunk) => {
13324
+ if (collapse && pendingSpace && out.length > lineStart) {
13325
+ const before = out[out.length - 1];
13326
+ if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
13327
+ out += " ";
13328
+ }
13329
+ pendingSpace = false;
13330
+ out += chunk;
13331
+ };
13332
+ const emitVerbatim = (chunk) => {
13333
+ emitCode(chunk);
13334
+ kept = out.length;
13335
+ };
13336
+ const newline = () => {
13337
+ if (!collapse) {
13338
+ let end = out.length;
13339
+ while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
13340
+ end -= 1;
13341
+ out = out.slice(0, end);
13342
+ }
13343
+ pendingSpace = false;
13344
+ if (out.length > lineStart) {
13345
+ out += `
13346
+ `;
13347
+ lineStart = out.length;
13348
+ }
13349
+ };
13350
+ const regexAllowed = () => {
13351
+ let end = out.length;
13352
+ while (end > 0 && /\s/u.test(out[end - 1]))
13353
+ end -= 1;
13354
+ if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
13355
+ return true;
13356
+ let start = end;
13357
+ while (start > 0 && WORD.test(out[start - 1]))
13358
+ start -= 1;
13359
+ return REGEX_KEYWORDS.has(out.slice(start, end));
13360
+ };
13361
+ const scanString = (quote, escapes, multiline) => {
13362
+ const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
13363
+ const fstring = syntax.fstring === true && /f/iu.test(prefix);
13364
+ let end = index + quote.length;
13365
+ while (end < text.length) {
13366
+ const char = text[end];
13367
+ if (escapes && char === "\\") {
13368
+ end += 2;
13369
+ continue;
13370
+ }
13371
+ if (text.startsWith(quote, end)) {
13372
+ if (fstring && unbalanced(text.slice(index + quote.length, end)))
13373
+ return false;
13374
+ emitVerbatim(text.slice(index, end + quote.length));
13375
+ index = end + quote.length;
13376
+ return true;
13377
+ }
13378
+ if (char === `
13379
+ ` && !multiline)
13380
+ return false;
13381
+ end += 1;
13382
+ }
13383
+ return false;
13384
+ };
13385
+ while (index < text.length) {
13386
+ const frame = frames[frames.length - 1];
13387
+ const char = text[index];
13388
+ if (frame.kind === "template") {
13389
+ if (char === "\\") {
13390
+ emitVerbatim(text.slice(index, index + 2));
13391
+ index += 2;
13392
+ } else if (char === "`") {
13393
+ emitVerbatim(char);
13394
+ frames.pop();
13395
+ index += 1;
13396
+ } else if (text.startsWith("${", index)) {
13397
+ emitVerbatim("${");
13398
+ frames.push({ kind: "code", braces: 0 });
13399
+ index += 2;
13400
+ } else {
13401
+ out += char;
13402
+ kept = out.length;
13403
+ index += 1;
13404
+ }
13405
+ continue;
13406
+ }
13407
+ if (char === `
13408
+ `) {
13409
+ newline();
13410
+ index += 1;
13411
+ continue;
13412
+ }
13413
+ if (SPACE.test(char)) {
13414
+ if (collapse)
13415
+ pendingSpace = true;
13416
+ else
13417
+ out += char;
13418
+ index += 1;
13419
+ continue;
13420
+ }
13421
+ const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
13422
+ if (lineOpener) {
13423
+ const end = text.indexOf(`
13424
+ `, index);
13425
+ const comment = text.slice(index, end < 0 ? text.length : end);
13426
+ if (DIRECTIVE.test(comment.slice(lineOpener.length)))
13427
+ emitVerbatim(comment);
13428
+ else
13429
+ pendingSpace = true;
13430
+ index = end < 0 ? text.length : end;
13431
+ continue;
13432
+ }
13433
+ const block = syntax.block.find(([open]) => text.startsWith(open, index));
13434
+ if (block) {
13435
+ const end = text.indexOf(block[1], index + block[0].length);
13436
+ if (end < 0)
13437
+ return null;
13438
+ const comment = text.slice(index, end + block[1].length);
13439
+ if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
13440
+ emitVerbatim(comment);
13441
+ else
13442
+ pendingSpace = true;
13443
+ if (comment.includes(`
13444
+ `))
13445
+ newline();
13446
+ index = end + block[1].length;
13447
+ continue;
13448
+ }
13449
+ if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
13450
+ if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
13451
+ return null;
13452
+ continue;
13453
+ }
13454
+ if (syntax.quotes.includes(char)) {
13455
+ if (!scanString(char, true, Boolean(syntax.multiline)))
13456
+ return null;
13457
+ continue;
13458
+ }
13459
+ if (syntax.literal?.includes(char)) {
13460
+ if (!scanString(char, false, false))
13461
+ return null;
13462
+ continue;
13463
+ }
13464
+ if (syntax.rawBacktick && char === "`") {
13465
+ if (!scanString("`", false, true))
13466
+ return null;
13467
+ continue;
13468
+ }
13469
+ if (syntax.js && char === "`") {
13470
+ emitVerbatim(char);
13471
+ frames.push({ kind: "template" });
13472
+ index += 1;
13473
+ continue;
13474
+ }
13475
+ if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
13476
+ return null;
13477
+ if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
13478
+ const lineEnd = text.indexOf(`
13479
+ `, index);
13480
+ if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
13481
+ return null;
13482
+ }
13483
+ if (syntax.js && char === "/" && regexAllowed()) {
13484
+ let end = index + 1;
13485
+ let inClass = false;
13486
+ while (end < text.length && (inClass || text[end] !== "/")) {
13487
+ if (text[end] === `
13488
+ `)
13489
+ return null;
13490
+ if (text[end] === "\\")
13491
+ end += 1;
13492
+ else if (text[end] === "[")
13493
+ inClass = true;
13494
+ else if (text[end] === "]")
13495
+ inClass = false;
13496
+ end += 1;
13497
+ }
13498
+ if (end >= text.length)
13499
+ return null;
13500
+ emitVerbatim(text.slice(index, end + 1));
13501
+ index = end + 1;
13502
+ continue;
13503
+ }
13504
+ if (frames.length > 1 && frame.kind === "code") {
13505
+ if (char === "{")
13506
+ frame.braces += 1;
13507
+ else if (char === "}") {
13508
+ if (frame.braces === 0) {
13509
+ emitVerbatim(char);
13510
+ frames.pop();
13511
+ index += 1;
13512
+ continue;
13513
+ }
13514
+ frame.braces -= 1;
13515
+ }
13516
+ }
13517
+ emitCode(char);
13518
+ index += 1;
13519
+ }
13520
+ if (frames.length !== 1)
13521
+ return null;
13522
+ newline();
13523
+ return out;
13524
+ }
13525
+ function unbalanced(body) {
13526
+ let depth = 0;
13527
+ for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
13528
+ if (part === "{")
13529
+ depth += 1;
13530
+ else if (part === "}")
13531
+ depth = Math.max(0, depth - 1);
13532
+ }
13533
+ return depth > 0;
13534
+ }
13535
+ function hasOpaqueLineBreak(text) {
13536
+ return /\r(?!\n)|[\u2028\u2029]/u.test(text);
13537
+ }
13538
+ var decoder = new TextDecoder("utf-8", { fatal: true });
13539
+ function textOf(bytes) {
13540
+ if (bytes.includes(0))
13541
+ return null;
13542
+ try {
13543
+ return decoder.decode(bytes);
13544
+ } catch {
13545
+ return null;
13546
+ }
13547
+ }
13548
+ function cosmeticChange(file, before, after) {
13549
+ const left = textOf(before);
13550
+ const right = textOf(after);
13551
+ if (left === null || right === null)
13552
+ return false;
13553
+ if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
13554
+ return false;
13555
+ const a = substanceOf(file, left);
13556
+ return a !== null && a === substanceOf(file, right);
13557
+ }
13558
+
13559
+ // packages/workit-core/src/core/policy/lane.ts
13560
+ var LANE_THRESHOLDS = {
13561
+ selfMaxLines: 100,
13562
+ selfMaxFiles: 5,
13563
+ fullMinLines: 500
13564
+ };
13565
+ var RISK_TOKENS = new Set([
13566
+ "auth",
13567
+ "authn",
13568
+ "authz",
13569
+ "oauth",
13570
+ "security",
13571
+ "permission",
13572
+ "permissions",
13573
+ "secret",
13574
+ "secrets",
13575
+ "crypto",
13576
+ "gate",
13577
+ "gates",
13578
+ "policy",
13579
+ "policies",
13580
+ "hook",
13581
+ "hooks",
13582
+ "husky",
13583
+ "lefthook",
13584
+ "release",
13585
+ "releases",
13586
+ "migration",
13587
+ "migrations",
13588
+ "migrate",
13589
+ "workflows"
13590
+ ]);
13591
+ var RISK_FILES = new Set([
13592
+ ".gitlab-ci.yml",
13593
+ "Jenkinsfile",
13594
+ "azure-pipelines.yml",
13595
+ ".pre-commit-config.yaml",
13596
+ ".workit/risk-paths"
13597
+ ]);
13598
+ var RISK_NAMES = new Set([".gitattributes"]);
13599
+ var RISK_PATHS_FILE = ".workit/risk-paths";
13600
+ var LOCKFILES = new Set([
13601
+ "bun.lock",
13602
+ "bun.lockb",
13603
+ "package-lock.json",
13604
+ "npm-shrinkwrap.json",
13605
+ "yarn.lock",
13606
+ "pnpm-lock.yaml",
13607
+ "Cargo.lock",
13608
+ "go.sum",
13609
+ "poetry.lock",
13610
+ "uv.lock",
13611
+ "Pipfile.lock",
13612
+ "Gemfile.lock",
13613
+ "composer.lock"
13614
+ ]);
13615
+ var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc)$/iu;
13616
+ var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
13617
+ var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
13618
+ var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__)\/|\.(?:test|spec)\.[^/]+$|_test\.[^/]+$/iu;
13619
+ var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
13620
+ var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
13621
+ function kindOf(file) {
13622
+ if (file.binary)
13623
+ return "behavior";
13624
+ if (file.generated)
13625
+ return "generated";
13626
+ if (LOCKFILES.has(basename(file.path)))
13627
+ return "lockfile";
13628
+ if (isDoc(file.path))
13629
+ return "doc";
13630
+ if (file.cosmetic)
13631
+ return "cosmetic";
13632
+ return TEST_PATH.test(file.path) ? "test" : "behavior";
13633
+ }
13634
+ var globCache = new Map;
13635
+ function globMatch(pattern, file) {
13636
+ let regex = globCache.get(pattern);
13637
+ if (!regex) {
13638
+ let source = "";
13639
+ for (let index = 0;index < pattern.length; index += 1) {
13640
+ const char = pattern[index];
13641
+ if (char === "*" && pattern[index + 1] === "*") {
13642
+ source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
13643
+ index += pattern[index + 2] === "/" ? 2 : 1;
13644
+ } else if (char === "*")
13645
+ source += "[^/]*";
13646
+ else if (char === "?")
13647
+ source += "[^/]";
13648
+ else
13649
+ source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
13650
+ }
13651
+ regex = new RegExp(`^${source}$`, "u");
13652
+ globCache.set(pattern, regex);
13653
+ }
13654
+ return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
13655
+ }
13656
+ function isRiskPath(file, extra = []) {
13657
+ if (extra.some((pattern) => globMatch(pattern, file)))
13658
+ return true;
13659
+ if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
13660
+ return true;
13661
+ if (TEST_PATH.test(file) || isDoc(file))
13662
+ return false;
13663
+ 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)));
13664
+ }
13665
+ var parseRiskPaths = (text) => text.split(`
13666
+ `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
13667
+ function computeLane(input) {
13668
+ const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
13669
+ const risky = new Set(riskFiles);
13670
+ const kinds = input.files.map((file) => ({
13671
+ file,
13672
+ kind: risky.has(file.path) ? "behavior" : kindOf(file)
13673
+ }));
13674
+ const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
13675
+ const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
13676
+ const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
13677
+ const result = (lane, reasons) => ({
13678
+ lane,
13679
+ reasons,
13680
+ lines,
13681
+ files: behavior.length,
13682
+ riskFiles
13683
+ });
13684
+ const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
13685
+ if (input.riskTier === "high")
13686
+ return result("full", ["the task judgment records high risk"]);
13687
+ if (riskFiles.length)
13688
+ return result("full", [
13689
+ `touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
13690
+ ]);
13691
+ if (!behavior.length)
13692
+ return result("none", [
13693
+ input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
13694
+ ]);
13695
+ if (lines >= fullMinLines)
13696
+ return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
13697
+ if (lines > selfMaxLines)
13698
+ return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
13699
+ if (behavior.length > selfMaxFiles)
13700
+ return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
13701
+ if (opaque.length)
13702
+ return result("focused", [
13703
+ `a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
13704
+ ]);
13705
+ return result("self", [
13706
+ `small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
13707
+ ]);
13708
+ }
13709
+ function untrackedStat(cwd, file) {
13710
+ try {
13711
+ const bytes = readFileSync4(path14.join(cwd, file));
13712
+ if (bytes.includes(0))
13713
+ return { path: file, added: 0, deleted: 0, binary: true };
13714
+ const text = bytes.toString("utf8");
13715
+ if (hasOpaqueLineBreak(text))
13716
+ return { path: file, added: 0, deleted: 0, binary: true };
13717
+ const lines = text.split(`
13718
+ `).length - (text.endsWith(`
13719
+ `) ? 1 : 0);
13720
+ return { path: file, added: lines, deleted: 0 };
13721
+ } catch {
13722
+ return { path: file, added: 0, deleted: 0, binary: true };
13723
+ }
13724
+ }
13725
+ var REGULAR = new Set(["100644", "100755"]);
13726
+ var ABSENT = /^0+$/u;
13727
+ function cosmeticFiles(cwd, fork, head) {
13728
+ const raw = diffRaw(cwd, fork, head);
13729
+ if (!raw)
13730
+ return null;
13731
+ const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
13732
+ const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
13733
+ if (!blobs)
13734
+ return null;
13735
+ const out = new Set;
13736
+ const opaque = new Set;
13737
+ for (const entry of candidates) {
13738
+ const before = blobs.get(entry.oldBlob);
13739
+ let after = blobs.get(entry.newBlob);
13740
+ if (!after && head === null && ABSENT.test(entry.newBlob))
13741
+ try {
13742
+ after = readFileSync4(path14.join(cwd, entry.path));
13743
+ } catch {
13744
+ after = undefined;
13745
+ }
13746
+ if (before && after && cosmeticChange(entry.path, before, after))
13747
+ out.add(entry.path);
13748
+ else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
13749
+ opaque.add(entry.path);
13750
+ }
13751
+ return { cosmetic: out, opaque };
13752
+ }
13753
+ function generatedAt(cwd, fork, paths) {
13754
+ const files = treeFilesNamed(cwd, fork, ".gitattributes");
13755
+ const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
13756
+ if (!files || !blobs)
13757
+ return new Set;
13758
+ const texts = new Map;
13759
+ for (const [file, id] of files) {
13760
+ const bytes = blobs.get(id);
13761
+ if (bytes)
13762
+ texts.set(file, bytes.toString("utf8"));
13763
+ }
13764
+ return generatedByAttributes(texts, paths);
13765
+ }
13766
+ var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
13767
+ function laneFiles(cwd, fork, head, attributesAt = fork) {
13768
+ const stats = diffNumstat(cwd, fork, head);
13769
+ const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
13770
+ if (!stats || !cosmetic)
13771
+ return null;
13772
+ const files = [
13773
+ ...stats.map((entry) => ({
13774
+ ...entry,
13775
+ ...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
13776
+ cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
13777
+ })),
13778
+ ...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
13779
+ ];
13780
+ const generated = generatedAt(cwd, attributesAt, files.map((entry) => entry.path));
13781
+ return files.map((entry) => ({
13782
+ ...entry,
13783
+ generated: generated.has(entry.path)
13784
+ }));
13785
+ }
13786
+ function driftFiles(cwd, range) {
13787
+ if (range.fromFork === range.toFork)
13788
+ return laneFiles(cwd, range.from, range.to, range.toFork);
13789
+ const drifted = ownChangeDrift(cwd, range);
13790
+ const stats = drifted ? diffNumstat(cwd, range.from, range.to) : null;
13791
+ if (!drifted || !stats)
13792
+ return null;
13793
+ const byPath = new Map(stats.map((entry) => [entry.path, entry]));
13794
+ const generated = generatedAt(cwd, range.toFork, drifted);
13795
+ return drifted.map((file) => ({
13796
+ path: file,
13797
+ added: byPath.get(file)?.added ?? 0,
13798
+ deleted: byPath.get(file)?.deleted ?? 0,
13799
+ ...byPath.get(file)?.binary ? { binary: true } : {},
13800
+ cosmetic: false,
13801
+ generated: generated.has(file)
13802
+ }));
13803
+ }
13804
+ function branchLane(cwd, options) {
13805
+ const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
13806
+ const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
13807
+ const head = options.workingTree ? null : options.head;
13808
+ const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
13809
+ const files = fork ? laneFiles(cwd, fork, head) : null;
13810
+ const riskTier = options.judgment?.riskTier ?? null;
13811
+ if (!fork || !files) {
13812
+ const high = riskTier === "high";
13813
+ return {
13814
+ lane: high ? "full" : "focused",
13815
+ reasons: [
13816
+ `the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
13817
+ ...high ? ["the task judgment records high risk"] : []
13818
+ ],
13819
+ lines: 0,
13820
+ files: 0,
13821
+ riskFiles: [],
13822
+ base,
13823
+ head: options.head
13824
+ };
13825
+ }
13826
+ const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
13827
+ return {
13828
+ ...computeLane({
13829
+ files,
13830
+ riskTier,
13831
+ riskPaths: [...repoRisk, ...options.riskPaths ?? []]
13832
+ }),
13833
+ base,
13834
+ head: options.head
13835
+ };
13836
+ }
13837
+ var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
13838
+
13839
+ // packages/workit-core/src/core/policy/carry.ts
13840
+ var TEST_ONLY_KINDS = new Set(["doc", "cosmetic", "generated", "test"]);
13841
+ function verdictDrift(cwd, input) {
13842
+ const since = input.from;
13843
+ const stale = (reason, files = null, focused = false) => ({
13844
+ carries: false,
13845
+ since,
13846
+ files,
13847
+ focused,
13848
+ reason
13849
+ });
13850
+ const fromFork = mergeBase(cwd, input.fromBase, input.from);
13851
+ const toFork = mergeBase(cwd, input.toBase, input.to);
13852
+ const files = fromFork && toFork ? driftFiles(cwd, { from: input.from, to: input.to, fromFork, toFork }) : null;
13853
+ if (!fromFork || !toFork || !files)
13854
+ return stale(`the change since ${since.slice(0, 12)} cannot be read`);
13855
+ const riskPaths = [fromFork, toFork].flatMap((fork) => parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? ""));
13856
+ const lane = computeLane({ files, riskPaths });
13857
+ const paths = files.map((file) => file.path);
13858
+ if (lane.lane === "none")
13859
+ return { carries: true, rule: "lane-none", since, files: paths };
13860
+ if (input.kind === "unit" && lane.lane === "self" && files.every((file) => TEST_ONLY_KINDS.has(kindOf(file))))
13861
+ return { carries: true, rule: "tests-only", since, files: paths };
13862
+ return stale(lane.reasons[0] ?? `lane ${lane.lane}`, paths, lane.lane === "self");
13863
+ }
12985
13864
 
12986
13865
  // packages/workit-core/src/hooks/standing-signal.ts
12987
13866
  import { createHash as createHash10 } from "node:crypto";
12988
13867
  import fs12 from "node:fs";
12989
- import path14 from "node:path";
13868
+ import path15 from "node:path";
12990
13869
  var gitStoreDir = (cwd) => {
12991
13870
  try {
12992
- for (let dir = path14.resolve(cwd);; dir = path14.dirname(dir)) {
12993
- const dotGit = path14.join(dir, ".git");
13871
+ for (let dir = path15.resolve(cwd);; dir = path15.dirname(dir)) {
13872
+ const dotGit = path15.join(dir, ".git");
12994
13873
  if (fs12.existsSync(dotGit)) {
12995
13874
  let gitDir = dotGit;
12996
13875
  if (fs12.statSync(dotGit).isFile()) {
12997
13876
  const target = /^gitdir:\s*(.+)$/mu.exec(fs12.readFileSync(dotGit, "utf8"))?.[1]?.trim();
12998
13877
  if (!target)
12999
13878
  return null;
13000
- gitDir = path14.resolve(dir, target);
13001
- const common = path14.join(gitDir, "commondir");
13879
+ gitDir = path15.resolve(dir, target);
13880
+ const common = path15.join(gitDir, "commondir");
13002
13881
  if (fs12.existsSync(common))
13003
- gitDir = path14.resolve(gitDir, fs12.readFileSync(common, "utf8").trim());
13882
+ gitDir = path15.resolve(gitDir, fs12.readFileSync(common, "utf8").trim());
13004
13883
  }
13005
- return path14.join(gitDir, "workit");
13884
+ return path15.join(gitDir, "workit");
13006
13885
  }
13007
- if (path14.dirname(dir) === dir)
13886
+ if (path15.dirname(dir) === dir)
13008
13887
  return null;
13009
13888
  }
13010
13889
  } catch {
13011
13890
  return null;
13012
13891
  }
13013
13892
  };
13014
- var standingRevFile = (store) => path14.join(store, "standing", "rev");
13015
- var standingSeenFile = (store, session, agentId) => path14.join(store, "standing", "seen", `${createHash10("sha256").update(`${session}\x00${agentId ?? ""}`).digest("hex").slice(0, 32)}.json`);
13893
+ var standingRevFile = (store) => path15.join(store, "standing", "rev");
13894
+ var standingSeenFile = (store, session, agentId) => path15.join(store, "standing", "seen", `${createHash10("sha256").update(`${session}\x00${agentId ?? ""}`).digest("hex").slice(0, 32)}.json`);
13016
13895
  var readStandingRev = (store) => {
13017
13896
  try {
13018
13897
  return fs12.readFileSync(standingRevFile(store), "utf8").trim() || null;
@@ -13033,7 +13912,7 @@ var readStandingSeen = (file) => {
13033
13912
  };
13034
13913
  var writeStandingSeen = (file, seen) => {
13035
13914
  try {
13036
- fs12.mkdirSync(path14.dirname(file), { recursive: true });
13915
+ fs12.mkdirSync(path15.dirname(file), { recursive: true });
13037
13916
  fs12.writeFileSync(file, JSON.stringify(seen));
13038
13917
  } catch {}
13039
13918
  };
@@ -13083,52 +13962,19 @@ var git4 = (cwd, args) => {
13083
13962
  const value = run.ok ? run.stdout.trim() : "";
13084
13963
  return value ? value : null;
13085
13964
  };
13086
- var safeRef = (value) => value.length > 0 && !value.startsWith("-");
13965
+ var safeRef2 = (value) => value.length > 0 && !value.startsWith("-");
13087
13966
  function resolveCommit(cwd, ref) {
13088
- if (!safeRef(ref))
13967
+ if (!safeRef2(ref))
13089
13968
  return null;
13090
13969
  return git4(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
13091
13970
  }
13092
13971
  function branchHead(cwd, branch) {
13093
- if (!safeRef(branch))
13972
+ if (!safeRef2(branch))
13094
13973
  return null;
13095
13974
  return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
13096
13975
  }
13097
- var trackTrunk = (cwd) => {
13098
- try {
13099
- const resolved = vcsConfig("resolve", cwd);
13100
- if (resolved.ok === false || !resolved.releaseTrack || resolved.releaseTrack.blocking)
13101
- return null;
13102
- const target = String(resolved.defaultTargetBranch ?? "").trim();
13103
- return target && safeRef(target) ? target : null;
13104
- } catch {
13105
- return null;
13106
- }
13107
- };
13108
- function defaultBase(cwd) {
13109
- const trunk = trackTrunk(cwd);
13110
- if (trunk) {
13111
- for (const candidate of [`origin/${trunk}`, trunk])
13112
- if (resolveCommit(cwd, candidate))
13113
- return candidate;
13114
- }
13115
- const originHead = git4(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]);
13116
- if (originHead && resolveCommit(cwd, originHead))
13117
- return originHead;
13118
- for (const candidate of [
13119
- "origin/main",
13120
- "origin/master",
13121
- "main",
13122
- "master",
13123
- "origin/develop",
13124
- "develop"
13125
- ])
13126
- if (resolveCommit(cwd, candidate))
13127
- return candidate;
13128
- return null;
13129
- }
13130
13976
  function diffHash(cwd, base, head) {
13131
- if (!safeRef(base) || !safeRef(head))
13977
+ if (!safeRef2(base) || !safeRef2(head))
13132
13978
  return null;
13133
13979
  const diff = gitRun2(cwd, [
13134
13980
  "diff",
@@ -13160,14 +14006,14 @@ function diffHash(cwd, base, head) {
13160
14006
  return `sha256:${hash.digest("hex")}`;
13161
14007
  }
13162
14008
  function storeRoot(cwd) {
13163
- const location = resolveStore(path15.resolve(cwd));
14009
+ const location = resolveStore(path16.resolve(cwd));
13164
14010
  if (location instanceof Error)
13165
14011
  return err("unavailable", location.message.replace(/; fix the repository.*$/u, ""), "fix the repository (git status should work here) or run from a non-git directory");
13166
14012
  return { ok: true, value: { root: location.dir, shared: location.shared } };
13167
14013
  }
13168
14014
  function ledgerPath(cwd) {
13169
14015
  const root = storeRoot(cwd);
13170
- return root.ok ? { ok: true, value: path15.join(root.value.root, "ledger", "ledger.jsonl") } : root;
14016
+ return root.ok ? { ok: true, value: path16.join(root.value.root, "ledger", "ledger.jsonl") } : root;
13171
14017
  }
13172
14018
  var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
13173
14019
  var NETWORK_FS_TYPES = new Set([
@@ -13197,13 +14043,13 @@ var LOCK_STALE_MS = 30000;
13197
14043
  var LOCK_OWNER = "owner";
13198
14044
  var lockToken = (lock) => {
13199
14045
  try {
13200
- return fs13.readFileSync(path15.join(lock, LOCK_OWNER), "utf8");
14046
+ return fs13.readFileSync(path16.join(lock, LOCK_OWNER), "utf8");
13201
14047
  } catch {
13202
14048
  return null;
13203
14049
  }
13204
14050
  };
13205
14051
  var lockAgeMs = (lock) => {
13206
- for (const target of [path15.join(lock, LOCK_OWNER), lock]) {
14052
+ for (const target of [path16.join(lock, LOCK_OWNER), lock]) {
13207
14053
  try {
13208
14054
  return Date.now() - fs13.statSync(target).mtimeMs;
13209
14055
  } catch {}
@@ -13220,7 +14066,7 @@ var ledgerLock = {
13220
14066
  throw error;
13221
14067
  }
13222
14068
  const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
13223
- fs13.writeFileSync(path15.join(lock, LOCK_OWNER), token);
14069
+ fs13.writeFileSync(path16.join(lock, LOCK_OWNER), token);
13224
14070
  return token;
13225
14071
  },
13226
14072
  release(lock, token) {
@@ -13353,8 +14199,8 @@ function appendLine(file, build, options) {
13353
14199
  }
13354
14200
  };
13355
14201
  try {
13356
- fs13.mkdirSync(path15.dirname(file), { recursive: true });
13357
- if (!options.forceLock && !onNetworkFs(path15.dirname(file)))
14202
+ fs13.mkdirSync(path16.dirname(file), { recursive: true });
14203
+ if (!options.forceLock && !onNetworkFs(path16.dirname(file)))
13358
14204
  return write();
13359
14205
  const locked = withLedgerLock(file, write);
13360
14206
  return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
@@ -13588,7 +14434,7 @@ function verdictBasis(verdict, current) {
13588
14434
  }
13589
14435
  var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
13590
14436
  function trailerSessions(cwd, base, head) {
13591
- if (!base || !head || !safeRef(base) || !safeRef(head))
14437
+ if (!base || !head || !safeRef2(base) || !safeRef2(head))
13592
14438
  return [];
13593
14439
  const out = git4(cwd, [
13594
14440
  "log",
@@ -13601,7 +14447,7 @@ var RENAMED = /^branch: renamed refs\/heads\/(.+) to refs\/heads\/(.+)$/iu;
13601
14447
  function branchScope(cwd, branch) {
13602
14448
  const renamedAt = new Map;
13603
14449
  let since = Number.NEGATIVE_INFINITY;
13604
- if (safeRef(branch)) {
14450
+ if (safeRef2(branch)) {
13605
14451
  const log = git4(cwd, [
13606
14452
  "log",
13607
14453
  "-g",
@@ -13746,12 +14592,29 @@ function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
13746
14592
  }
13747
14593
  return effective;
13748
14594
  }
13749
- function restackCarries(rows, branch, from, to) {
14595
+ var restackExact = new WeakMap;
14596
+ function restackCarries(exactIn, rows, branch, from, to) {
13750
14597
  if (!from || !to)
13751
14598
  return false;
14599
+ const exact = (row, fromHead, head) => {
14600
+ if (exactIn === null)
14601
+ return true;
14602
+ const cwd = exactIn;
14603
+ let known = restackExact.get(row);
14604
+ if (known === undefined) {
14605
+ known = typeof row.fromBase === "string" && typeof row.toBase === "string" && sameOwnChange(cwd, {
14606
+ from: fromHead,
14607
+ to: head,
14608
+ fromBase: row.fromBase,
14609
+ toBase: row.toBase
14610
+ });
14611
+ restackExact.set(row, known);
14612
+ }
14613
+ return known;
14614
+ };
13752
14615
  const edges = new Map;
13753
14616
  for (const row of rows)
13754
- if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
14617
+ if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head && exact(row, row.fromHead, row.head))
13755
14618
  edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
13756
14619
  const seen = new Set([from]);
13757
14620
  const queue = [from];
@@ -13785,9 +14648,57 @@ function checkVerdicts(cwd, branch, ledgerRows) {
13785
14648
  return key;
13786
14649
  };
13787
14650
  const keyFor = (base) => keysAt(base, head);
13788
- const basisOf = (row) => {
14651
+ const driftCache = new Map;
14652
+ const driftOf = (row) => {
14653
+ if (driftCache.has(row))
14654
+ return driftCache.get(row) ?? null;
14655
+ let drift = null;
14656
+ const toBase = row.base ?? fallbackBase;
14657
+ const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
14658
+ const fromBase = row.baseSha ?? toBaseSha;
14659
+ if (row.head && head && row.head !== head && toBaseSha && fromBase && !untrusted(reasonsOf(row)))
14660
+ drift = verdictDrift(cwd, {
14661
+ from: row.head,
14662
+ to: head,
14663
+ fromBase,
14664
+ toBase: toBaseSha,
14665
+ kind: typeof row.kind === "string" ? row.kind : "review"
14666
+ });
14667
+ driftCache.set(row, drift);
14668
+ return drift;
14669
+ };
14670
+ const exactCache = new Map;
14671
+ const exactCarry = (row) => {
14672
+ if (FAILING.has(String(row.result)))
14673
+ return true;
14674
+ let exact = exactCache.get(row);
14675
+ if (exact === undefined) {
14676
+ const toBase = row.base ?? fallbackBase;
14677
+ const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
14678
+ const fromBase = row.baseSha ?? toBaseSha;
14679
+ exact = Boolean(row.head && head && toBaseSha && fromBase && sameOwnChange(cwd, { from: row.head, to: head, fromBase, toBase: toBaseSha }));
14680
+ exactCache.set(row, exact);
14681
+ }
14682
+ return exact;
14683
+ };
14684
+ const keyBasis = (row) => {
13789
14685
  const basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
13790
- return basis === "stale" && restackCarries(rows, branch, row.head, head) ? "carried" : basis;
14686
+ return basis === "carried" && !exactCarry(row) ? "stale" : basis;
14687
+ };
14688
+ const restacked = (row) => restackCarries(FAILING.has(String(row.result)) ? null : cwd, rows, branch, row.head, head);
14689
+ const carryOf = (row) => {
14690
+ if (keyBasis(row) !== "stale")
14691
+ return null;
14692
+ if (restacked(row))
14693
+ return null;
14694
+ const drift = driftOf(row);
14695
+ return drift?.carries ? { rule: drift.rule, since: drift.since, files: drift.files } : null;
14696
+ };
14697
+ const basisOf = (row) => {
14698
+ const basis = keyBasis(row);
14699
+ if (basis !== "stale")
14700
+ return basis;
14701
+ return restacked(row) || carryOf(row) ? "carried" : basis;
13791
14702
  };
13792
14703
  const sameCode = (failed, row) => {
13793
14704
  if (failed.head && failed.head === row.head)
@@ -13795,7 +14706,7 @@ function checkVerdicts(cwd, branch, ledgerRows) {
13795
14706
  const base = failed.base ?? fallbackBase;
13796
14707
  const stored = (row.base ?? fallbackBase) === base && row.baseSha !== null && row.baseSha === failed.baseSha;
13797
14708
  const keys = stored ? { patchId: row.patchId, diffHash: row.diffHash } : keysAt(base, row.head);
13798
- return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
14709
+ return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(null, rows, branch, failed.head, row.head);
13799
14710
  };
13800
14711
  const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
13801
14712
  const failureAuthorCache = new Map;
@@ -13851,6 +14762,7 @@ function checkVerdicts(cwd, branch, ledgerRows) {
13851
14762
  independent: independence.length === 0,
13852
14763
  accepted: reasons.length === 0,
13853
14764
  reasons,
14765
+ carry: basis === "carried" ? carryOf(row) : null,
13854
14766
  verdict: row
13855
14767
  });
13856
14768
  if (reasons.length === 0) {
@@ -13879,7 +14791,12 @@ function checkVerdicts(cwd, branch, ledgerRows) {
13879
14791
  verdict: failing?.verdict ?? null,
13880
14792
  reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
13881
14793
  };
13882
- const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
14794
+ 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))));
14795
+ const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
14796
+ const carryEntry = verdicts.find((entry) => entry.verdict === accepted.verdict && entry.accepted) ?? newestCurrent;
14797
+ const lastPass = accepted.accepted || failing || tampered.length ? undefined : verdicts.findLast((entry) => !entry.current && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
14798
+ const drift = lastPass ? driftOf(lastPass.verdict) : null;
14799
+ const reverify = drift && !drift.carries && drift.focused && drift.files ? { scope: "focused", since: drift.since, files: drift.files } : null;
13883
14800
  return {
13884
14801
  branch,
13885
14802
  head,
@@ -13888,6 +14805,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
13888
14805
  base: newestCurrent?.verdict.base ?? fallbackBase,
13889
14806
  patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
13890
14807
  current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
14808
+ carry: carryEntry?.carry ?? null,
14809
+ reverify,
13891
14810
  accepted,
13892
14811
  verdicts,
13893
14812
  authors: [...authors].toSorted(),
@@ -13929,6 +14848,7 @@ var RULES = {
13929
14848
  self: "verdict:self",
13930
14849
  nonAuthor: "verdict:non-author",
13931
14850
  verified: "verdict:verified",
14851
+ lane: "verdict:lane",
13932
14852
  product: "decision:product",
13933
14853
  plan: "plan"
13934
14854
  };
@@ -13951,6 +14871,7 @@ var normalizedScope = (scope) => ({
13951
14871
  function deriveRequirements(judgment, scope, constraints, verification = "self") {
13952
14872
  const out = [];
13953
14873
  const base = { scope, dependentAction: null };
14874
+ const proportional = verification === "proportional";
13954
14875
  if (judgment.behaviorChange)
13955
14876
  out.push(derived2({
13956
14877
  ...base,
@@ -13961,7 +14882,17 @@ function deriveRequirements(judgment, scope, constraints, verification = "self")
13961
14882
  before: "close",
13962
14883
  acceptanceAllowed: false
13963
14884
  }));
13964
- if (judgment.behaviorChange && judgment.riskTier === "normal")
14885
+ if (proportional && judgment.riskTier === "normal")
14886
+ out.push(derived2({
14887
+ ...base,
14888
+ ruleId: RULES.lane,
14889
+ dimension: "review",
14890
+ 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.",
14891
+ 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.",
14892
+ before: "close",
14893
+ acceptanceAllowed: false
14894
+ }));
14895
+ if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
13965
14896
  out.push(verification === "independent" ? derived2({
13966
14897
  ...base,
13967
14898
  ruleId: RULES.nonAuthor,
@@ -14064,7 +14995,7 @@ var productDecision = (task, ledger) => {
14064
14995
  };
14065
14996
  };
14066
14997
  var planRecorded = (task, root) => {
14067
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path16.resolve(root, ref.path)));
14998
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(path17.resolve(root, ref.path)));
14068
14999
  if (cited)
14069
15000
  return {
14070
15001
  met: true,
@@ -14078,14 +15009,30 @@ var planRecorded = (task, root) => {
14078
15009
  decisionIds: []
14079
15010
  };
14080
15011
  };
14081
- var verdict = (requirement, root, ledger) => {
15012
+ var workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) });
15013
+ var laneVerdict = (task, root, ledger) => {
15014
+ if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
15015
+ return {
15016
+ met: true,
15017
+ reason: "no branch to read a lane from, and the judgment records no behavior change",
15018
+ decisionIds: []
15019
+ };
15020
+ const lane = workingLane(task, root);
15021
+ const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
15022
+ const need = laneNeed(lane.lane);
15023
+ if (need === "none")
15024
+ return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
15025
+ const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
15026
+ return { ...status, reason: `${label}: ${status.reason}` };
15027
+ };
15028
+ var verdict = (ruleId, root, ledger) => {
14082
15029
  const branch = ledger.branch();
14083
15030
  if (!branch)
14084
15031
  return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
14085
15032
  const check = checkVerdicts(root, branch, ledger.rows());
14086
15033
  const failing = check.accepted.reasons.includes("failing_verdict");
14087
15034
  const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
14088
- 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)));
15035
+ 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)));
14089
15036
  if (met)
14090
15037
  return {
14091
15038
  met: true,
@@ -14093,7 +15040,7 @@ var verdict = (requirement, root, ledger) => {
14093
15040
  decisionIds: []
14094
15041
  };
14095
15042
  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, " ");
14096
- 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>\``;
15043
+ 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>\``;
14097
15044
  return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
14098
15045
  };
14099
15046
  function derivedRuleStatus(task, requirement, root, ledger) {
@@ -14105,7 +15052,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
14105
15052
  case RULES.self:
14106
15053
  case RULES.nonAuthor:
14107
15054
  case RULES.verified:
14108
- return verdict(requirement, root, ledger);
15055
+ return verdict(requirement.ruleId, root, ledger);
15056
+ case RULES.lane:
15057
+ return laneVerdict(task, root, ledger);
14109
15058
  default:
14110
15059
  return null;
14111
15060
  }
@@ -14124,14 +15073,14 @@ function writeBlockers(task, root) {
14124
15073
 
14125
15074
  // packages/workit-core/src/core/task-evaluation.ts
14126
15075
  var digestBytes = (value) => createHash12("sha256").update(value).digest("hex");
14127
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path17.sep}`);
14128
- var relative = (root, candidate) => path17.relative(root, candidate).split(path17.sep).join("/") || ".";
15076
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path18.sep}`);
15077
+ var relative = (root, candidate) => path18.relative(root, candidate).split(path18.sep).join("/") || ".";
14129
15078
  var canonicalPath = (value) => {
14130
15079
  if (typeof value !== "string" || !value)
14131
15080
  return null;
14132
15081
  if (value.includes("\\") || value.split("/").includes(".."))
14133
15082
  return null;
14134
- const normalized = path17.posix.normalize(value);
15083
+ const normalized = path18.posix.normalize(value);
14135
15084
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
14136
15085
  return null;
14137
15086
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -14179,14 +15128,14 @@ var gitPaths = (root) => {
14179
15128
  git: !/not a git repository/i.test(stderr)
14180
15129
  };
14181
15130
  }
14182
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")) : [];
15131
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")) : [];
14183
15132
  const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
14184
15133
  if (stagedDeleted.status !== 0) {
14185
15134
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
14186
15135
  if (!/not a git repository/i.test(stderr))
14187
15136
  return { paths, uncertain: true, git: true };
14188
15137
  } else if (stagedDeleted.stdout) {
14189
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")));
15138
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")));
14190
15139
  }
14191
15140
  return { paths: [...new Set(paths)], uncertain: false, git: true };
14192
15141
  };
@@ -14201,7 +15150,7 @@ var walk = (root, directory, output) => {
14201
15150
  for (const name of names) {
14202
15151
  if (name === ".git" || name === ".workit")
14203
15152
  continue;
14204
- const target = path17.join(directory, name);
15153
+ const target = path18.join(directory, name);
14205
15154
  let stat;
14206
15155
  try {
14207
15156
  stat = fs14.lstatSync(target);
@@ -14219,12 +15168,12 @@ var walk = (root, directory, output) => {
14219
15168
  var scopeRoots = (root, scope) => {
14220
15169
  const roots = [];
14221
15170
  for (const item of scope.paths.length ? scope.paths : ["."]) {
14222
- const target = path17.resolve(root, item);
15171
+ const target = path18.resolve(root, item);
14223
15172
  if (!inside(root, target))
14224
15173
  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` }] });
14225
15174
  let ancestor = target;
14226
15175
  while (!fs14.existsSync(ancestor) && ancestor !== root)
14227
- ancestor = path17.dirname(ancestor);
15176
+ ancestor = path18.dirname(ancestor);
14228
15177
  try {
14229
15178
  if (!inside(root, fs14.realpathSync(ancestor)))
14230
15179
  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` }] });
@@ -14276,7 +15225,7 @@ function captureCandidate(root, scope, environment = []) {
14276
15225
  for (const item of [...names].toSorted()) {
14277
15226
  if (!scopeMatches(item, normalizedScope))
14278
15227
  continue;
14279
- const target = path17.join(checkout, item);
15228
+ const target = path18.join(checkout, item);
14280
15229
  try {
14281
15230
  const stat = fs14.lstatSync(target);
14282
15231
  if (stat.isSymbolicLink()) {
@@ -14460,23 +15409,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
14460
15409
  continue;
14461
15410
  if (!reference.digest)
14462
15411
  return failure2("invalid_input", "document references require a byte digest");
14463
- const target = path17.resolve(root, reference.path);
14464
- if (target !== root && !target.startsWith(`${root}${path17.sep}`))
15412
+ const target = path18.resolve(root, reference.path);
15413
+ if (target !== root && !target.startsWith(`${root}${path18.sep}`))
14465
15414
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
14466
15415
  fields: [
14467
15416
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
14468
15417
  ]
14469
15418
  });
14470
15419
  try {
14471
- const below = path17.relative(root, target);
15420
+ const below = path18.relative(root, target);
14472
15421
  let current = root;
14473
- for (const segment of below.split(path17.sep)) {
15422
+ for (const segment of below.split(path18.sep)) {
14474
15423
  if (!segment)
14475
15424
  continue;
14476
- current = path17.join(current, segment);
15425
+ current = path18.join(current, segment);
14477
15426
  const stat = fs14.lstatSync(current);
14478
15427
  const real = fs14.realpathSync(current);
14479
- if (real !== root && !real.startsWith(`${root}${path17.sep}`))
15428
+ if (real !== root && !real.startsWith(`${root}${path18.sep}`))
14480
15429
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
14481
15430
  fields: [
14482
15431
  {
@@ -14913,9 +15862,9 @@ Autonomy contract:
14913
15862
  Prose is a note: only a configured check the CLI ran (\`workit check <name>\`)
14914
15863
  satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
14915
15864
  - Author is not verifier: your own verdict is \`--self\`, shown as
14916
- self-reviewed, never verified. High risk, \`verification: independent\` and
14917
- merge need a verifier with its own host-derived session (a subagent's
14918
- \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
15865
+ self-reviewed, never verified. High risk, a focused or full lane and
15866
+ merge under \`verification: self|independent\` need a verifier with its
15867
+ own host-derived session (a subagent's \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
14919
15868
  - A local-commit endpoint does not imply PR readiness, and a local commit alone
14920
15869
  is not evidence of a requested remote push. Before reporting delivery,
14921
15870
  reconcile every requested item and observe it (\`workit verify-delivery\`).
@@ -14923,7 +15872,7 @@ Autonomy contract:
14923
15872
  CLI first: if a step has one right answer, use the \`workit\` verb instead of
14924
15873
  hand-running git, gh or glab (\`workit git|pr|ci|check|help\`;
14925
15874
  else \`npx -y @brainervirus/workit-cli\`).
14926
- Other checkout: \`workit --cwd <dir> <verb>\` (Codex: \`<verb> --cwd <dir>\`); one workit
15875
+ Other checkout: \`workit <verb> --cwd <dir> …\` (verb first: host rules match it); one workit
14927
15876
  verb per shell call, never \`cd <dir> && workit\`. zsh-safe: no \`echo =====\`
14928
15877
  (\`printf '%s\\n' ---\`), quote globs and URLs, no variable named \`status\` or \`path\`.
14929
15878
  Add Co-Authored-By via \`workit git commit --trailer "K: v" -- <paths>\`, never
@@ -14935,7 +15884,7 @@ authorizes an effect. Judge work once with
14935
15884
  \`workit policy assess --judge risk= behavior= product-choice= plan=\`.
14936
15885
  Across repositories, bind items to checkout, branch and endpoint, resolve
14937
15886
  competing targets before a mutation, and reconcile an uncertain
14938
- external effect before a retry.
15887
+ effect before a retry.
14939
15888
 
14940
15889
  Mid-task: never silently drop or resume an objective (workit-continue).
14941
15890
 
@@ -15078,20 +16027,20 @@ var exportDigest = (bundle) => sha256({
15078
16027
  });
15079
16028
  // packages/workit-core/src/core/route-intent.ts
15080
16029
  import { spawnSync as spawnSync12 } from "node:child_process";
15081
- import { existsSync as existsSync6 } from "node:fs";
15082
- import path20 from "node:path";
16030
+ import { existsSync as existsSync7 } from "node:fs";
16031
+ import path21 from "node:path";
15083
16032
 
15084
16033
  // packages/workit-core/src/hooks/raw-git.ts
15085
16034
  import { spawnSync as spawnSync11 } from "node:child_process";
15086
16035
  import { createHash as createHash14 } from "node:crypto";
15087
16036
  import fs16 from "node:fs";
15088
16037
  import os4 from "node:os";
15089
- import path19 from "node:path";
16038
+ import path20 from "node:path";
15090
16039
 
15091
16040
  // packages/workit-core/src/hooks/session-marker.ts
15092
16041
  import { createHash as createHash13 } from "node:crypto";
15093
16042
  import fs15 from "node:fs";
15094
- import path18 from "node:path";
16043
+ import path19 from "node:path";
15095
16044
  var sessionMarkerFile = (input, session, store) => {
15096
16045
  let dir = store;
15097
16046
  if (dir === undefined) {
@@ -15101,7 +16050,7 @@ var sessionMarkerFile = (input, session, store) => {
15101
16050
  dir = location.dir;
15102
16051
  }
15103
16052
  const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
15104
- return path18.join(dir, "hooks", `skills-${name}.json`);
16053
+ return path19.join(dir, "hooks", `skills-${name}.json`);
15105
16054
  };
15106
16055
  var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
15107
16056
  var EMPTY = { nudged: [], loaded: [], raw: [], stops: [], judges: [] };
@@ -15128,7 +16077,7 @@ var parseMarker = (text) => {
15128
16077
  };
15129
16078
  var readSessionMarker = (file) => parseMarker(readRaw(file));
15130
16079
  var writeAtomic = (file, marker) => {
15131
- fs15.mkdirSync(path18.dirname(file), { recursive: true });
16080
+ fs15.mkdirSync(path19.dirname(file), { recursive: true });
15132
16081
  const temp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
15133
16082
  try {
15134
16083
  fs15.writeFileSync(temp, JSON.stringify(marker));
@@ -15415,6 +16364,18 @@ var substitutions = (body) => {
15415
16364
  };
15416
16365
  var PLAIN_POSIX = /[^\s'"\\`$(){},<>|;&#]+/uy;
15417
16366
  var PLAIN_POWERSHELL = /[^\s'"`(){}<>|;&#]+/uy;
16367
+ var expandsInQuotes = (body, dialect) => {
16368
+ const escape = dialect === "posix" ? "\\" : "`";
16369
+ for (let index = 0;index < body.length; index++) {
16370
+ const char = body[index];
16371
+ if (char === escape)
16372
+ index++;
16373
+ else if (char === "$" || dialect === "posix" && char === "`")
16374
+ return true;
16375
+ }
16376
+ return false;
16377
+ };
16378
+ var EXPANDING_RUN = /[*?[]/u;
15418
16379
  var arithmeticEnd = (command, start) => {
15419
16380
  const end = substitutionEnd(command, start);
15420
16381
  return command[end - 1] === ")" && end > start + 2 ? end : 0;
@@ -15433,6 +16394,7 @@ var tokenize = (command, dialect) => {
15433
16394
  let current = { words: [], redirects: [] };
15434
16395
  let word = "";
15435
16396
  let inWord = false;
16397
+ let expanding = false;
15436
16398
  let pending = null;
15437
16399
  const heredocs = [];
15438
16400
  const escape = posix ? "\\" : "`";
@@ -15447,11 +16409,16 @@ var tokenize = (command, dialect) => {
15447
16409
  heredocs.push({ delimiter: text.replace(/^-/, ""), owner: current });
15448
16410
  else if (pending === "herestring")
15449
16411
  (current.stdin ??= []).push(text);
15450
- else if (pending !== "input")
16412
+ else if (pending !== "input") {
16413
+ const at = current.words.length;
15451
16414
  current.words.push(...MARKERS.test(word) ? expandBraces(word) : [word]);
16415
+ if (expanding || !posix && word.startsWith("@"))
16416
+ (current.expands ??= []).push(...current.words.slice(at).map((_, i) => at + i));
16417
+ }
15452
16418
  pending = null;
15453
16419
  word = "";
15454
16420
  inWord = false;
16421
+ expanding = false;
15455
16422
  };
15456
16423
  const flushSegment = () => {
15457
16424
  flushWord();
@@ -15466,6 +16433,7 @@ var tokenize = (command, dialect) => {
15466
16433
  };
15467
16434
  const substitution = (inner, text) => {
15468
16435
  (current.subs ??= []).push(inner);
16436
+ expanding = true;
15469
16437
  word += text;
15470
16438
  inWord = true;
15471
16439
  };
@@ -15476,6 +16444,8 @@ var tokenize = (command, dialect) => {
15476
16444
  if (run) {
15477
16445
  word += run[0];
15478
16446
  inWord = true;
16447
+ if (EXPANDING_RUN.test(run[0]))
16448
+ expanding = true;
15479
16449
  index += run[0].length - 1;
15480
16450
  continue;
15481
16451
  }
@@ -15504,6 +16474,8 @@ var tokenize = (command, dialect) => {
15504
16474
  const body = command.slice(index + 1, end < 0 ? undefined : end);
15505
16475
  if (posix && char === '"' && /\$\(|`/.test(body))
15506
16476
  (current.subs ??= []).push(...substitutions(body));
16477
+ if (char === '"' && expandsInQuotes(body, dialect))
16478
+ expanding = true;
15507
16479
  word += unquote(body, char, dialect);
15508
16480
  inWord = true;
15509
16481
  index = end < 0 ? command.length : end;
@@ -15549,6 +16521,8 @@ var tokenize = (command, dialect) => {
15549
16521
  if (char === "(" && inWord && word.endsWith("$")) {
15550
16522
  word = word.slice(0, -1);
15551
16523
  inWord = word !== "";
16524
+ if (!inWord)
16525
+ expanding = false;
15552
16526
  }
15553
16527
  group(char === "(" ? "open" : "close");
15554
16528
  } else if (!posix && (char === "{" || char === "}")) {
@@ -15602,6 +16576,8 @@ var tokenize = (command, dialect) => {
15602
16576
  else if (posix && char === "}" && word.includes(OPEN))
15603
16577
  word += CLOSE;
15604
16578
  else {
16579
+ if (char === "$")
16580
+ expanding = true;
15605
16581
  word += char;
15606
16582
  inWord = true;
15607
16583
  }
@@ -15659,14 +16635,14 @@ var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
15659
16635
  var ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
15660
16636
  var MAX_DEPTH = 4;
15661
16637
  var MAX_EXPANSIONS = 16;
15662
- var isAbsolute = (value) => path19.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
16638
+ var isAbsolute = (value) => path20.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
15663
16639
  var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
15664
16640
  var joinDir = (base, raw) => {
15665
16641
  const next = raw.replace(HERE, ".");
15666
16642
  if (base === undefined || /[$`*?]/.test(next) || next === "-")
15667
16643
  return;
15668
- const expanded = next === "~" || next.startsWith("~/") ? path19.join(os4.homedir(), next.slice(1)) : next;
15669
- return base === null || isAbsolute(expanded) ? expanded : path19.join(base, expanded);
16644
+ const expanded = next === "~" || next.startsWith("~/") ? path20.join(os4.homedir(), next.slice(1)) : next;
16645
+ return base === null || isAbsolute(expanded) ? expanded : path20.join(base, expanded);
15670
16646
  };
15671
16647
  var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
15672
16648
  var cdTarget = (words) => {
@@ -16046,8 +17022,17 @@ function walk2(command, dialect, depth, seen) {
16046
17022
  visit(next, undefined, hops + 1);
16047
17023
  };
16048
17024
  const wrapped = seen ? null : workitCheckTail(words);
16049
- if (wrapped)
16050
- return "script" in wrapped ? nested(wrapped.script, "posix") : runs([wrapped.argv]);
17025
+ if (wrapped) {
17026
+ const outer = dir;
17027
+ if (wrapped.cwd !== null)
17028
+ dir = joinDir(dir, wrapped.cwd);
17029
+ if ("script" in wrapped)
17030
+ nested(wrapped.script, "posix");
17031
+ else
17032
+ runs([wrapped.argv]);
17033
+ dir = outer;
17034
+ return;
17035
+ }
16051
17036
  if (CD.has(name)) {
16052
17037
  const target = cdTarget(words);
16053
17038
  dir = target === null ? os4.homedir() : joinDir(dir, target);
@@ -16148,14 +17133,14 @@ function workitCheckTail(words) {
16148
17133
  return null;
16149
17134
  } else if (!/^workit(?:\.exe|\.cmd)?$/.test(commandName(words[0])))
16150
17135
  return null;
16151
- const args = words.slice(at + 1);
17136
+ const { rest: args, cwd } = skipWorkitGlobals(words.slice(at + 1));
16152
17137
  if (args[0] !== "check" && !(args[0] === "lock" && args[1] === "run"))
16153
17138
  return null;
16154
17139
  const split = args.indexOf("--");
16155
17140
  const tail = split < 0 ? [] : args.slice(split + 1);
16156
17141
  if (!tail.length)
16157
17142
  return null;
16158
- return args.slice(0, split).includes("--shell") ? { script: tail.join(" ") } : { argv: tail };
17143
+ return args.slice(0, split).includes("--shell") ? { script: tail.join(" "), cwd } : { argv: tail, cwd };
16159
17144
  }
16160
17145
  function simpleCommand(command, dialect = "posix", depth = 0) {
16161
17146
  let segments;
@@ -16216,7 +17201,21 @@ function skipWorkitGlobals(args) {
16216
17201
  } else if (args[at] !== "--json")
16217
17202
  break;
16218
17203
  }
16219
- return { rest: args.slice(at), cwd };
17204
+ const rest = [];
17205
+ for (;at < args.length; at++) {
17206
+ const arg = args[at];
17207
+ if (arg === "--") {
17208
+ rest.push(...args.slice(at));
17209
+ break;
17210
+ }
17211
+ if (arg.startsWith("--cwd="))
17212
+ cwd = arg.slice("--cwd=".length);
17213
+ else if (arg === "--cwd" && args[at + 1] !== undefined)
17214
+ cwd = args[++at];
17215
+ else
17216
+ rest.push(arg);
17217
+ }
17218
+ return { rest, cwd };
16220
17219
  }
16221
17220
  var PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
16222
17221
  var CURRENT_BRANCH = /^\$\(\s*git\s+(?:branch\s+--show-current|rev-parse\s+--abbrev-ref\s+HEAD|symbolic-ref\s+--short\s+HEAD)\s*\)$/;
@@ -16517,17 +17516,17 @@ var headSha2 = (repo) => {
16517
17516
  if (!git || git.reftable)
16518
17517
  return null;
16519
17518
  try {
16520
- const head = fs16.readFileSync(path19.join(git.gitDir, "HEAD"), "utf8").trim();
17519
+ const head = fs16.readFileSync(path20.join(git.gitDir, "HEAD"), "utf8").trim();
16521
17520
  if (/^[0-9a-f]{40,64}$/.test(head))
16522
17521
  return head;
16523
17522
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
16524
17523
  if (!ref)
16525
17524
  return null;
16526
- const common = path19.dirname(repo.location.dir);
17525
+ const common = path20.dirname(repo.location.dir);
16527
17526
  try {
16528
- return fs16.readFileSync(path19.join(common, ref), "utf8").trim() || null;
17527
+ return fs16.readFileSync(path20.join(common, ref), "utf8").trim() || null;
16529
17528
  } catch {}
16530
- const packed = fs16.readFileSync(path19.join(common, "packed-refs"), "utf8");
17529
+ const packed = fs16.readFileSync(path20.join(common, "packed-refs"), "utf8");
16531
17530
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
16532
17531
  } catch {
16533
17532
  return null;
@@ -16615,7 +17614,7 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
16615
17614
  const sub = invocation.args[0];
16616
17615
  if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
16617
17616
  return [invocation];
16618
- const dir = path19.resolve(root, invocation.dir ?? ".");
17617
+ const dir = path20.resolve(root, invocation.dir ?? ".");
16619
17618
  if (!fs16.existsSync(dir))
16620
17619
  return [invocation];
16621
17620
  const config = gitConfig(dir, invocation, cache);
@@ -16638,11 +17637,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
16638
17637
  var normalizedUrl = (url, dir) => {
16639
17638
  const trimmed = url.trim();
16640
17639
  if (/^file:\/\//i.test(trimmed))
16641
- return path19.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
17640
+ return path20.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
16642
17641
  const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
16643
17642
  const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
16644
17643
  if (!scheme && !scp)
16645
- return path19.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
17644
+ return path20.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
16646
17645
  const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
16647
17646
  return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
16648
17647
  };
@@ -16866,7 +17865,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
16866
17865
  const out = [];
16867
17866
  const repos = new Map;
16868
17867
  const repoFor = (relative) => {
16869
- const dir = path19.resolve(cwd, relative ?? ".");
17868
+ const dir = path20.resolve(cwd, relative ?? ".");
16870
17869
  if (!repos.has(dir)) {
16871
17870
  const repo = repoAt(dir);
16872
17871
  repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
@@ -16952,7 +17951,7 @@ var analysisDenial = (error) => ({
16952
17951
  });
16953
17952
  var NEXT_COMMAND = "next";
16954
17953
  var MARKER_TTL_SECONDS = 3600;
16955
- var markerFile = (repo, session, key) => path19.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
17954
+ var markerFile = (repo, session, key) => path20.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
16956
17955
  var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
16957
17956
  function noteRawCommit(input, command, key) {
16958
17957
  try {
@@ -16963,7 +17962,7 @@ function noteRawCommit(input, command, key) {
16963
17962
  if (action.kind !== "commit")
16964
17963
  continue;
16965
17964
  const file = markerFile(repo, input.session.id, key);
16966
- fs16.mkdirSync(path19.dirname(file), { recursive: true });
17965
+ fs16.mkdirSync(path20.dirname(file), { recursive: true });
16967
17966
  fs16.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
16968
17967
  `);
16969
17968
  }
@@ -17194,8 +18193,8 @@ var judgedDir = (root, dir) => {
17194
18193
  return null;
17195
18194
  if (dir === null)
17196
18195
  return root;
17197
- const resolved = path20.resolve(root, dir);
17198
- return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
18196
+ const resolved = path21.resolve(root, dir);
18197
+ return existsSync7(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
17199
18198
  };
17200
18199
  var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
17201
18200
  let judged = false;
@@ -17430,7 +18429,7 @@ var judgeInputSummary = (raw) => {
17430
18429
 
17431
18430
  // packages/workit-core/src/autonomy.ts
17432
18431
  import os5 from "node:os";
17433
- import path21 from "node:path";
18432
+ import path22 from "node:path";
17434
18433
  var accountHome = () => {
17435
18434
  try {
17436
18435
  return os5.userInfo().homedir;
@@ -17439,10 +18438,10 @@ var accountHome = () => {
17439
18438
  }
17440
18439
  };
17441
18440
  var grantsHome = { resolve: accountHome };
17442
- var grantsDir = () => path21.join(grantsHome.resolve(), ".config", "workit");
18441
+ var grantsDir = () => path22.join(grantsHome.resolve(), ".config", "workit");
17443
18442
  var grantsOverride = () => {
17444
- const configured = path21.resolve(resolveConfigDir());
17445
- return configured === path21.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 ${path21.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
18443
+ const configured = path22.resolve(resolveConfigDir());
18444
+ return configured === path22.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 ${path22.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
17446
18445
  };
17447
18446
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
17448
18447
  var DEFAULT_GRANTS = Object.freeze({
@@ -17495,8 +18494,16 @@ function endpointView(entry, context) {
17495
18494
  ...issue ? { endpointIssue: issue } : {}
17496
18495
  };
17497
18496
  }
17498
- var DEFAULT_VERIFICATION = "self";
17499
- var verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self";
18497
+ var VERIFICATION_MODES = [
18498
+ "proportional",
18499
+ "self",
18500
+ "independent"
18501
+ ];
18502
+ var DEFAULT_VERIFICATION = "proportional";
18503
+ var verificationOf = (entry) => {
18504
+ const value = entry?.verification;
18505
+ return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
18506
+ };
17500
18507
  var isGrantValue = (value) => value === true || value === false || value === "verified";
17501
18508
  var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
17502
18509
  var fromAutoApprove = (value) => {
@@ -19244,8 +20251,8 @@ class WorkitCore {
19244
20251
  }
19245
20252
  // packages/workit-core/src/core/repo-context.ts
19246
20253
  import { spawnSync as spawnSync13 } from "node:child_process";
19247
- import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
19248
- import path22 from "node:path";
20254
+ import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "node:fs";
20255
+ import path23 from "node:path";
19249
20256
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
19250
20257
  try {
19251
20258
  gitRevisionParts(value);
@@ -19379,7 +20386,7 @@ var findPrTemplate = (cwd) => {
19379
20386
  ]) {
19380
20387
  const parts = rel.split("/");
19381
20388
  const base = parts[parts.length - 1];
19382
- const dir = parts.length > 1 ? path22.join(cwd, ...parts.slice(0, -1)) : cwd;
20389
+ const dir = parts.length > 1 ? path23.join(cwd, ...parts.slice(0, -1)) : cwd;
19383
20390
  const actual = probeCaseInsensitive(dir, base);
19384
20391
  if (actual)
19385
20392
  return [...parts.slice(0, -1), actual].join("/");
@@ -19410,11 +20417,11 @@ var diffStatForRange = (cwd, range) => {
19410
20417
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
19411
20418
  };
19412
20419
  var packageScripts = (cwd, keys) => {
19413
- const pkgPath = path22.join(cwd, "package.json");
19414
- if (!existsSync7(pkgPath))
20420
+ const pkgPath = path23.join(cwd, "package.json");
20421
+ if (!existsSync8(pkgPath))
19415
20422
  return [];
19416
20423
  try {
19417
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
20424
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
19418
20425
  return keys.filter((k) => pkg.scripts && typeof pkg.scripts[k] === "string").map((k) => `${k}: ${pkg.scripts?.[k]}`);
19419
20426
  } catch {
19420
20427
  return [];
@@ -19436,9 +20443,9 @@ var documentationFiles = (cwd) => {
19436
20443
  if (entry.isDirectory()) {
19437
20444
  if (excluded.has(entry.name))
19438
20445
  continue;
19439
- walk(path22.join(dir, entry.name), depth + 1);
20446
+ walk(path23.join(dir, entry.name), depth + 1);
19440
20447
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
19441
- matches.push("./" + path22.relative(cwd, path22.join(dir, entry.name)).replaceAll("\\", "/"));
20448
+ matches.push("./" + path23.relative(cwd, path23.join(dir, entry.name)).replaceAll("\\", "/"));
19442
20449
  }
19443
20450
  }
19444
20451
  };
@@ -19446,15 +20453,15 @@ var documentationFiles = (cwd) => {
19446
20453
  return matches.toSorted().slice(0, 200);
19447
20454
  };
19448
20455
  var readTrimmed2 = (file, maxLines) => {
19449
- if (!existsSync7(file))
20456
+ if (!existsSync8(file))
19450
20457
  return "";
19451
- const lines = readFileSync5(file, "utf8").split(`
20458
+ const lines = readFileSync6(file, "utf8").split(`
19452
20459
  `);
19453
20460
  return lines.slice(0, maxLines).join(`
19454
20461
  `);
19455
20462
  };
19456
20463
  function prReadyContext(root, range) {
19457
- const cwd = path22.resolve(repoRoot(root));
20464
+ const cwd = path23.resolve(repoRoot(root));
19458
20465
  if (range !== undefined && !isResolvableContextRange(cwd, range))
19459
20466
  return invalidRange(cwd);
19460
20467
  let stdout = "";
@@ -19512,7 +20519,7 @@ ${status.stderr}` : "");
19512
20519
  stdout += `template_path: ${template}
19513
20520
 
19514
20521
  `;
19515
- stdout += readTrimmed2(path22.join(cwd, template), 220);
20522
+ stdout += readTrimmed2(path23.join(cwd, template), 220);
19516
20523
  } else {
19517
20524
  stdout += `template_path: none
19518
20525
 
@@ -19522,7 +20529,7 @@ ${status.stderr}` : "");
19522
20529
  stdout += `
19523
20530
  `;
19524
20531
  stdout += printSection("Recent Validation Signals");
19525
- if (existsSync7(path22.join(cwd, "package.json"))) {
20532
+ if (existsSync8(path23.join(cwd, "package.json"))) {
19526
20533
  stdout += `package.json detected. Common scripts:
19527
20534
  `;
19528
20535
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -19531,7 +20538,7 @@ ${status.stderr}` : "");
19531
20538
  `) + `
19532
20539
  `;
19533
20540
  }
19534
- if (existsSync7(path22.join(cwd, "Cargo.toml")) || existsSync7(path22.join(cwd, "src-tauri/Cargo.toml"))) {
20541
+ if (existsSync8(path23.join(cwd, "Cargo.toml")) || existsSync8(path23.join(cwd, "src-tauri/Cargo.toml"))) {
19535
20542
  stdout += `Rust project detected.
19536
20543
  `;
19537
20544
  }
@@ -19564,7 +20571,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
19564
20571
  - Apply with the host-native editor, preserving the repository changelog conventions.
19565
20572
  - If Unreleased already has duplicate category headings, normalize_only first.`;
19566
20573
  function changelogContext(root, range) {
19567
- const cwd = path22.resolve(repoRoot(root));
20574
+ const cwd = path23.resolve(repoRoot(root));
19568
20575
  if (range !== undefined && !isResolvableContextRange(cwd, range))
19569
20576
  return invalidRange(cwd);
19570
20577
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -19582,8 +20589,8 @@ function changelogContext(root, range) {
19582
20589
  stdout += CHANGELOG_RULES + `
19583
20590
  `;
19584
20591
  stdout += printSection("Existing CHANGELOG.md");
19585
- const changelogPath = path22.join(cwd, "CHANGELOG.md");
19586
- if (existsSync7(changelogPath)) {
20592
+ const changelogPath = path23.join(cwd, "CHANGELOG.md");
20593
+ if (existsSync8(changelogPath)) {
19587
20594
  stdout += readTrimmed2(changelogPath, 260) + `
19588
20595
  `;
19589
20596
  } else {
@@ -19602,7 +20609,7 @@ function changelogContext(root, range) {
19602
20609
  return { stdout, stderr: "", exitCode: 0, cwd };
19603
20610
  }
19604
20611
  function docsRefreshContext(root, range) {
19605
- const cwd = path22.resolve(repoRoot(root));
20612
+ const cwd = path23.resolve(repoRoot(root));
19606
20613
  if (range !== undefined && !isResolvableContextRange(cwd, range))
19607
20614
  return invalidRange(cwd);
19608
20615
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -19624,8 +20631,8 @@ function docsRefreshContext(root, range) {
19624
20631
  `) + `
19625
20632
  `;
19626
20633
  stdout += printSection("README Preview");
19627
- const readme = path22.join(cwd, "README.md");
19628
- if (existsSync7(readme)) {
20634
+ const readme = path23.join(cwd, "README.md");
20635
+ if (existsSync8(readme)) {
19629
20636
  stdout += readTrimmed2(readme, 220) + `
19630
20637
  `;
19631
20638
  } else {
@@ -19633,10 +20640,10 @@ function docsRefreshContext(root, range) {
19633
20640
  `;
19634
20641
  }
19635
20642
  stdout += printSection("Package Scripts");
19636
- const pkgPath = path22.join(cwd, "package.json");
19637
- if (existsSync7(pkgPath)) {
20643
+ const pkgPath = path23.join(cwd, "package.json");
20644
+ if (existsSync8(pkgPath)) {
19638
20645
  try {
19639
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
20646
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
19640
20647
  stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
19641
20648
  `;
19642
20649
  } catch {}
@@ -19647,7 +20654,7 @@ function docsRefreshContext(root, range) {
19647
20654
  return { stdout, stderr: "", exitCode: 0, cwd };
19648
20655
  }
19649
20656
  function releaseNotesContext(root, rangeOrTag) {
19650
- const cwd = path22.resolve(repoRoot(root));
20657
+ const cwd = path23.resolve(repoRoot(root));
19651
20658
  if (!rangeOrTag) {
19652
20659
  return { stdout: "", stderr: `ERROR: release tag or range required
19653
20660
  `, exitCode: 1, cwd };
@@ -19685,7 +20692,7 @@ function releaseNotesContext(root, rangeOrTag) {
19685
20692
  `;
19686
20693
  stdout += printSection("Existing Release Files");
19687
20694
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
19688
- if (existsSync7(path22.join(cwd, rel)))
20695
+ if (existsSync8(path23.join(cwd, rel)))
19689
20696
  stdout += rel + `
19690
20697
  `;
19691
20698
  }
@@ -19717,7 +20724,7 @@ import { spawnSync as spawnSync15 } from "node:child_process";
19717
20724
 
19718
20725
  // packages/workit-core/src/core/pr-create.ts
19719
20726
  import fs17 from "node:fs";
19720
- import path23 from "node:path";
20727
+ import path24 from "node:path";
19721
20728
  import { spawnSync as spawnSync14 } from "node:child_process";
19722
20729
  // packages/workit-core/src/forge/pr-body.ts
19723
20730
  function parseGhIssue(value) {
@@ -19728,11 +20735,11 @@ function parseGhIssue(value) {
19728
20735
  // packages/workit-core/src/core/pr-create.ts
19729
20736
  function whichOnPath(tool) {
19730
20737
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
19731
- for (const dir of (process.env.PATH ?? "").split(path23.delimiter)) {
20738
+ for (const dir of (process.env.PATH ?? "").split(path24.delimiter)) {
19732
20739
  if (!dir)
19733
20740
  continue;
19734
20741
  for (const name of names) {
19735
- const candidate = path23.join(dir, name);
20742
+ const candidate = path24.join(dir, name);
19736
20743
  try {
19737
20744
  fs17.accessSync(candidate, fs17.constants.X_OK);
19738
20745
  return candidate;
@@ -19853,7 +20860,7 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
19853
20860
 
19854
20861
  // packages/workit-core/src/core/youtrack.ts
19855
20862
  import fs18 from "node:fs";
19856
- import path24 from "node:path";
20863
+ import path25 from "node:path";
19857
20864
 
19858
20865
  // packages/workit-core/src/core/templates.ts
19859
20866
  var repoRoot2 = assetRoot();
@@ -19866,8 +20873,8 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
19866
20873
  // packages/workit-core/src/core/youtrack.ts
19867
20874
  var ISSUE_RE = /^[A-Z]+-\d+$/;
19868
20875
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
19869
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path24.join(configDir(), "youtrack.json");
19870
- var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path24.join(resolveConfigDir(), "youtrack.json");
20876
+ var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path25.join(configDir(), "youtrack.json");
20877
+ var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path25.join(resolveConfigDir(), "youtrack.json");
19871
20878
  var youTrackTokenModeOk = (p) => {
19872
20879
  if (process.platform === "win32")
19873
20880
  return true;
@@ -19935,7 +20942,7 @@ var youTrackToken = () => {
19935
20942
  if ("error" in loaded)
19936
20943
  return loaded;
19937
20944
  const tokenFile = String(loaded.config.tokenFile ?? "");
19938
- const tokenPath = tokenFile ? path24.isAbsolute(tokenFile) ? path24.resolve(tokenFile) : path24.resolve(process.cwd(), tokenFile) : "";
20945
+ const tokenPath = tokenFile ? path25.isAbsolute(tokenFile) ? path25.resolve(tokenFile) : path25.resolve(process.cwd(), tokenFile) : "";
19939
20946
  if (!tokenPath || !fs18.existsSync(tokenPath))
19940
20947
  return { error: "missing youtrack.token" };
19941
20948
  if (!youTrackTokenModeOk(tokenPath))
@@ -20292,15 +21299,15 @@ function resolveGitRevision(root, value) {
20292
21299
  }
20293
21300
  function resolveInside(root, candidate) {
20294
21301
  const base = realpathSync5(root);
20295
- const target = path25.resolve(base, candidate);
20296
- if (target !== base && !target.startsWith(base + path25.sep)) {
21302
+ const target = path26.resolve(base, candidate);
21303
+ if (target !== base && !target.startsWith(base + path26.sep)) {
20297
21304
  throw new Error("path must stay inside repository root");
20298
21305
  }
20299
21306
  let ancestor = target;
20300
- while (!existsSync8(ancestor))
20301
- ancestor = path25.dirname(ancestor);
21307
+ while (!existsSync9(ancestor))
21308
+ ancestor = path26.dirname(ancestor);
20302
21309
  const canonicalAncestor = realpathSync5(ancestor);
20303
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path25.sep)) {
21310
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path26.sep)) {
20304
21311
  throw new Error("path must stay inside repository root");
20305
21312
  }
20306
21313
  return target;
@@ -20346,7 +21353,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
20346
21353
  // packages/workit-core/src/hooks/context.ts
20347
21354
  import { createHash as createHash15 } from "node:crypto";
20348
21355
  import fs19 from "node:fs";
20349
- import path26 from "node:path";
21356
+ import path27 from "node:path";
20350
21357
  var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
20351
21358
  var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
20352
21359
  var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
@@ -20431,7 +21438,7 @@ function entryCompactContext(store, entry, session, context) {
20431
21438
  return null;
20432
21439
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
20433
21440
  const observed = task?.data.evidence.some(isObservedCheck) ?? false;
20434
- const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path26.resolve(store.root, ref.path)] : []));
21441
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path27.resolve(store.root, ref.path)] : []));
20435
21442
  const observedFiles = filesKey(files);
20436
21443
  const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
20437
21444
  if (!compact.ok || !compact.data)
@@ -20509,14 +21516,14 @@ var turnMarker = (input) => {
20509
21516
  if (location instanceof Error)
20510
21517
  return null;
20511
21518
  const session = [input.host, input.session.id, input.session.agentId ?? ""].join("\x00");
20512
- return path26.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
21519
+ return path27.join(location.dir, "hooks", `turn-${digestOf(session).slice(0, 32)}.json`);
20513
21520
  } catch {
20514
21521
  return null;
20515
21522
  }
20516
21523
  };
20517
21524
  var writeTurnDigest = (file, digest) => {
20518
21525
  try {
20519
- fs19.mkdirSync(path26.dirname(file), { recursive: true });
21526
+ fs19.mkdirSync(path27.dirname(file), { recursive: true });
20520
21527
  fs19.writeFileSync(file, `${JSON.stringify({ digest })}
20521
21528
  `);
20522
21529
  } catch {}
@@ -20531,7 +21538,7 @@ var pruneTurnDigests = (dir, now) => {
20531
21538
  for (const name of fs19.readdirSync(dir)) {
20532
21539
  if (!name.startsWith("turn-"))
20533
21540
  continue;
20534
- const file = path26.join(dir, name);
21541
+ const file = path27.join(dir, name);
20535
21542
  try {
20536
21543
  if (now - fs19.statSync(file).mtimeMs > TURN_TTL_MS)
20537
21544
  fs19.rmSync(file, { force: true });
@@ -20561,7 +21568,7 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
20561
21568
  const file = turnMarker(input);
20562
21569
  if (!file)
20563
21570
  return;
20564
- pruneTurnDigests(path26.dirname(file), Date.now());
21571
+ pruneTurnDigests(path27.dirname(file), Date.now());
20565
21572
  if (text)
20566
21573
  writeTurnDigest(file, digestOf(text));
20567
21574
  else
@@ -20579,10 +21586,57 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
20579
21586
  }
20580
21587
  return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
20581
21588
  };
21589
+ // packages/workit-core/src/hooks/delivery-verbs.ts
21590
+ var WORKIT_ALLOWED_VERBS = [
21591
+ "git",
21592
+ "pr",
21593
+ "ci",
21594
+ "stack",
21595
+ "release",
21596
+ "task",
21597
+ "ledger",
21598
+ "evidence",
21599
+ "finding",
21600
+ "decision",
21601
+ "worker",
21602
+ "verify-delivery",
21603
+ "policy",
21604
+ "doctor",
21605
+ "help"
21606
+ ];
21607
+ var WORKIT_BYPASS_FLAGS = ["--unverified", "--overwrite-unintegrated"];
21608
+ var ALLOWED = new Set(WORKIT_ALLOWED_VERBS);
21609
+ var isBypass = (word) => WORKIT_BYPASS_FLAGS.some((flag) => word === flag || word.startsWith(`${flag}=`));
21610
+ var isWorkitWord = (word) => /^(?:.*[\\/])?workit(?:-cli)?$/u.test(word);
21611
+ var bypassReason = (flag) => `Workit: \`${flag}\` overrides a Workit gate, so the user decides this call (no allow rule covers it).`;
21612
+ var EXPANSION_REASON = "Workit: an expansion before `--` could become a bypass flag, so the user decides this call (no allow rule covers it).";
21613
+ var workitBypassFlag = (command, dialect = "posix") => {
21614
+ for (const segment of segmentsOf(command, dialect)) {
21615
+ if (segment.unanalyzable) {
21616
+ const text = command.replaceAll(/['"\\`]/gu, "");
21617
+ if (!/\bworkit\b/u.test(text))
21618
+ return null;
21619
+ const found = WORKIT_BYPASS_FLAGS.find((flag) => text.includes(flag));
21620
+ return found ? bypassReason(found) : null;
21621
+ }
21622
+ const start = segment.words.findIndex(isWorkitWord);
21623
+ if (start < 0)
21624
+ continue;
21625
+ const words = segment.words.slice(start + 1);
21626
+ const end = words.indexOf("--");
21627
+ const flag = (end < 0 ? words : words.slice(0, end)).find(isBypass);
21628
+ if (flag)
21629
+ return bypassReason(flag.split("=")[0] ?? flag);
21630
+ const limit = start + 1 + (end < 0 ? words.length : end);
21631
+ if (segment.expands?.some((index) => index > start && index < limit))
21632
+ return EXPANSION_REASON;
21633
+ }
21634
+ return null;
21635
+ };
20582
21636
  // packages/workit-core/src/core/heavy-rules.ts
20583
21637
  import fs20 from "node:fs";
20584
21638
  import os6 from "node:os";
20585
- import path27 from "node:path";
21639
+ import path28 from "node:path";
20586
21640
  var EMPTY2 = () => ({ deny: [], allow: [] });
20587
21641
  var readText2 = (file) => {
20588
21642
  try {
@@ -20623,19 +21677,19 @@ function parseJsonc(text) {
20623
21677
  return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
20624
21678
  }
20625
21679
  function gitTop(cwd) {
20626
- for (let dir = path27.resolve(cwd);; dir = path27.dirname(dir)) {
20627
- if (fs20.existsSync(path27.join(dir, ".git")))
21680
+ for (let dir = path28.resolve(cwd);; dir = path28.dirname(dir)) {
21681
+ if (fs20.existsSync(path28.join(dir, ".git")))
20628
21682
  return dir;
20629
- if (path27.dirname(dir) === dir)
21683
+ if (path28.dirname(dir) === dir)
20630
21684
  return null;
20631
21685
  }
20632
21686
  }
20633
21687
  var projectDirs = (cwd) => {
20634
21688
  const top = gitTop(cwd);
20635
21689
  const dirs = [];
20636
- for (let dir = path27.resolve(cwd);; dir = path27.dirname(dir)) {
21690
+ for (let dir = path28.resolve(cwd);; dir = path28.dirname(dir)) {
20637
21691
  dirs.push(dir);
20638
- if (!top || dir === top || path27.dirname(dir) === dir)
21692
+ if (!top || dir === top || path28.dirname(dir) === dir)
20639
21693
  return dirs;
20640
21694
  }
20641
21695
  };
@@ -20647,12 +21701,12 @@ var CLAUDE_MANAGED = {
20647
21701
  };
20648
21702
  function claudeSettingsFiles(cwd, env, platform = process.platform) {
20649
21703
  const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
20650
- const user = env.CLAUDE_CONFIG_DIR || path27.join(homeOf(env), ".claude");
21704
+ const user = env.CLAUDE_CONFIG_DIR || path28.join(homeOf(env), ".claude");
20651
21705
  return [
20652
21706
  ...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
20653
- path27.join(user, "settings.json"),
20654
- path27.join(project, ".claude", "settings.json"),
20655
- path27.join(project, ".claude", "settings.local.json")
21707
+ path28.join(user, "settings.json"),
21708
+ path28.join(project, ".claude", "settings.json"),
21709
+ path28.join(project, ".claude", "settings.local.json")
20656
21710
  ];
20657
21711
  }
20658
21712
  var bashPattern = (entry) => {
@@ -20742,10 +21796,10 @@ var opencodeConfig = (value, rules) => {
20742
21796
  function opencodeRules(cwd, env = process.env) {
20743
21797
  const rules = EMPTY2();
20744
21798
  try {
20745
- const config = env.XDG_CONFIG_HOME || path27.join(homeOf(env), ".config");
21799
+ const config = env.XDG_CONFIG_HOME || path28.join(homeOf(env), ".config");
20746
21800
  const files = [
20747
- ...OPENCODE_NAMES.map((name) => path27.join(config, "opencode", name)),
20748
- ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path27.join(dir, name), path27.join(dir, ".opencode", name)])),
21801
+ ...OPENCODE_NAMES.map((name) => path28.join(config, "opencode", name)),
21802
+ ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path28.join(dir, name), path28.join(dir, ".opencode", name)])),
20749
21803
  ...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
20750
21804
  ];
20751
21805
  for (const file of files) {
@@ -20789,9 +21843,9 @@ var codexRule = (body) => {
20789
21843
  function codexRules(cwd, env = process.env) {
20790
21844
  const rules = EMPTY2();
20791
21845
  try {
20792
- const home = env.CODEX_HOME || path27.join(homeOf(env), ".codex");
21846
+ const home = env.CODEX_HOME || path28.join(homeOf(env), ".codex");
20793
21847
  const top = gitTop(cwd) ?? cwd;
20794
- for (const dir of [path27.join(home, "rules"), path27.join(top, ".codex", "rules")]) {
21848
+ for (const dir of [path28.join(home, "rules"), path28.join(top, ".codex", "rules")]) {
20795
21849
  let files;
20796
21850
  try {
20797
21851
  files = fs20.readdirSync(dir).filter((name) => name.endsWith(".rules"));
@@ -20801,7 +21855,7 @@ function codexRules(cwd, env = process.env) {
20801
21855
  return "unreadable";
20802
21856
  }
20803
21857
  for (const name of files) {
20804
- const text = readText2(path27.join(dir, name)) ?? "";
21858
+ const text = readText2(path28.join(dir, name)) ?? "";
20805
21859
  for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
20806
21860
  const rule = codexRule(part);
20807
21861
  if (!rule)
@@ -20820,8 +21874,8 @@ function cursorRules(cwd, env = process.env) {
20820
21874
  try {
20821
21875
  const top = gitTop(cwd) ?? cwd;
20822
21876
  for (const file of [
20823
- path27.join(homeOf(env), ".cursor", "cli-config.json"),
20824
- path27.join(top, ".cursor", "cli.json")
21877
+ path28.join(homeOf(env), ".cursor", "cli-config.json"),
21878
+ path28.join(top, ".cursor", "cli.json")
20825
21879
  ]) {
20826
21880
  const text = readText2(file);
20827
21881
  if (text === null)
@@ -20897,21 +21951,21 @@ function wrapAllowed(read, words, command, options = {}) {
20897
21951
  // packages/workit-core/src/core/heavy.ts
20898
21952
  import fs21 from "node:fs";
20899
21953
  import os7 from "node:os";
20900
- import path28 from "node:path";
21954
+ import path29 from "node:path";
20901
21955
  function stateDir(env, platform = process.platform) {
20902
21956
  if (env.WORKFLOW_TOOLKIT_STATE)
20903
21957
  return env.WORKFLOW_TOOLKIT_STATE;
20904
21958
  if (env.XDG_STATE_HOME)
20905
- return path28.join(env.XDG_STATE_HOME, "workit");
21959
+ return path29.join(env.XDG_STATE_HOME, "workit");
20906
21960
  const home = env.HOME || os7.homedir();
20907
21961
  if (platform === "darwin")
20908
- return path28.join(home, "Library", "Application Support", "workit");
21962
+ return path29.join(home, "Library", "Application Support", "workit");
20909
21963
  if (platform === "win32")
20910
- return path28.join(env.LOCALAPPDATA ?? path28.join(home, "AppData", "Local"), "workit");
20911
- return path28.join(home, ".local", "state", "workit");
21964
+ return path29.join(env.LOCALAPPDATA ?? path29.join(home, "AppData", "Local"), "workit");
21965
+ return path29.join(home, ".local", "state", "workit");
20912
21966
  }
20913
21967
  var HEAVY_COMMANDS_FILE = "heavy-commands.json";
20914
- var heavyCommandsFile = (env) => path28.join(stateDir(env), HEAVY_COMMANDS_FILE);
21968
+ var heavyCommandsFile = (env) => path29.join(stateDir(env), HEAVY_COMMANDS_FILE);
20915
21969
  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;
20916
21970
  var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
20917
21971
  var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
@@ -21186,7 +22240,7 @@ function machinePatterns(env) {
21186
22240
  return isRecord5(value) ? stringList(value.patterns) : [];
21187
22241
  }
21188
22242
  function repoPatterns(cwd) {
21189
- return repoHeavyCommands(gitTop(cwd) ?? path28.resolve(cwd));
22243
+ return repoHeavyCommands(gitTop(cwd) ?? path29.resolve(cwd));
21190
22244
  }
21191
22245
  function heavyLines(lines, options) {
21192
22246
  const found = [];
@@ -21264,12 +22318,12 @@ function heavyPlan(command, options) {
21264
22318
  }
21265
22319
  function workitOnPath2(env = process.env) {
21266
22320
  const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
21267
- for (const dir of (env.PATH ?? "").split(path28.delimiter))
22321
+ for (const dir of (env.PATH ?? "").split(path29.delimiter))
21268
22322
  for (const name of names) {
21269
22323
  if (!dir)
21270
22324
  continue;
21271
22325
  try {
21272
- fs21.accessSync(path28.join(dir, name), fs21.constants.X_OK);
22326
+ fs21.accessSync(path29.join(dir, name), fs21.constants.X_OK);
21273
22327
  return true;
21274
22328
  } catch {}
21275
22329
  }
@@ -21288,20 +22342,20 @@ function leaseRewrite(command) {
21288
22342
  // packages/workit-core/src/core/heavy-words.ts
21289
22343
  import fs22 from "node:fs";
21290
22344
  import os8 from "node:os";
21291
- import path29 from "node:path";
22345
+ import path30 from "node:path";
21292
22346
  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(" "));
21293
22347
  var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
21294
22348
  var stateDir2 = (env) => {
21295
22349
  if (env.WORKFLOW_TOOLKIT_STATE)
21296
22350
  return env.WORKFLOW_TOOLKIT_STATE;
21297
22351
  if (env.XDG_STATE_HOME)
21298
- return path29.join(env.XDG_STATE_HOME, "workit");
22352
+ return path30.join(env.XDG_STATE_HOME, "workit");
21299
22353
  const home = env.HOME || os8.homedir();
21300
22354
  if (process.platform === "darwin")
21301
- return path29.join(home, "Library", "Application Support", "workit");
22355
+ return path30.join(home, "Library", "Application Support", "workit");
21302
22356
  if (process.platform === "win32")
21303
- return path29.join(env.LOCALAPPDATA ?? path29.join(home, "AppData", "Local"), "workit");
21304
- return path29.join(home, ".local", "state", "workit");
22357
+ return path30.join(env.LOCALAPPDATA ?? path30.join(home, "AppData", "Local"), "workit");
22358
+ return path30.join(home, ".local", "state", "workit");
21305
22359
  };
21306
22360
  var readJson2 = (file) => {
21307
22361
  try {
@@ -21312,15 +22366,15 @@ var readJson2 = (file) => {
21312
22366
  };
21313
22367
  var patternWords = (cwd, env) => {
21314
22368
  const patterns = [];
21315
- const machine = readJson2(path29.join(stateDir2(env), "heavy-commands.json"));
22369
+ const machine = readJson2(path30.join(stateDir2(env), "heavy-commands.json"));
21316
22370
  if (Array.isArray(machine?.patterns))
21317
22371
  patterns.push(...machine.patterns);
21318
- for (let dir = path29.resolve(cwd);; dir = path29.dirname(dir)) {
21319
- if (fs22.existsSync(path29.join(dir, ".git"))) {
22372
+ for (let dir = path30.resolve(cwd);; dir = path30.dirname(dir)) {
22373
+ if (fs22.existsSync(path30.join(dir, ".git"))) {
21320
22374
  patterns.push(...repoHeavyCommands(dir));
21321
22375
  break;
21322
22376
  }
21323
- if (path29.dirname(dir) === dir)
22377
+ if (path30.dirname(dir) === dir)
21324
22378
  break;
21325
22379
  }
21326
22380
  const words = new Set;
@@ -21390,16 +22444,16 @@ var splitDenial = (plan) => ({
21390
22444
  unblock: plan.unblock
21391
22445
  });
21392
22446
  // packages/workit-core/src/hooks/write-gate.ts
21393
- import { existsSync as existsSync9, realpathSync as realpathSync6 } from "node:fs";
22447
+ import { existsSync as existsSync10, realpathSync as realpathSync6 } from "node:fs";
21394
22448
  import os10 from "node:os";
21395
- import path31 from "node:path";
22449
+ import path32 from "node:path";
21396
22450
 
21397
22451
  // packages/workit-core/src/core/live-session.ts
21398
22452
  import { spawnSync as spawnSync17 } from "node:child_process";
21399
22453
  import { createHash as createHash16 } from "node:crypto";
21400
22454
  import fs23 from "node:fs";
21401
22455
  import os9 from "node:os";
21402
- import path30 from "node:path";
22456
+ import path31 from "node:path";
21403
22457
  var LIVE_SESSION_TTL_MS = 15 * 60000;
21404
22458
  var HEARTBEAT_EVERY_MS = 30000;
21405
22459
  var liveProbe = {
@@ -21473,15 +22527,15 @@ var liveOwner = (input, transport, env = process.env) => {
21473
22527
  };
21474
22528
  };
21475
22529
  var checkoutOf = (target) => {
21476
- let dir = path30.resolve(target);
21477
- while (!fs23.existsSync(dir) && path30.dirname(dir) !== dir)
21478
- dir = path30.dirname(dir);
22530
+ let dir = path31.resolve(target);
22531
+ while (!fs23.existsSync(dir) && path31.dirname(dir) !== dir)
22532
+ dir = path31.dirname(dir);
21479
22533
  if (fs23.existsSync(dir) && !fs23.statSync(dir).isDirectory())
21480
- dir = path30.dirname(dir);
21481
- const parts = dir.split(path30.sep);
22534
+ dir = path31.dirname(dir);
22535
+ const parts = dir.split(path31.sep);
21482
22536
  const dotGit = parts.lastIndexOf(".git");
21483
22537
  if (dotGit > 0)
21484
- dir = parts.slice(0, dotGit).join(path30.sep) || path30.sep;
22538
+ dir = parts.slice(0, dotGit).join(path31.sep) || path31.sep;
21485
22539
  const location = resolveStore(dir);
21486
22540
  if (location instanceof Error || !location.shared || !location.top)
21487
22541
  return null;
@@ -21491,9 +22545,9 @@ var checkoutOf = (target) => {
21491
22545
  } catch {}
21492
22546
  return top;
21493
22547
  };
21494
- var markerFile2 = (top) => path30.join(resolveStateDir(), "live-sessions", `${createHash16("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
22548
+ var markerFile2 = (top) => path31.join(resolveStateDir(), "live-sessions", `${createHash16("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
21495
22549
  var checkoutKey = (top, platform = process.platform) => {
21496
- let key = path30.resolve(top);
22550
+ let key = path31.resolve(top);
21497
22551
  try {
21498
22552
  key = fs23.realpathSync.native(key);
21499
22553
  } catch {}
@@ -21588,7 +22642,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
21588
22642
  checkout,
21589
22643
  heartbeatAt: now.toISOString()
21590
22644
  };
21591
- fs23.mkdirSync(path30.dirname(file), { recursive: true });
22645
+ fs23.mkdirSync(path31.dirname(file), { recursive: true });
21592
22646
  const temp = `${file}.${process.pid}.tmp`;
21593
22647
  fs23.writeFileSync(temp, JSON.stringify(marker));
21594
22648
  fs23.renameSync(temp, file);
@@ -21600,22 +22654,22 @@ var NONE3 = { kind: "none" };
21600
22654
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
21601
22655
  var canonical2 = (file) => {
21602
22656
  let rest = "";
21603
- for (let dir = path31.resolve(file);; dir = path31.dirname(dir)) {
22657
+ for (let dir = path32.resolve(file);; dir = path32.dirname(dir)) {
21604
22658
  try {
21605
- return path31.join(realpathSync6.native(dir), rest);
22659
+ return path32.join(realpathSync6.native(dir), rest);
21606
22660
  } catch {
21607
- if (path31.dirname(dir) === dir)
21608
- return path31.resolve(file);
21609
- rest = rest ? path31.join(path31.basename(dir), rest) : path31.basename(dir);
22661
+ if (path32.dirname(dir) === dir)
22662
+ return path32.resolve(file);
22663
+ rest = rest ? path32.join(path32.basename(dir), rest) : path32.basename(dir);
21610
22664
  }
21611
22665
  }
21612
22666
  };
21613
22667
  var exempt = (cwd, root, file, plans) => {
21614
22668
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
21615
- const relative = path31.relative(fold(canonical2(root)), fold(canonical2(path31.resolve(cwd, file))));
21616
- if (relative.startsWith("..") || path31.isAbsolute(relative))
22669
+ const relative = path32.relative(fold(canonical2(root)), fold(canonical2(path32.resolve(cwd, file))));
22670
+ if (relative.startsWith("..") || path32.isAbsolute(relative))
21617
22671
  return true;
21618
- const posix = relative.split(path31.sep).join("/");
22672
+ const posix = relative.split(path32.sep).join("/");
21619
22673
  const segments = posix.split("/");
21620
22674
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
21621
22675
  };
@@ -21792,10 +22846,10 @@ var linkSources = (args, at) => {
21792
22846
  const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
21793
22847
  const target = dest === undefined ? null : at(dest);
21794
22848
  const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
21795
- const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync9(target) || /[\\/]$/.test(dest ?? "") ? target : path31.dirname(target);
22849
+ const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync10(target) || /[\\/]$/.test(dest ?? "") ? target : path32.dirname(target);
21796
22850
  return sources.flatMap((source) => {
21797
- if (symbolic && base !== null && !path31.isAbsolute(homeExpanded(source)))
21798
- return [path31.resolve(base, source)];
22851
+ if (symbolic && base !== null && !path32.isAbsolute(homeExpanded(source)))
22852
+ return [path32.resolve(base, source)];
21799
22853
  const resolved = at(source);
21800
22854
  return resolved ? [resolved] : [];
21801
22855
  });
@@ -21824,7 +22878,7 @@ var shellWrites = (command, depth = 0) => {
21824
22878
  return { writes: true, targets: unknown ? null : targets };
21825
22879
  };
21826
22880
  var cache2 = new Map;
21827
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path31.resolve(root, file)) ?? "-").join("\x00");
22881
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path32.resolve(root, file)) ?? "-").join("\x00");
21828
22882
  var gateFor = (cwd) => {
21829
22883
  const store = new TaskStore(cwd);
21830
22884
  const listed = store.listTaskIndex();
@@ -21903,13 +22957,13 @@ var gitMutationDirs = (cwd, command, dialect) => {
21903
22957
  return [];
21904
22958
  if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
21905
22959
  return [];
21906
- return [path31.resolve(cwd, invocation.dir ?? ".")];
22960
+ return [path32.resolve(cwd, invocation.dir ?? ".")];
21907
22961
  });
21908
22962
  };
21909
22963
  var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
21910
22964
  var PUSHD2 = new Set(["pushd", "push-location"]);
21911
22965
  var POPD2 = new Set(["popd", "pop-location"]);
21912
- var UNKNOWN_DIR = path31.resolve(path31.parse(process.cwd()).root, ".workit-unknown-dir");
22966
+ var UNKNOWN_DIR = path32.resolve(path32.parse(process.cwd()).root, ".workit-unknown-dir");
21913
22967
  var cdInto = (dir, words, strict = false) => {
21914
22968
  const lost = strict ? UNKNOWN_DIR : undefined;
21915
22969
  const named = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
@@ -21918,7 +22972,7 @@ var cdInto = (dir, words, strict = false) => {
21918
22972
  return lost;
21919
22973
  if (dir === undefined)
21920
22974
  return lost;
21921
- return target === undefined ? os10.homedir() : path31.resolve(dir, target);
22975
+ return target === undefined ? os10.homedir() : path32.resolve(dir, target);
21922
22976
  };
21923
22977
  var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
21924
22978
  var homeExpanded = (file) => {
@@ -21932,7 +22986,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21932
22986
  const groups = [];
21933
22987
  const at = (file) => {
21934
22988
  const home = homeExpanded(file);
21935
- return path31.isAbsolute(home) ? home : dir === undefined ? null : path31.resolve(dir, home);
22989
+ return path32.isAbsolute(home) ? home : dir === undefined ? null : path32.resolve(dir, home);
21936
22990
  };
21937
22991
  for (const segment of segmentsOf(command, dialect)) {
21938
22992
  if (segment.group === "open") {
@@ -21963,7 +23017,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21963
23017
  }
21964
23018
  const inner = depth < 4 ? wrappedScript(words) ?? (strict ? shellScript2(words) : null) : null;
21965
23019
  if (inner !== null && dir !== undefined)
21966
- targets.push(...segmentTargets(dir, inner, "posix", depth + 1, strict));
23020
+ targets.push(...segmentTargets(workitRunDir(dir, words), inner, "posix", depth + 1, strict));
21967
23021
  if (strict && dir !== undefined) {
21968
23022
  const runIn = dir;
21969
23023
  for (const sub of depth < 4 ? segment.subs ?? [] : [])
@@ -21972,7 +23026,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21972
23026
  for (const file of opaque ?? []) {
21973
23027
  const resolved = at(file);
21974
23028
  if (resolved)
21975
- targets.push(file === "." ? path31.join(resolved, DIR_TARGET) : resolved);
23029
+ targets.push(file === "." ? path32.join(resolved, DIR_TARGET) : resolved);
21976
23030
  }
21977
23031
  const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
21978
23032
  if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
@@ -21987,7 +23041,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21987
23041
  const edits = editsOf(segment.words);
21988
23042
  if (edits === null) {
21989
23043
  if (dir !== undefined)
21990
- targets.push(path31.join(dir, DIR_TARGET));
23044
+ targets.push(path32.join(dir, DIR_TARGET));
21991
23045
  } else
21992
23046
  for (const file of edits) {
21993
23047
  const resolved = at(file);
@@ -21998,20 +23052,28 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21998
23052
  for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
21999
23053
  const { at: runsIn, args } = workitCwd(dir, invocation);
22000
23054
  if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
22001
- targets.push(path31.join(runsIn, DIR_TARGET));
23055
+ targets.push(path32.join(runsIn, DIR_TARGET));
22002
23056
  }
22003
23057
  }
22004
23058
  return targets;
22005
23059
  };
23060
+ var workitRunDir = (dir, words) => {
23061
+ for (const invocation of workitInvocations(words.map(quoteWord2).join(" "), "posix")) {
23062
+ const { at, args } = workitCwd(dir, invocation);
23063
+ if (args[0] === "check" || args[0] === "lock" && args[1] === "run")
23064
+ return at;
23065
+ }
23066
+ return dir;
23067
+ };
22006
23068
  var workitCwd = (dir, args) => {
22007
23069
  const { rest, cwd } = skipWorkitGlobals(args);
22008
- return { at: cwd === null ? dir : path31.resolve(dir, homeExpanded(cwd)), args: rest };
23070
+ return { at: cwd === null ? dir : path32.resolve(dir, homeExpanded(cwd)), args: rest };
22009
23071
  };
22010
23072
  var shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
22011
23073
  try {
22012
23074
  return [
22013
23075
  ...segmentTargets(cwd, command, dialect, 0, strict),
22014
- ...gitMutationDirs(cwd, command, dialect).map((dir) => path31.join(dir, DIR_TARGET))
23076
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path32.join(dir, DIR_TARGET))
22015
23077
  ];
22016
23078
  } catch {
22017
23079
  return [];
@@ -22048,7 +23110,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
22048
23110
  var guarded = (targets) => {
22049
23111
  const seen = new Set;
22050
23112
  return targets.filter((target) => {
22051
- const dir = path31.dirname(target);
23113
+ const dir = path32.dirname(target);
22052
23114
  if (seen.has(dir))
22053
23115
  return false;
22054
23116
  seen.add(dir);
@@ -22069,7 +23131,7 @@ var claimAll = (owner, targets, deps) => {
22069
23131
  for (const target of guarded(targets))
22070
23132
  claimLiveSession(target, owner, deps);
22071
23133
  };
22072
- var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path31.resolve(cwd, file)) : [path31.join(cwd, DIR_TARGET)];
23134
+ var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path32.resolve(cwd, file)) : [path32.join(cwd, DIR_TARGET)];
22073
23135
  var issuedTarget = (cwd, targets) => {
22074
23136
  const state = resolveStateDir();
22075
23137
  const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
@@ -22077,7 +23139,7 @@ var issuedTarget = (cwd, targets) => {
22077
23139
  };
22078
23140
  var underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
22079
23141
  const win = platform === "win32";
22080
- const p = win ? path31.win32 : path31.posix;
23142
+ const p = win ? path32.win32 : path32.posix;
22081
23143
  const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
22082
23144
  const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
22083
23145
  const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
@@ -22168,6 +23230,24 @@ function beforeWriteGate(cwd, paths) {
22168
23230
  }
22169
23231
  // packages/workit-core/src/hooks/stop.ts
22170
23232
  import { spawnSync as spawnSync18 } from "node:child_process";
23233
+
23234
+ // packages/workit-core/src/core/policy/current-judgment.ts
23235
+ function currentJudgment(cwd) {
23236
+ try {
23237
+ const store = new TaskStore(cwd);
23238
+ const listed = store.listTaskIndex();
23239
+ const key = store.currentKey();
23240
+ if (!listed.ok || !key.ok)
23241
+ return null;
23242
+ const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
23243
+ const task = entry ? store.readTask(entry.id) : null;
23244
+ return task?.ok === true ? latestJudgment(task.data) : null;
23245
+ } catch {
23246
+ return null;
23247
+ }
23248
+ }
23249
+
23250
+ // packages/workit-core/src/hooks/stop.ts
22171
23251
  var NONE4 = { kind: "none" };
22172
23252
  var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
22173
23253
  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;
@@ -22260,29 +23340,32 @@ var redChecks = (rows, pr, head, endpoint) => {
22260
23340
  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)" : ""}.`
22261
23341
  };
22262
23342
  };
22263
- var taskJudgment = (cwd) => {
22264
- try {
22265
- const store = new TaskStore(cwd);
22266
- const listed = store.listTaskIndex();
22267
- const key = store.currentKey();
22268
- if (!listed.ok || !key.ok)
22269
- return null;
22270
- const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
22271
- const task = entry ? store.readTask(entry.id) : null;
22272
- return task?.ok === true ? latestJudgment(task.data) : null;
22273
- } catch {
22274
- return null;
22275
- }
23343
+ var prBase = (rows) => {
23344
+ const seen = rows.findLast((row) => (row.type === "pr.status" || row.type === "pr.created") && row.observer === "workit_cli" && typeof row.base === "string");
23345
+ return typeof seen?.base === "string" ? seen.base : null;
22276
23346
  };
22277
- var unverified = (cwd, branch, all, descriptor, verification) => {
23347
+ var unverified = (cwd, branch, all, descriptor, verification, head) => {
22278
23348
  const check = checkVerdicts(cwd, branch, all);
22279
23349
  if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
22280
23350
  return null;
22281
- const judgment = taskJudgment(cwd);
23351
+ const judgment = currentJudgment(cwd);
22282
23352
  const high = judgment?.riskTier === "high";
22283
- if (judgment && !judgment.behaviorChange && !high)
22284
- return null;
22285
- const independent = verification === "independent" || high;
23353
+ let independent;
23354
+ if (verification === "proportional") {
23355
+ const lane = branchLane(cwd, {
23356
+ base: prBase(all.filter((row) => row.branch === branch)),
23357
+ head,
23358
+ judgment
23359
+ });
23360
+ const need = laneNeed(lane.lane);
23361
+ if (need === "none")
23362
+ return null;
23363
+ independent = need === "non-author";
23364
+ } else {
23365
+ if (judgment && !judgment.behaviorChange && !high)
23366
+ return null;
23367
+ independent = verification === "independent" || high;
23368
+ }
22286
23369
  if (!independent && check.review === "self-reviewed")
22287
23370
  return null;
22288
23371
  const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
@@ -22329,7 +23412,7 @@ function stopObligation(input, descriptor) {
22329
23412
  }
22330
23413
  if (!delivered || endpoint !== "green" && endpoint !== "merged")
22331
23414
  return null;
22332
- const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
23415
+ const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
22333
23416
  return left ? { ...left, head } : null;
22334
23417
  } catch {
22335
23418
  return null;
@@ -22627,9 +23710,9 @@ var writePaths = (input) => {
22627
23710
  };
22628
23711
  // packages/workit-core/src/hooks/standing.ts
22629
23712
  import fs24 from "node:fs";
22630
- import path32 from "node:path";
23713
+ import path33 from "node:path";
22631
23714
  var readPlans = (store) => {
22632
- const dir = path32.join(store, "fanouts");
23715
+ const dir = path33.join(store, "fanouts");
22633
23716
  let names;
22634
23717
  try {
22635
23718
  names = fs24.readdirSync(dir).filter((name) => name.endsWith(".json"));
@@ -22639,7 +23722,7 @@ var readPlans = (store) => {
22639
23722
  const plans = [];
22640
23723
  for (const name of names) {
22641
23724
  try {
22642
- const raw = JSON.parse(fs24.readFileSync(path32.join(dir, name), "utf8"));
23725
+ const raw = JSON.parse(fs24.readFileSync(path33.join(dir, name), "utf8"));
22643
23726
  if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
22644
23727
  continue;
22645
23728
  const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
@@ -22729,8 +23812,13 @@ var standingDelta = (input) => {
22729
23812
  };
22730
23813
  var sinceStart = new Map;
22731
23814
  // packages/workit-core/src/hooks/cwd-normalize.ts
22732
- var COMPOUND = /^\s*cd\s+([A-Za-z0-9_./@%+:,=-]+|'[^'$`\\\n]*'|"[^"$`\\!\n]*")\s*&&\s*(workit(?:\s[\s\S]*)?)$/u;
23815
+ var DIR = String.raw`[A-Za-z0-9_./@%+:,=-]+|'[^'$${"`"}\\\n]*'|"[^"$${"`"}\\!\n]*"`;
23816
+ var COMPOUND = new RegExp(String.raw`^\s*cd\s+(${DIR})\s*&&\s*(workit(?:\s[\s\S]*)?)$`, "u");
23817
+ var GLOBALS_FIRST = new RegExp(String.raw`^(\s*workit)((?:\s+(?:--json|--cwd(?:=|\s+)(?:${DIR})))+)\s+([a-z][a-z-]*)(?=\s|$)`, "u");
23818
+ var VERB_FIRST = /^((?:--json\s+)*)([a-z][a-z-]*)(?=\s|$)/u;
23819
+ var VERBS = new Set(WORKIT_ALLOWED_VERBS);
22733
23820
  var FILTERS = new Set(["tail", "head", "grep"]);
23821
+ var clean = (s) => !s.unanalyzable && !s.group && !s.subs?.length && !s.stdin?.length && !s.redirects.length;
22734
23822
  var stdinOnly = (words) => {
22735
23823
  const [name, ...args] = words;
22736
23824
  const operands = args.filter((w) => !w.startsWith("-"));
@@ -22752,53 +23840,67 @@ var cwdRewrite = (command) => {
22752
23840
  return null;
22753
23841
  if (head.words.some((w) => w === "--cwd" || w.startsWith("--cwd=")))
22754
23842
  return null;
22755
- const clean = (s) => !s.unanalyzable && !s.group && !s.subs?.length && !s.stdin?.length && !s.redirects.length;
22756
23843
  if (!segments.every(clean))
22757
23844
  return null;
22758
23845
  if (!segments.slice(0, -1).every((s) => s.pipe))
22759
23846
  return null;
22760
23847
  if (!filters.every((s) => FILTERS.has(s.words[0] ?? "") && stdinOnly(s.words)))
22761
23848
  return null;
22762
- return `workit --cwd ${dir} ${rest.slice("workit".length).trimStart()}`.trimEnd();
23849
+ const args = rest.slice("workit".length).trimStart();
23850
+ const verb = VERB_FIRST.exec(args);
23851
+ if (!verb?.[2] || !VERBS.has(verb[2]))
23852
+ return `workit --cwd ${dir} ${args}`.trimEnd();
23853
+ const json = verb[1] ? " --json" : "";
23854
+ return `workit ${verb[2]} --cwd ${dir}${json}${args.slice(verb[0].length)}`.trimEnd();
23855
+ };
23856
+ var verbFirst = (command) => {
23857
+ const match = GLOBALS_FIRST.exec(command);
23858
+ const [whole, lead, globals, verb] = match ?? [];
23859
+ if (!whole || !verb || !VERBS.has(verb) || !globals?.includes("--cwd"))
23860
+ return null;
23861
+ const segments = segmentsOf(command);
23862
+ if (segments.length !== 1 || !segments.every(clean))
23863
+ return null;
23864
+ return `${lead} ${verb}${globals}${command.slice(whole.length)}`;
22763
23865
  };
22764
23866
 
22765
23867
  // packages/workit-core/src/hooks/worktree-confine.ts
22766
23868
  import { createHash as createHash17 } from "node:crypto";
22767
23869
  import fs25 from "node:fs";
22768
- import path33 from "node:path";
23870
+ import path34 from "node:path";
22769
23871
  var FANOUT_TOKEN_FILE = "workit-fanout-token";
22770
23872
  var NONE5 = { kind: "none" };
22771
23873
  var CASE_INSENSITIVE2 = process.platform === "darwin" || process.platform === "win32";
22772
23874
  var canonical3 = (file) => {
22773
23875
  let rest = "";
22774
- for (let dir = path33.resolve(file);; dir = path33.dirname(dir)) {
23876
+ for (let dir = path34.resolve(file);; dir = path34.dirname(dir)) {
22775
23877
  try {
22776
- return path33.join(fs25.realpathSync.native(dir), rest);
23878
+ return path34.join(fs25.realpathSync.native(dir), rest);
22777
23879
  } catch {
22778
- if (path33.dirname(dir) === dir)
22779
- return path33.resolve(file);
22780
- rest = rest ? path33.join(path33.basename(dir), rest) : path33.basename(dir);
23880
+ if (path34.dirname(dir) === dir)
23881
+ return path34.resolve(file);
23882
+ rest = rest ? path34.join(path34.basename(dir), rest) : path34.basename(dir);
22781
23883
  }
22782
23884
  }
22783
23885
  };
22784
23886
  var within = (child, parent) => {
22785
23887
  const fold = (value) => CASE_INSENSITIVE2 ? value.toLowerCase() : value;
22786
- const relative = path33.relative(fold(parent), fold(child));
22787
- return relative === "" || !relative.startsWith("..") && !path33.isAbsolute(relative);
23888
+ const relative = path34.relative(fold(parent), fold(child));
23889
+ return relative === "" || !relative.startsWith("..") && !path34.isAbsolute(relative);
22788
23890
  };
22789
23891
  var checkoutOf2 = (file) => {
22790
- for (let dir = canonical3(file);; dir = path33.dirname(dir)) {
22791
- const dotGit = path33.join(dir, ".git");
23892
+ for (let dir = canonical3(file);; dir = path34.dirname(dir)) {
23893
+ const dotGit = path34.join(dir, ".git");
22792
23894
  if (fs25.existsSync(dotGit)) {
22793
23895
  if (!fs25.statSync(dotGit).isFile())
22794
23896
  return { root: dir, workit: false };
22795
23897
  const admin = /^gitdir:\s*(.+)$/mu.exec(fs25.readFileSync(dotGit, "utf8"))?.[1]?.trim();
22796
23898
  return {
22797
23899
  root: dir,
22798
- workit: Boolean(admin) && fs25.existsSync(path33.join(path33.resolve(dir, admin), FANOUT_TOKEN_FILE))
23900
+ workit: Boolean(admin) && fs25.existsSync(path34.join(path34.resolve(dir, admin), FANOUT_TOKEN_FILE))
22799
23901
  };
22800
23902
  }
22801
- if (path33.dirname(dir) === dir)
23903
+ if (path34.dirname(dir) === dir)
22802
23904
  return null;
22803
23905
  }
22804
23906
  };
@@ -22810,8 +23912,8 @@ var workitWorktreeOf = (file) => {
22810
23912
  return null;
22811
23913
  }
22812
23914
  };
22813
- var bindingDir = () => path33.join(resolveStateDir(), "worktree-bind");
22814
- var bindingFile = (session, agentId) => path33.join(bindingDir(), `${createHash17("sha256").update(`${session}\x00${agentId}`).digest("hex").slice(0, 32)}.json`);
23915
+ var bindingDir = () => path34.join(resolveStateDir(), "worktree-bind");
23916
+ var bindingFile = (session, agentId) => path34.join(bindingDir(), `${createHash17("sha256").update(`${session}\x00${agentId}`).digest("hex").slice(0, 32)}.json`);
22815
23917
  var readBinding = (file) => {
22816
23918
  try {
22817
23919
  const value = JSON.parse(fs25.readFileSync(file, "utf8"));
@@ -22836,7 +23938,7 @@ var bind = (input, worktree) => {
22836
23938
  agentId,
22837
23939
  at: new Date().toISOString()
22838
23940
  };
22839
- fs25.mkdirSync(path33.dirname(file), { recursive: true });
23941
+ fs25.mkdirSync(path34.dirname(file), { recursive: true });
22840
23942
  fs25.writeFileSync(file, JSON.stringify(binding));
22841
23943
  try {
22842
23944
  if (inWorkitWorkspace(input.cwd))
@@ -22855,27 +23957,27 @@ var hostIsolated = (cwd, agentId) => {
22855
23957
  const checkout = checkoutOf2(cwd);
22856
23958
  if (!checkout)
22857
23959
  return false;
22858
- const dotGit = path33.join(checkout.root, ".git");
23960
+ const dotGit = path34.join(checkout.root, ".git");
22859
23961
  if (!fs25.statSync(dotGit).isFile())
22860
23962
  return false;
22861
23963
  const admin = /^gitdir:\s*(.+)$/mu.exec(fs25.readFileSync(dotGit, "utf8"))?.[1]?.trim();
22862
23964
  if (!admin)
22863
23965
  return false;
22864
- const adminDir = path33.resolve(checkout.root, admin);
22865
- const back = fs25.readFileSync(path33.join(adminDir, "gitdir"), "utf8").trim();
22866
- if (path33.relative(canonical3(path33.resolve(adminDir, back)), canonical3(dotGit)) !== "")
23966
+ const adminDir = path34.resolve(checkout.root, admin);
23967
+ const back = fs25.readFileSync(path34.join(adminDir, "gitdir"), "utf8").trim();
23968
+ if (path34.relative(canonical3(path34.resolve(adminDir, back)), canonical3(dotGit)) !== "")
22867
23969
  return false;
22868
- const common = path33.join(adminDir, "commondir");
22869
- const gitDir = fs25.existsSync(common) ? path33.resolve(adminDir, fs25.readFileSync(common, "utf8").trim()) : adminDir;
22870
- const expected = path33.join(path33.dirname(canonical3(gitDir)), ".claude", "worktrees", `agent-${agentId}`);
22871
- return path33.relative(canonical3(expected), checkout.root) === "";
23970
+ const common = path34.join(adminDir, "commondir");
23971
+ const gitDir = fs25.existsSync(common) ? path34.resolve(adminDir, fs25.readFileSync(common, "utf8").trim()) : adminDir;
23972
+ const expected = path34.join(path34.dirname(canonical3(gitDir)), ".claude", "worktrees", `agent-${agentId}`);
23973
+ return path34.relative(canonical3(expected), checkout.root) === "";
22872
23974
  } catch {
22873
23975
  return false;
22874
23976
  }
22875
23977
  };
22876
- var shown = (target) => path33.basename(target) === ".workit-live-target" ? path33.dirname(target) : target;
23978
+ var shown = (target) => path34.basename(target) === ".workit-live-target" ? path34.dirname(target) : target;
22877
23979
  var unboundDeny = (input, target) => {
22878
- 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.";
23980
+ 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 git --cwd <worktree> commit -m … -- <paths>`, `workit check --cwd <worktree> <name>`). If your brief names no worktree, stop and report that to the lead.";
22879
23981
  return {
22880
23982
  kind: "deny",
22881
23983
  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}`,
@@ -22883,7 +23985,7 @@ var unboundDeny = (input, target) => {
22883
23985
  };
22884
23986
  };
22885
23987
  var outsideDeny = (input, worktree, target) => {
22886
- 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.`;
23988
+ const unblock = `Edit only under ${worktree} and run git there, one verb per call: \`workit git --cwd ${worktree} commit -m … -- <paths>\`, \`workit check --cwd ${worktree} <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.`;
22887
23989
  return {
22888
23990
  kind: "deny",
22889
23991
  reason: `workit worktree confinement: implementer ${input.session.agentId} is bound to the worktree ${worktree}, and ${shown(target)} is outside it. ${unblock}`,
@@ -22892,7 +23994,7 @@ var outsideDeny = (input, worktree, target) => {
22892
23994
  };
22893
23995
  var unknownDirDeny = (input, worktree) => {
22894
23996
  const wt = worktree ?? "<worktree>";
22895
- const unblock = `Workit cannot resolve \`cd -\`, \`cd "$X"\` or \`popd\`: write by absolute path, \`git -C ${wt}\` or \`workit --cwd ${wt}\`.`;
23997
+ const unblock = `Workit cannot resolve \`cd -\`, \`cd "$X"\` or \`popd\`: write by absolute path, \`git -C ${wt}\` or \`workit <verb> --cwd ${wt}\`.`;
22896
23998
  return {
22897
23999
  kind: "deny",
22898
24000
  reason: `workit worktree confinement: implementer ${input.session.agentId} writes after an unknown directory change. ${unblock}`,
@@ -22903,7 +24005,7 @@ function confineImplementer(input, targets) {
22903
24005
  try {
22904
24006
  if (!input.session.agentId || hostIsolated(input.cwd, input.session.agentId))
22905
24007
  return NONE5;
22906
- if (targets.some((target) => within(path33.resolve(target), UNKNOWN_DIR)))
24008
+ if (targets.some((target) => within(path34.resolve(target), UNKNOWN_DIR)))
22907
24009
  return unknownDirDeny(input, boundWorktree(input));
22908
24010
  const bindings = canonical3(bindingDir());
22909
24011
  const judged = targets.map((named) => ({ named, real: canonical3(named) })).filter(({ real }) => within(real, bindings) || checkoutOf2(real) !== null);
@@ -23050,7 +24152,7 @@ var subagentStartText = (input, descriptor, event) => {
23050
24152
  return `${isolatedGit(input.cwd)}
23051
24153
  ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
23052
24154
  if (role === "implementer")
23053
- 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.`;
24155
+ 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 git --cwd <worktree> commit -m "<message>" -- <paths>\`, \`workit check --cwd <worktree> <name>\` (verb first, so host allow rules match). The worktree is on the brief's branch once the lead made it; otherwise switch with \`workit git --cwd <worktree> 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.`;
23054
24156
  const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
23055
24157
  if (role !== "judge" || !event.agentId)
23056
24158
  return readOnly;
@@ -23067,7 +24169,7 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
23067
24169
  var PRE_TOOL = new Set(["shell.pre", "write.pre", "tool.pre"]);
23068
24170
  function handleHook(input, deps) {
23069
24171
  const decision = decide(input, deps);
23070
- if (deps.descriptor.transport !== "hook-process" || !PRE_TOOL.has(input.event.kind) || decision.kind === "deny")
24172
+ if (deps.descriptor.transport !== "hook-process" || !PRE_TOOL.has(input.event.kind) || decision.kind === "deny" || decision.kind === "ask")
23071
24173
  return decision;
23072
24174
  const delta = standingDelta(input);
23073
24175
  if (!delta)
@@ -23134,9 +24236,12 @@ function decide(input, deps) {
23134
24236
  if (plan.kind === "rewrite" && !rewrites && canDeny)
23135
24237
  return heavyDenial(event.command, plan);
23136
24238
  const lease = plan.kind === "rewrite" && rewrites ? plan : null;
23137
- const cwdCommand = !lease && rewrites && input.host === "claude_code" ? cwdRewrite(event.command) : null;
24239
+ const cwdCommand = !lease && rewrites && input.host === "claude_code" ? cwdRewrite(event.command) ?? verbFirst(event.command) : null;
23138
24240
  const aside = plan.kind === "passthrough" ? plan.note : null;
23139
24241
  noteRawCommit(input, event.command, postTool ? callKey(event.toolUseId, event.command) : NEXT_COMMAND);
24242
+ const bypass = !lease && input.host === "claude_code" ? workitBypassFlag(event.command, event.dialect) : null;
24243
+ if (bypass)
24244
+ return { kind: "ask", reason: bypass };
23140
24245
  const rewrittenTo = lease?.command ?? cwdCommand;
23141
24246
  const rewritten = (text) => rewrittenTo ? {
23142
24247
  kind: "rewrite",
@@ -23496,14 +24601,14 @@ var observeToolResult = (event, ctx) => {
23496
24601
  var skillPromptNudge = (prompt, ctx) => process.env.WORKIT_PI_WORKER_SESSION ? null : promptNudge(hookInput(ctx, { kind: "context.turn", prompt }), prompt);
23497
24602
 
23498
24603
  // packages/workit-pi/src/launches.ts
23499
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync6, readdirSync as readdirSync5, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
24604
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync7, readdirSync as readdirSync5, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
23500
24605
  import os11 from "node:os";
23501
- import path34 from "node:path";
24606
+ import path35 from "node:path";
23502
24607
  var launchDir = (root) => {
23503
24608
  const location = resolveStore(root);
23504
- return location instanceof Error ? null : path34.join(location.dir, "pi-launches");
24609
+ return location instanceof Error ? null : path35.join(location.dir, "pi-launches");
23505
24610
  };
23506
- var fileFor = (dir, workerId) => path34.join(dir, `${workerId.replace(/[^A-Za-z0-9-]/g, "_")}.json`);
24611
+ var fileFor = (dir, workerId) => path35.join(dir, `${workerId.replace(/[^A-Za-z0-9-]/g, "_")}.json`);
23507
24612
  var recordLaunch = (root, attempt) => {
23508
24613
  const dir = launchDir(root);
23509
24614
  if (!dir)
@@ -23549,7 +24654,7 @@ var abandonedLaunches = (root) => {
23549
24654
  const found = [];
23550
24655
  for (const name of names) {
23551
24656
  try {
23552
- const attempt = JSON.parse(readFileSync6(path34.join(dir, name), "utf8"));
24657
+ const attempt = JSON.parse(readFileSync7(path35.join(dir, name), "utf8"));
23553
24658
  if (typeof attempt.workerId === "string" && typeof attempt.session === "string") {
23554
24659
  if (!alive(attempt))
23555
24660
  found.push(attempt);