@brainervirus/workit-opencode 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/plugin.js CHANGED
@@ -5,8 +5,8 @@ function define(plugin) {
5
5
 
6
6
  // packages/workit-core/src/core.ts
7
7
  import { spawnSync as spawnSync16 } from "node:child_process";
8
- import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
9
- import path25 from "node:path";
8
+ import { existsSync as existsSync9, realpathSync as realpathSync5 } from "node:fs";
9
+ import path26 from "node:path";
10
10
 
11
11
  // packages/workit-core/src/core/task-contract.ts
12
12
  import { createHash, randomUUID } from "node:crypto";
@@ -8430,7 +8430,7 @@ var workspaceConfigSchema = object({
8430
8430
  }).passthrough().optional(),
8431
8431
  autoApprove: union([boolean2(), array(nonBlank)]).optional(),
8432
8432
  defaultEndpoint: unknown().optional(),
8433
- verification: _enum(["self", "independent"]).optional(),
8433
+ verification: _enum(["proportional", "self", "independent"]).optional(),
8434
8434
  delegation: unknown().optional(),
8435
8435
  trailers: boolean2().optional(),
8436
8436
  liveSessionGuard: boolean2().optional(),
@@ -9015,6 +9015,144 @@ function resolveRef(cwd, ref) {
9015
9015
  return null;
9016
9016
  return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
9017
9017
  }
9018
+ function originHeadRef(cwd) {
9019
+ return line(git(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]));
9020
+ }
9021
+ function ownChangeDrift(cwd, range) {
9022
+ const before = diffRaw(cwd, range.fromFork, range.from);
9023
+ const after = diffRaw(cwd, range.toFork, range.to);
9024
+ if (!before || !after)
9025
+ return null;
9026
+ const judged = new Map(before.map((entry) => [entry.path, entry]));
9027
+ const current = new Map(after.map((entry) => [entry.path, entry]));
9028
+ const same = (a, b) => a !== undefined && b !== undefined && a.oldMode === b.oldMode && a.oldBlob === b.oldBlob && a.newMode === b.newMode && a.newBlob === b.newBlob;
9029
+ return [...new Set([...judged.keys(), ...current.keys()])].filter((file) => !same(judged.get(file), current.get(file))).toSorted();
9030
+ }
9031
+ function sameOwnChange(cwd, range) {
9032
+ const fromFork = mergeBase(cwd, range.fromBase, range.from);
9033
+ const toFork = mergeBase(cwd, range.toBase, range.to);
9034
+ if (!fromFork || !toFork)
9035
+ return false;
9036
+ return ownChangeDrift(cwd, { from: range.from, to: range.to, fromFork, toFork })?.length === 0;
9037
+ }
9038
+ var LANE_DIFF = [
9039
+ "-c",
9040
+ "core.quotePath=false",
9041
+ "diff",
9042
+ "--no-color",
9043
+ "--no-ext-diff",
9044
+ "--no-textconv",
9045
+ "--no-renames"
9046
+ ];
9047
+ var laneRange = (fork, head) => {
9048
+ if (!safeArg(fork) || head !== null && !safeArg(head))
9049
+ return null;
9050
+ return head === null ? [fork] : [fork, head];
9051
+ };
9052
+ function diffNumstat(cwd, fork, head) {
9053
+ const range = laneRange(fork, head);
9054
+ if (!range)
9055
+ return null;
9056
+ const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
9057
+ if (!run.ok)
9058
+ return null;
9059
+ const out = [];
9060
+ for (const record of run.stdout.split("\x00")) {
9061
+ const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
9062
+ if (!match)
9063
+ continue;
9064
+ const binary = match[1] === "-";
9065
+ out.push({
9066
+ path: match[3],
9067
+ added: binary ? 0 : Number(match[1]),
9068
+ deleted: binary ? 0 : Number(match[2]),
9069
+ binary
9070
+ });
9071
+ }
9072
+ return out;
9073
+ }
9074
+ function diffRaw(cwd, fork, head) {
9075
+ const range = laneRange(fork, head);
9076
+ if (!range)
9077
+ return null;
9078
+ const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
9079
+ if (!run.ok)
9080
+ return null;
9081
+ const fields = run.stdout.split("\x00");
9082
+ const out = [];
9083
+ for (let index = 0;index + 1 < fields.length; index += 2) {
9084
+ const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
9085
+ if (!meta)
9086
+ return null;
9087
+ out.push({
9088
+ path: fields[index + 1],
9089
+ oldMode: meta[1],
9090
+ newMode: meta[2],
9091
+ oldBlob: meta[3],
9092
+ newBlob: meta[4]
9093
+ });
9094
+ }
9095
+ return out;
9096
+ }
9097
+ function readBlobs(cwd, ids) {
9098
+ const out = new Map;
9099
+ const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
9100
+ if (!wanted.length)
9101
+ return out;
9102
+ const run = spawnSync3("git", ["cat-file", "--batch"], {
9103
+ cwd,
9104
+ input: `${wanted.join(`
9105
+ `)}
9106
+ `,
9107
+ maxBuffer: MAX_BUFFER,
9108
+ timeout: GIT_TIMEOUTS.local,
9109
+ killSignal: "SIGKILL",
9110
+ windowsHide: true
9111
+ });
9112
+ if (run.status !== 0 || !run.stdout)
9113
+ return null;
9114
+ const bytes = run.stdout;
9115
+ let at = 0;
9116
+ for (const id of wanted) {
9117
+ const end = bytes.indexOf(10, at);
9118
+ if (end < 0)
9119
+ return null;
9120
+ const header = bytes.subarray(at, end).toString("utf8").split(" ");
9121
+ at = end + 1;
9122
+ if (header[1] === "missing")
9123
+ continue;
9124
+ const size = Number(header[2]);
9125
+ if (header[0] !== id || !Number.isSafeInteger(size))
9126
+ return null;
9127
+ out.set(id, bytes.subarray(at, at + size));
9128
+ at += size + 1;
9129
+ }
9130
+ return out;
9131
+ }
9132
+ function treeFilesNamed(cwd, rev, name) {
9133
+ if (!safeArg(rev))
9134
+ return null;
9135
+ const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
9136
+ if (!run.ok)
9137
+ return null;
9138
+ const out = new Map;
9139
+ for (const record of run.stdout.split("\x00")) {
9140
+ const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
9141
+ if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
9142
+ out.set(match[2], match[1]);
9143
+ }
9144
+ return out;
9145
+ }
9146
+ function fileAt(cwd, rev, file) {
9147
+ if (!safeArg(rev))
9148
+ return null;
9149
+ const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
9150
+ return run.ok ? run.stdout : null;
9151
+ }
9152
+ function untrackedFiles(cwd) {
9153
+ const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
9154
+ return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
9155
+ }
9018
9156
  var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
9019
9157
  function currentBranch(cwd) {
9020
9158
  return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
@@ -12777,7 +12915,7 @@ var holdsLiveState = (state, dirty) => state === "running" || dirty === true;
12777
12915
  import { createHash as createHash12 } from "node:crypto";
12778
12916
  import * as fs14 from "node:fs";
12779
12917
  import { spawnSync as spawnSync10 } from "node:child_process";
12780
- import path17 from "node:path";
12918
+ import path18 from "node:path";
12781
12919
 
12782
12920
  // packages/workit-core/src/check-config.ts
12783
12921
  import { spawnSync as spawnSync8 } from "node:child_process";
@@ -13007,45 +13145,786 @@ function checkCommand(args, version, onPath = workitAvailable()) {
13007
13145
  }
13008
13146
 
13009
13147
  // packages/workit-core/src/core/policy/derive.ts
13010
- import { existsSync as existsSync4 } from "node:fs";
13011
- import path16 from "node:path";
13148
+ import { existsSync as existsSync5 } from "node:fs";
13149
+ import path17 from "node:path";
13012
13150
 
13013
13151
  // packages/workit-core/src/ledger.ts
13014
13152
  import { spawnSync as spawnSync9 } from "node:child_process";
13015
13153
  import { createHash as createHash11, randomBytes as randomBytes3 } from "node:crypto";
13016
13154
  import fs13 from "node:fs";
13017
- import path15 from "node:path";
13155
+ import path16 from "node:path";
13156
+
13157
+ // packages/workit-core/src/core/policy/lane.ts
13158
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
13159
+ import path14 from "node:path";
13160
+
13161
+ // packages/workit-core/src/git/default-base.ts
13162
+ var safeRef = (value) => value.length > 0 && !value.startsWith("-");
13163
+ var trackTrunk = (cwd) => {
13164
+ try {
13165
+ const resolved = vcsConfig("resolve", cwd);
13166
+ if (resolved.ok === false || !resolved.releaseTrack || resolved.releaseTrack.blocking)
13167
+ return null;
13168
+ const target = String(resolved.defaultTargetBranch ?? "").trim();
13169
+ return target && safeRef(target) ? target : null;
13170
+ } catch {
13171
+ return null;
13172
+ }
13173
+ };
13174
+ function defaultBase(cwd) {
13175
+ const trunk = trackTrunk(cwd);
13176
+ if (trunk) {
13177
+ for (const candidate of [`origin/${trunk}`, trunk])
13178
+ if (resolveRef(cwd, candidate))
13179
+ return candidate;
13180
+ }
13181
+ const originHead = originHeadRef(cwd);
13182
+ if (originHead && resolveRef(cwd, originHead))
13183
+ return originHead;
13184
+ for (const candidate of [
13185
+ "origin/main",
13186
+ "origin/master",
13187
+ "main",
13188
+ "master",
13189
+ "origin/develop",
13190
+ "develop"
13191
+ ])
13192
+ if (resolveRef(cwd, candidate))
13193
+ return candidate;
13194
+ return null;
13195
+ }
13196
+
13197
+ // packages/workit-core/src/core/policy/attributes.ts
13198
+ function patternRegex(dir, pattern) {
13199
+ if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
13200
+ return null;
13201
+ const anchored = pattern.includes("/");
13202
+ const body = pattern.replace(/^\//u, "");
13203
+ let source = "";
13204
+ for (let index = 0;index < body.length; index += 1) {
13205
+ const char = body[index];
13206
+ if (char === "*" && body[index + 1] === "*") {
13207
+ const slash = body[index + 2] === "/";
13208
+ source += slash ? "(?:.*/)?" : ".*";
13209
+ index += slash ? 2 : 1;
13210
+ } else if (char === "*")
13211
+ source += "[^/]*";
13212
+ else if (char === "?")
13213
+ source += "[^/]";
13214
+ else if (char === "[") {
13215
+ const end = body.indexOf("]", index + 2);
13216
+ if (end < 0)
13217
+ return null;
13218
+ const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
13219
+ source += `[${set}]`;
13220
+ index = end;
13221
+ } else if (char === "\\" && index + 1 < body.length) {
13222
+ source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
13223
+ index += 1;
13224
+ } else
13225
+ source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
13226
+ }
13227
+ const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
13228
+ try {
13229
+ return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
13230
+ } catch {
13231
+ return null;
13232
+ }
13233
+ }
13234
+ function rulesOf(dir, text) {
13235
+ const out = [];
13236
+ for (const raw of text.split(`
13237
+ `)) {
13238
+ const line = raw.trim();
13239
+ if (!line || line.startsWith("#") || line.startsWith("[attr]"))
13240
+ continue;
13241
+ const [pattern, ...attributes] = line.split(/\s+/u);
13242
+ let generated = null;
13243
+ for (const attribute of attributes) {
13244
+ if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
13245
+ generated = true;
13246
+ else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
13247
+ generated = false;
13248
+ }
13249
+ if (generated === null)
13250
+ continue;
13251
+ const regex = patternRegex(dir, pattern);
13252
+ if (regex)
13253
+ out.push({ regex, generated });
13254
+ }
13255
+ return out;
13256
+ }
13257
+ function generatedByAttributes(files, paths) {
13258
+ const rules = [...files].map(([file, text]) => {
13259
+ const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
13260
+ return {
13261
+ depth: dir ? dir.split("/").length : 0,
13262
+ rules: rulesOf(dir, text)
13263
+ };
13264
+ }).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
13265
+ const out = new Set;
13266
+ for (const file of paths) {
13267
+ const last = rules.findLast((rule) => rule.regex.test(file));
13268
+ if (last?.generated)
13269
+ out.add(file);
13270
+ }
13271
+ return out;
13272
+ }
13273
+
13274
+ // packages/workit-core/src/core/policy/substance.ts
13275
+ var SLASH_BLOCK = [["/*", "*/"]];
13276
+ var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
13277
+ var SYNTAXES = Object.fromEntries([
13278
+ ...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
13279
+ ...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
13280
+ ext,
13281
+ { ...C_LIKE, triple: true }
13282
+ ]),
13283
+ ["go", { ...C_LIKE, rawBacktick: true }],
13284
+ ...["css", "scss", "less"].map((ext) => [
13285
+ ext,
13286
+ { line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
13287
+ ]),
13288
+ ...["py", "pyi"].map((ext) => [
13289
+ ext,
13290
+ { line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
13291
+ ]),
13292
+ ["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
13293
+ ...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
13294
+ ext,
13295
+ { line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
13296
+ ])
13297
+ ]);
13298
+ 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;
13299
+ var extensionOf = (file) => {
13300
+ const name = file.slice(file.lastIndexOf("/") + 1);
13301
+ const dot = name.lastIndexOf(".");
13302
+ return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
13303
+ };
13304
+ var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
13305
+ var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
13306
+ var SPACE = /[ \t\r\f\v]/u;
13307
+ var WORD = /[\w$]/u;
13308
+ var NO_SPACE_AFTER = "([{,;";
13309
+ var NO_SPACE_BEFORE = ")]},;";
13310
+ var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
13311
+ var REGEX_KEYWORDS = new Set([
13312
+ "return",
13313
+ "typeof",
13314
+ "instanceof",
13315
+ "case",
13316
+ "do",
13317
+ "else",
13318
+ "in",
13319
+ "of",
13320
+ "new",
13321
+ "delete",
13322
+ "void",
13323
+ "throw",
13324
+ "yield",
13325
+ "await"
13326
+ ]);
13327
+ function substanceOf(file, text) {
13328
+ const syntax = syntaxOf(file);
13329
+ if (!syntax)
13330
+ return plainSubstance(text);
13331
+ const markup = syntax.block.some(([open]) => open === "<!--");
13332
+ if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
13333
+ return plainSubstance(text);
13334
+ return lex(syntax, text);
13335
+ }
13336
+ function plainSubstance(text) {
13337
+ return text.split(`
13338
+ `).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
13339
+ `);
13340
+ }
13341
+ function lex(syntax, text) {
13342
+ const collapse = !syntax.indented;
13343
+ let out = "";
13344
+ let lineStart = 0;
13345
+ let kept = 0;
13346
+ let pendingSpace = false;
13347
+ const frames = [{ kind: "code", braces: 0 }];
13348
+ let index = 0;
13349
+ if (text.startsWith("#!")) {
13350
+ const end = text.indexOf(`
13351
+ `);
13352
+ out = end < 0 ? text : text.slice(0, end);
13353
+ kept = out.length;
13354
+ index = end < 0 ? text.length : end;
13355
+ }
13356
+ const emitCode = (chunk) => {
13357
+ if (collapse && pendingSpace && out.length > lineStart) {
13358
+ const before = out[out.length - 1];
13359
+ if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
13360
+ out += " ";
13361
+ }
13362
+ pendingSpace = false;
13363
+ out += chunk;
13364
+ };
13365
+ const emitVerbatim = (chunk) => {
13366
+ emitCode(chunk);
13367
+ kept = out.length;
13368
+ };
13369
+ const newline = () => {
13370
+ if (!collapse) {
13371
+ let end = out.length;
13372
+ while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
13373
+ end -= 1;
13374
+ out = out.slice(0, end);
13375
+ }
13376
+ pendingSpace = false;
13377
+ if (out.length > lineStart) {
13378
+ out += `
13379
+ `;
13380
+ lineStart = out.length;
13381
+ }
13382
+ };
13383
+ const regexAllowed = () => {
13384
+ let end = out.length;
13385
+ while (end > 0 && /\s/u.test(out[end - 1]))
13386
+ end -= 1;
13387
+ if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
13388
+ return true;
13389
+ let start = end;
13390
+ while (start > 0 && WORD.test(out[start - 1]))
13391
+ start -= 1;
13392
+ return REGEX_KEYWORDS.has(out.slice(start, end));
13393
+ };
13394
+ const scanString = (quote, escapes, multiline) => {
13395
+ const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
13396
+ const fstring = syntax.fstring === true && /f/iu.test(prefix);
13397
+ let end = index + quote.length;
13398
+ while (end < text.length) {
13399
+ const char = text[end];
13400
+ if (escapes && char === "\\") {
13401
+ end += 2;
13402
+ continue;
13403
+ }
13404
+ if (text.startsWith(quote, end)) {
13405
+ if (fstring && unbalanced(text.slice(index + quote.length, end)))
13406
+ return false;
13407
+ emitVerbatim(text.slice(index, end + quote.length));
13408
+ index = end + quote.length;
13409
+ return true;
13410
+ }
13411
+ if (char === `
13412
+ ` && !multiline)
13413
+ return false;
13414
+ end += 1;
13415
+ }
13416
+ return false;
13417
+ };
13418
+ while (index < text.length) {
13419
+ const frame = frames[frames.length - 1];
13420
+ const char = text[index];
13421
+ if (frame.kind === "template") {
13422
+ if (char === "\\") {
13423
+ emitVerbatim(text.slice(index, index + 2));
13424
+ index += 2;
13425
+ } else if (char === "`") {
13426
+ emitVerbatim(char);
13427
+ frames.pop();
13428
+ index += 1;
13429
+ } else if (text.startsWith("${", index)) {
13430
+ emitVerbatim("${");
13431
+ frames.push({ kind: "code", braces: 0 });
13432
+ index += 2;
13433
+ } else {
13434
+ out += char;
13435
+ kept = out.length;
13436
+ index += 1;
13437
+ }
13438
+ continue;
13439
+ }
13440
+ if (char === `
13441
+ `) {
13442
+ newline();
13443
+ index += 1;
13444
+ continue;
13445
+ }
13446
+ if (SPACE.test(char)) {
13447
+ if (collapse)
13448
+ pendingSpace = true;
13449
+ else
13450
+ out += char;
13451
+ index += 1;
13452
+ continue;
13453
+ }
13454
+ const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
13455
+ if (lineOpener) {
13456
+ const end = text.indexOf(`
13457
+ `, index);
13458
+ const comment = text.slice(index, end < 0 ? text.length : end);
13459
+ if (DIRECTIVE.test(comment.slice(lineOpener.length)))
13460
+ emitVerbatim(comment);
13461
+ else
13462
+ pendingSpace = true;
13463
+ index = end < 0 ? text.length : end;
13464
+ continue;
13465
+ }
13466
+ const block = syntax.block.find(([open]) => text.startsWith(open, index));
13467
+ if (block) {
13468
+ const end = text.indexOf(block[1], index + block[0].length);
13469
+ if (end < 0)
13470
+ return null;
13471
+ const comment = text.slice(index, end + block[1].length);
13472
+ if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
13473
+ emitVerbatim(comment);
13474
+ else
13475
+ pendingSpace = true;
13476
+ if (comment.includes(`
13477
+ `))
13478
+ newline();
13479
+ index = end + block[1].length;
13480
+ continue;
13481
+ }
13482
+ if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
13483
+ if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
13484
+ return null;
13485
+ continue;
13486
+ }
13487
+ if (syntax.quotes.includes(char)) {
13488
+ if (!scanString(char, true, Boolean(syntax.multiline)))
13489
+ return null;
13490
+ continue;
13491
+ }
13492
+ if (syntax.literal?.includes(char)) {
13493
+ if (!scanString(char, false, false))
13494
+ return null;
13495
+ continue;
13496
+ }
13497
+ if (syntax.rawBacktick && char === "`") {
13498
+ if (!scanString("`", false, true))
13499
+ return null;
13500
+ continue;
13501
+ }
13502
+ if (syntax.js && char === "`") {
13503
+ emitVerbatim(char);
13504
+ frames.push({ kind: "template" });
13505
+ index += 1;
13506
+ continue;
13507
+ }
13508
+ if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
13509
+ return null;
13510
+ if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
13511
+ const lineEnd = text.indexOf(`
13512
+ `, index);
13513
+ if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
13514
+ return null;
13515
+ }
13516
+ if (syntax.js && char === "/" && regexAllowed()) {
13517
+ let end = index + 1;
13518
+ let inClass = false;
13519
+ while (end < text.length && (inClass || text[end] !== "/")) {
13520
+ if (text[end] === `
13521
+ `)
13522
+ return null;
13523
+ if (text[end] === "\\")
13524
+ end += 1;
13525
+ else if (text[end] === "[")
13526
+ inClass = true;
13527
+ else if (text[end] === "]")
13528
+ inClass = false;
13529
+ end += 1;
13530
+ }
13531
+ if (end >= text.length)
13532
+ return null;
13533
+ emitVerbatim(text.slice(index, end + 1));
13534
+ index = end + 1;
13535
+ continue;
13536
+ }
13537
+ if (frames.length > 1 && frame.kind === "code") {
13538
+ if (char === "{")
13539
+ frame.braces += 1;
13540
+ else if (char === "}") {
13541
+ if (frame.braces === 0) {
13542
+ emitVerbatim(char);
13543
+ frames.pop();
13544
+ index += 1;
13545
+ continue;
13546
+ }
13547
+ frame.braces -= 1;
13548
+ }
13549
+ }
13550
+ emitCode(char);
13551
+ index += 1;
13552
+ }
13553
+ if (frames.length !== 1)
13554
+ return null;
13555
+ newline();
13556
+ return out;
13557
+ }
13558
+ function unbalanced(body) {
13559
+ let depth = 0;
13560
+ for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
13561
+ if (part === "{")
13562
+ depth += 1;
13563
+ else if (part === "}")
13564
+ depth = Math.max(0, depth - 1);
13565
+ }
13566
+ return depth > 0;
13567
+ }
13568
+ function hasOpaqueLineBreak(text) {
13569
+ return /\r(?!\n)|[\u2028\u2029]/u.test(text);
13570
+ }
13571
+ var decoder = new TextDecoder("utf-8", { fatal: true });
13572
+ function textOf(bytes) {
13573
+ if (bytes.includes(0))
13574
+ return null;
13575
+ try {
13576
+ return decoder.decode(bytes);
13577
+ } catch {
13578
+ return null;
13579
+ }
13580
+ }
13581
+ function cosmeticChange(file, before, after) {
13582
+ const left = textOf(before);
13583
+ const right = textOf(after);
13584
+ if (left === null || right === null)
13585
+ return false;
13586
+ if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
13587
+ return false;
13588
+ const a = substanceOf(file, left);
13589
+ return a !== null && a === substanceOf(file, right);
13590
+ }
13591
+
13592
+ // packages/workit-core/src/core/policy/lane.ts
13593
+ var LANE_THRESHOLDS = {
13594
+ selfMaxLines: 100,
13595
+ selfMaxFiles: 5,
13596
+ fullMinLines: 500
13597
+ };
13598
+ var RISK_TOKENS = new Set([
13599
+ "auth",
13600
+ "authn",
13601
+ "authz",
13602
+ "oauth",
13603
+ "security",
13604
+ "permission",
13605
+ "permissions",
13606
+ "secret",
13607
+ "secrets",
13608
+ "crypto",
13609
+ "gate",
13610
+ "gates",
13611
+ "policy",
13612
+ "policies",
13613
+ "hook",
13614
+ "hooks",
13615
+ "husky",
13616
+ "lefthook",
13617
+ "release",
13618
+ "releases",
13619
+ "migration",
13620
+ "migrations",
13621
+ "migrate",
13622
+ "workflows"
13623
+ ]);
13624
+ var RISK_FILES = new Set([
13625
+ ".gitlab-ci.yml",
13626
+ "Jenkinsfile",
13627
+ "azure-pipelines.yml",
13628
+ ".pre-commit-config.yaml",
13629
+ ".workit/risk-paths"
13630
+ ]);
13631
+ var RISK_NAMES = new Set([".gitattributes"]);
13632
+ var RISK_PATHS_FILE = ".workit/risk-paths";
13633
+ var LOCKFILES = new Set([
13634
+ "bun.lock",
13635
+ "bun.lockb",
13636
+ "package-lock.json",
13637
+ "npm-shrinkwrap.json",
13638
+ "yarn.lock",
13639
+ "pnpm-lock.yaml",
13640
+ "Cargo.lock",
13641
+ "go.sum",
13642
+ "poetry.lock",
13643
+ "uv.lock",
13644
+ "Pipfile.lock",
13645
+ "Gemfile.lock",
13646
+ "composer.lock"
13647
+ ]);
13648
+ var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc)$/iu;
13649
+ var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
13650
+ var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
13651
+ var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__)\/|\.(?:test|spec)\.[^/]+$|_test\.[^/]+$/iu;
13652
+ var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
13653
+ var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
13654
+ function kindOf(file) {
13655
+ if (file.binary)
13656
+ return "behavior";
13657
+ if (file.generated)
13658
+ return "generated";
13659
+ if (LOCKFILES.has(basename(file.path)))
13660
+ return "lockfile";
13661
+ if (isDoc(file.path))
13662
+ return "doc";
13663
+ if (file.cosmetic)
13664
+ return "cosmetic";
13665
+ return TEST_PATH.test(file.path) ? "test" : "behavior";
13666
+ }
13667
+ var globCache = new Map;
13668
+ function globMatch(pattern, file) {
13669
+ let regex = globCache.get(pattern);
13670
+ if (!regex) {
13671
+ let source = "";
13672
+ for (let index = 0;index < pattern.length; index += 1) {
13673
+ const char = pattern[index];
13674
+ if (char === "*" && pattern[index + 1] === "*") {
13675
+ source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
13676
+ index += pattern[index + 2] === "/" ? 2 : 1;
13677
+ } else if (char === "*")
13678
+ source += "[^/]*";
13679
+ else if (char === "?")
13680
+ source += "[^/]";
13681
+ else
13682
+ source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
13683
+ }
13684
+ regex = new RegExp(`^${source}$`, "u");
13685
+ globCache.set(pattern, regex);
13686
+ }
13687
+ return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
13688
+ }
13689
+ function isRiskPath(file, extra = []) {
13690
+ if (extra.some((pattern) => globMatch(pattern, file)))
13691
+ return true;
13692
+ if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
13693
+ return true;
13694
+ if (TEST_PATH.test(file) || isDoc(file))
13695
+ return false;
13696
+ 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)));
13697
+ }
13698
+ var parseRiskPaths = (text) => text.split(`
13699
+ `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
13700
+ function computeLane(input) {
13701
+ const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
13702
+ const risky = new Set(riskFiles);
13703
+ const kinds = input.files.map((file) => ({
13704
+ file,
13705
+ kind: risky.has(file.path) ? "behavior" : kindOf(file)
13706
+ }));
13707
+ const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
13708
+ const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
13709
+ const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
13710
+ const result = (lane, reasons) => ({
13711
+ lane,
13712
+ reasons,
13713
+ lines,
13714
+ files: behavior.length,
13715
+ riskFiles
13716
+ });
13717
+ const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
13718
+ if (input.riskTier === "high")
13719
+ return result("full", ["the task judgment records high risk"]);
13720
+ if (riskFiles.length)
13721
+ return result("full", [
13722
+ `touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
13723
+ ]);
13724
+ if (!behavior.length)
13725
+ return result("none", [
13726
+ input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
13727
+ ]);
13728
+ if (lines >= fullMinLines)
13729
+ return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
13730
+ if (lines > selfMaxLines)
13731
+ return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
13732
+ if (behavior.length > selfMaxFiles)
13733
+ return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
13734
+ if (opaque.length)
13735
+ return result("focused", [
13736
+ `a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
13737
+ ]);
13738
+ return result("self", [
13739
+ `small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
13740
+ ]);
13741
+ }
13742
+ function untrackedStat(cwd, file) {
13743
+ try {
13744
+ const bytes = readFileSync4(path14.join(cwd, file));
13745
+ if (bytes.includes(0))
13746
+ return { path: file, added: 0, deleted: 0, binary: true };
13747
+ const text = bytes.toString("utf8");
13748
+ if (hasOpaqueLineBreak(text))
13749
+ return { path: file, added: 0, deleted: 0, binary: true };
13750
+ const lines = text.split(`
13751
+ `).length - (text.endsWith(`
13752
+ `) ? 1 : 0);
13753
+ return { path: file, added: lines, deleted: 0 };
13754
+ } catch {
13755
+ return { path: file, added: 0, deleted: 0, binary: true };
13756
+ }
13757
+ }
13758
+ var REGULAR = new Set(["100644", "100755"]);
13759
+ var ABSENT = /^0+$/u;
13760
+ function cosmeticFiles(cwd, fork, head) {
13761
+ const raw = diffRaw(cwd, fork, head);
13762
+ if (!raw)
13763
+ return null;
13764
+ const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
13765
+ const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
13766
+ if (!blobs)
13767
+ return null;
13768
+ const out = new Set;
13769
+ const opaque = new Set;
13770
+ for (const entry of candidates) {
13771
+ const before = blobs.get(entry.oldBlob);
13772
+ let after = blobs.get(entry.newBlob);
13773
+ if (!after && head === null && ABSENT.test(entry.newBlob))
13774
+ try {
13775
+ after = readFileSync4(path14.join(cwd, entry.path));
13776
+ } catch {
13777
+ after = undefined;
13778
+ }
13779
+ if (before && after && cosmeticChange(entry.path, before, after))
13780
+ out.add(entry.path);
13781
+ else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
13782
+ opaque.add(entry.path);
13783
+ }
13784
+ return { cosmetic: out, opaque };
13785
+ }
13786
+ function generatedAt(cwd, fork, paths) {
13787
+ const files = treeFilesNamed(cwd, fork, ".gitattributes");
13788
+ const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
13789
+ if (!files || !blobs)
13790
+ return new Set;
13791
+ const texts = new Map;
13792
+ for (const [file, id] of files) {
13793
+ const bytes = blobs.get(id);
13794
+ if (bytes)
13795
+ texts.set(file, bytes.toString("utf8"));
13796
+ }
13797
+ return generatedByAttributes(texts, paths);
13798
+ }
13799
+ var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
13800
+ function laneFiles(cwd, fork, head, attributesAt = fork) {
13801
+ const stats = diffNumstat(cwd, fork, head);
13802
+ const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
13803
+ if (!stats || !cosmetic)
13804
+ return null;
13805
+ const files = [
13806
+ ...stats.map((entry) => ({
13807
+ ...entry,
13808
+ ...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
13809
+ cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
13810
+ })),
13811
+ ...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
13812
+ ];
13813
+ const generated = generatedAt(cwd, attributesAt, files.map((entry) => entry.path));
13814
+ return files.map((entry) => ({
13815
+ ...entry,
13816
+ generated: generated.has(entry.path)
13817
+ }));
13818
+ }
13819
+ function driftFiles(cwd, range) {
13820
+ if (range.fromFork === range.toFork)
13821
+ return laneFiles(cwd, range.from, range.to, range.toFork);
13822
+ const drifted = ownChangeDrift(cwd, range);
13823
+ const stats = drifted ? diffNumstat(cwd, range.from, range.to) : null;
13824
+ if (!drifted || !stats)
13825
+ return null;
13826
+ const byPath = new Map(stats.map((entry) => [entry.path, entry]));
13827
+ const generated = generatedAt(cwd, range.toFork, drifted);
13828
+ return drifted.map((file) => ({
13829
+ path: file,
13830
+ added: byPath.get(file)?.added ?? 0,
13831
+ deleted: byPath.get(file)?.deleted ?? 0,
13832
+ ...byPath.get(file)?.binary ? { binary: true } : {},
13833
+ cosmetic: false,
13834
+ generated: generated.has(file)
13835
+ }));
13836
+ }
13837
+ function branchLane(cwd, options) {
13838
+ const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
13839
+ const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
13840
+ const head = options.workingTree ? null : options.head;
13841
+ const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
13842
+ const files = fork ? laneFiles(cwd, fork, head) : null;
13843
+ const riskTier = options.judgment?.riskTier ?? null;
13844
+ if (!fork || !files) {
13845
+ const high = riskTier === "high";
13846
+ return {
13847
+ lane: high ? "full" : "focused",
13848
+ reasons: [
13849
+ `the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
13850
+ ...high ? ["the task judgment records high risk"] : []
13851
+ ],
13852
+ lines: 0,
13853
+ files: 0,
13854
+ riskFiles: [],
13855
+ base,
13856
+ head: options.head
13857
+ };
13858
+ }
13859
+ const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
13860
+ return {
13861
+ ...computeLane({
13862
+ files,
13863
+ riskTier,
13864
+ riskPaths: [...repoRisk, ...options.riskPaths ?? []]
13865
+ }),
13866
+ base,
13867
+ head: options.head
13868
+ };
13869
+ }
13870
+ var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
13871
+
13872
+ // packages/workit-core/src/core/policy/carry.ts
13873
+ var TEST_ONLY_KINDS = new Set(["doc", "cosmetic", "generated", "test"]);
13874
+ function verdictDrift(cwd, input) {
13875
+ const since = input.from;
13876
+ const stale = (reason, files = null, focused = false) => ({
13877
+ carries: false,
13878
+ since,
13879
+ files,
13880
+ focused,
13881
+ reason
13882
+ });
13883
+ const fromFork = mergeBase(cwd, input.fromBase, input.from);
13884
+ const toFork = mergeBase(cwd, input.toBase, input.to);
13885
+ const files = fromFork && toFork ? driftFiles(cwd, { from: input.from, to: input.to, fromFork, toFork }) : null;
13886
+ if (!fromFork || !toFork || !files)
13887
+ return stale(`the change since ${since.slice(0, 12)} cannot be read`);
13888
+ const riskPaths = [fromFork, toFork].flatMap((fork) => parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? ""));
13889
+ const lane = computeLane({ files, riskPaths });
13890
+ const paths = files.map((file) => file.path);
13891
+ if (lane.lane === "none")
13892
+ return { carries: true, rule: "lane-none", since, files: paths };
13893
+ if (input.kind === "unit" && lane.lane === "self" && files.every((file) => TEST_ONLY_KINDS.has(kindOf(file))))
13894
+ return { carries: true, rule: "tests-only", since, files: paths };
13895
+ return stale(lane.reasons[0] ?? `lane ${lane.lane}`, paths, lane.lane === "self");
13896
+ }
13018
13897
 
13019
13898
  // packages/workit-core/src/hooks/standing-signal.ts
13020
13899
  import { createHash as createHash10 } from "node:crypto";
13021
13900
  import fs12 from "node:fs";
13022
- import path14 from "node:path";
13901
+ import path15 from "node:path";
13023
13902
  var gitStoreDir = (cwd) => {
13024
13903
  try {
13025
- for (let dir = path14.resolve(cwd);; dir = path14.dirname(dir)) {
13026
- const dotGit = path14.join(dir, ".git");
13904
+ for (let dir = path15.resolve(cwd);; dir = path15.dirname(dir)) {
13905
+ const dotGit = path15.join(dir, ".git");
13027
13906
  if (fs12.existsSync(dotGit)) {
13028
13907
  let gitDir = dotGit;
13029
13908
  if (fs12.statSync(dotGit).isFile()) {
13030
13909
  const target = /^gitdir:\s*(.+)$/mu.exec(fs12.readFileSync(dotGit, "utf8"))?.[1]?.trim();
13031
13910
  if (!target)
13032
13911
  return null;
13033
- gitDir = path14.resolve(dir, target);
13034
- const common = path14.join(gitDir, "commondir");
13912
+ gitDir = path15.resolve(dir, target);
13913
+ const common = path15.join(gitDir, "commondir");
13035
13914
  if (fs12.existsSync(common))
13036
- gitDir = path14.resolve(gitDir, fs12.readFileSync(common, "utf8").trim());
13915
+ gitDir = path15.resolve(gitDir, fs12.readFileSync(common, "utf8").trim());
13037
13916
  }
13038
- return path14.join(gitDir, "workit");
13917
+ return path15.join(gitDir, "workit");
13039
13918
  }
13040
- if (path14.dirname(dir) === dir)
13919
+ if (path15.dirname(dir) === dir)
13041
13920
  return null;
13042
13921
  }
13043
13922
  } catch {
13044
13923
  return null;
13045
13924
  }
13046
13925
  };
13047
- var standingRevFile = (store) => path14.join(store, "standing", "rev");
13048
- var standingSeenFile = (store, session, agentId) => path14.join(store, "standing", "seen", `${createHash10("sha256").update(`${session}\x00${agentId ?? ""}`).digest("hex").slice(0, 32)}.json`);
13926
+ var standingRevFile = (store) => path15.join(store, "standing", "rev");
13927
+ var standingSeenFile = (store, session, agentId) => path15.join(store, "standing", "seen", `${createHash10("sha256").update(`${session}\x00${agentId ?? ""}`).digest("hex").slice(0, 32)}.json`);
13049
13928
  var readStandingRev = (store) => {
13050
13929
  try {
13051
13930
  return fs12.readFileSync(standingRevFile(store), "utf8").trim() || null;
@@ -13066,7 +13945,7 @@ var readStandingSeen = (file) => {
13066
13945
  };
13067
13946
  var writeStandingSeen = (file, seen) => {
13068
13947
  try {
13069
- fs12.mkdirSync(path14.dirname(file), { recursive: true });
13948
+ fs12.mkdirSync(path15.dirname(file), { recursive: true });
13070
13949
  fs12.writeFileSync(file, JSON.stringify(seen));
13071
13950
  } catch {}
13072
13951
  };
@@ -13116,52 +13995,19 @@ var git4 = (cwd, args) => {
13116
13995
  const value = run.ok ? run.stdout.trim() : "";
13117
13996
  return value ? value : null;
13118
13997
  };
13119
- var safeRef = (value) => value.length > 0 && !value.startsWith("-");
13998
+ var safeRef2 = (value) => value.length > 0 && !value.startsWith("-");
13120
13999
  function resolveCommit(cwd, ref) {
13121
- if (!safeRef(ref))
14000
+ if (!safeRef2(ref))
13122
14001
  return null;
13123
14002
  return git4(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
13124
14003
  }
13125
14004
  function branchHead(cwd, branch) {
13126
- if (!safeRef(branch))
14005
+ if (!safeRef2(branch))
13127
14006
  return null;
13128
14007
  return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
13129
14008
  }
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 (resolveCommit(cwd, candidate))
13146
- return candidate;
13147
- }
13148
- const originHead = git4(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]);
13149
- if (originHead && resolveCommit(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 (resolveCommit(cwd, candidate))
13160
- return candidate;
13161
- return null;
13162
- }
13163
14009
  function diffHash(cwd, base, head) {
13164
- if (!safeRef(base) || !safeRef(head))
14010
+ if (!safeRef2(base) || !safeRef2(head))
13165
14011
  return null;
13166
14012
  const diff = gitRun2(cwd, [
13167
14013
  "diff",
@@ -13193,14 +14039,14 @@ function diffHash(cwd, base, head) {
13193
14039
  return `sha256:${hash.digest("hex")}`;
13194
14040
  }
13195
14041
  function storeRoot(cwd) {
13196
- const location = resolveStore(path15.resolve(cwd));
14042
+ const location = resolveStore(path16.resolve(cwd));
13197
14043
  if (location instanceof Error)
13198
14044
  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");
13199
14045
  return { ok: true, value: { root: location.dir, shared: location.shared } };
13200
14046
  }
13201
14047
  function ledgerPath(cwd) {
13202
14048
  const root = storeRoot(cwd);
13203
- return root.ok ? { ok: true, value: path15.join(root.value.root, "ledger", "ledger.jsonl") } : root;
14049
+ return root.ok ? { ok: true, value: path16.join(root.value.root, "ledger", "ledger.jsonl") } : root;
13204
14050
  }
13205
14051
  var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
13206
14052
  var NETWORK_FS_TYPES = new Set([
@@ -13230,13 +14076,13 @@ var LOCK_STALE_MS = 30000;
13230
14076
  var LOCK_OWNER = "owner";
13231
14077
  var lockToken = (lock) => {
13232
14078
  try {
13233
- return fs13.readFileSync(path15.join(lock, LOCK_OWNER), "utf8");
14079
+ return fs13.readFileSync(path16.join(lock, LOCK_OWNER), "utf8");
13234
14080
  } catch {
13235
14081
  return null;
13236
14082
  }
13237
14083
  };
13238
14084
  var lockAgeMs = (lock) => {
13239
- for (const target of [path15.join(lock, LOCK_OWNER), lock]) {
14085
+ for (const target of [path16.join(lock, LOCK_OWNER), lock]) {
13240
14086
  try {
13241
14087
  return Date.now() - fs13.statSync(target).mtimeMs;
13242
14088
  } catch {}
@@ -13253,7 +14099,7 @@ var ledgerLock = {
13253
14099
  throw error;
13254
14100
  }
13255
14101
  const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
13256
- fs13.writeFileSync(path15.join(lock, LOCK_OWNER), token);
14102
+ fs13.writeFileSync(path16.join(lock, LOCK_OWNER), token);
13257
14103
  return token;
13258
14104
  },
13259
14105
  release(lock, token) {
@@ -13386,8 +14232,8 @@ function appendLine(file, build, options) {
13386
14232
  }
13387
14233
  };
13388
14234
  try {
13389
- fs13.mkdirSync(path15.dirname(file), { recursive: true });
13390
- if (!options.forceLock && !onNetworkFs(path15.dirname(file)))
14235
+ fs13.mkdirSync(path16.dirname(file), { recursive: true });
14236
+ if (!options.forceLock && !onNetworkFs(path16.dirname(file)))
13391
14237
  return write();
13392
14238
  const locked = withLedgerLock(file, write);
13393
14239
  return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
@@ -13621,7 +14467,7 @@ function verdictBasis(verdict, current) {
13621
14467
  }
13622
14468
  var isLiveVerdict = (row, retired) => row.type === "verdict" && typeof row.result === "string" && !retired.has(row.id);
13623
14469
  function trailerSessions(cwd, base, head) {
13624
- if (!base || !head || !safeRef(base) || !safeRef(head))
14470
+ if (!base || !head || !safeRef2(base) || !safeRef2(head))
13625
14471
  return [];
13626
14472
  const out = git4(cwd, [
13627
14473
  "log",
@@ -13634,7 +14480,7 @@ var RENAMED = /^branch: renamed refs\/heads\/(.+) to refs\/heads\/(.+)$/iu;
13634
14480
  function branchScope(cwd, branch) {
13635
14481
  const renamedAt = new Map;
13636
14482
  let since = Number.NEGATIVE_INFINITY;
13637
- if (safeRef(branch)) {
14483
+ if (safeRef2(branch)) {
13638
14484
  const log = git4(cwd, [
13639
14485
  "log",
13640
14486
  "-g",
@@ -13779,12 +14625,29 @@ function effectiveOf(rows, reasonsOf, sameCode, isCurrent) {
13779
14625
  }
13780
14626
  return effective;
13781
14627
  }
13782
- function restackCarries(rows, branch, from, to) {
14628
+ var restackExact = new WeakMap;
14629
+ function restackCarries(exactIn, rows, branch, from, to) {
13783
14630
  if (!from || !to)
13784
14631
  return false;
14632
+ const exact = (row, fromHead, head) => {
14633
+ if (exactIn === null)
14634
+ return true;
14635
+ const cwd = exactIn;
14636
+ let known = restackExact.get(row);
14637
+ if (known === undefined) {
14638
+ known = typeof row.fromBase === "string" && typeof row.toBase === "string" && sameOwnChange(cwd, {
14639
+ from: fromHead,
14640
+ to: head,
14641
+ fromBase: row.fromBase,
14642
+ toBase: row.toBase
14643
+ });
14644
+ restackExact.set(row, known);
14645
+ }
14646
+ return known;
14647
+ };
13785
14648
  const edges = new Map;
13786
14649
  for (const row of rows)
13787
- if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.integrity === "verified" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
14650
+ 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))
13788
14651
  edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
13789
14652
  const seen = new Set([from]);
13790
14653
  const queue = [from];
@@ -13818,9 +14681,57 @@ function checkVerdicts(cwd, branch, ledgerRows) {
13818
14681
  return key;
13819
14682
  };
13820
14683
  const keyFor = (base) => keysAt(base, head);
13821
- const basisOf = (row) => {
14684
+ const driftCache = new Map;
14685
+ const driftOf = (row) => {
14686
+ if (driftCache.has(row))
14687
+ return driftCache.get(row) ?? null;
14688
+ let drift = null;
14689
+ const toBase = row.base ?? fallbackBase;
14690
+ const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
14691
+ const fromBase = row.baseSha ?? toBaseSha;
14692
+ if (row.head && head && row.head !== head && toBaseSha && fromBase && !untrusted(reasonsOf(row)))
14693
+ drift = verdictDrift(cwd, {
14694
+ from: row.head,
14695
+ to: head,
14696
+ fromBase,
14697
+ toBase: toBaseSha,
14698
+ kind: typeof row.kind === "string" ? row.kind : "review"
14699
+ });
14700
+ driftCache.set(row, drift);
14701
+ return drift;
14702
+ };
14703
+ const exactCache = new Map;
14704
+ const exactCarry = (row) => {
14705
+ if (FAILING.has(String(row.result)))
14706
+ return true;
14707
+ let exact = exactCache.get(row);
14708
+ if (exact === undefined) {
14709
+ const toBase = row.base ?? fallbackBase;
14710
+ const toBaseSha = toBase ? resolveCommit(cwd, toBase) : null;
14711
+ const fromBase = row.baseSha ?? toBaseSha;
14712
+ exact = Boolean(row.head && head && toBaseSha && fromBase && sameOwnChange(cwd, { from: row.head, to: head, fromBase, toBase: toBaseSha }));
14713
+ exactCache.set(row, exact);
14714
+ }
14715
+ return exact;
14716
+ };
14717
+ const keyBasis = (row) => {
13822
14718
  const basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
13823
- return basis === "stale" && restackCarries(rows, branch, row.head, head) ? "carried" : basis;
14719
+ return basis === "carried" && !exactCarry(row) ? "stale" : basis;
14720
+ };
14721
+ const restacked = (row) => restackCarries(FAILING.has(String(row.result)) ? null : cwd, rows, branch, row.head, head);
14722
+ const carryOf = (row) => {
14723
+ if (keyBasis(row) !== "stale")
14724
+ return null;
14725
+ if (restacked(row))
14726
+ return null;
14727
+ const drift = driftOf(row);
14728
+ return drift?.carries ? { rule: drift.rule, since: drift.since, files: drift.files } : null;
14729
+ };
14730
+ const basisOf = (row) => {
14731
+ const basis = keyBasis(row);
14732
+ if (basis !== "stale")
14733
+ return basis;
14734
+ return restacked(row) || carryOf(row) ? "carried" : basis;
13824
14735
  };
13825
14736
  const sameCode = (failed, row) => {
13826
14737
  if (failed.head && failed.head === row.head)
@@ -13828,7 +14739,7 @@ function checkVerdicts(cwd, branch, ledgerRows) {
13828
14739
  const base = failed.base ?? fallbackBase;
13829
14740
  const stored = (row.base ?? fallbackBase) === base && row.baseSha !== null && row.baseSha === failed.baseSha;
13830
14741
  const keys = stored ? { patchId: row.patchId, diffHash: row.diffHash } : keysAt(base, row.head);
13831
- return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(rows, branch, failed.head, row.head);
14742
+ return verdictBasis(failed, { head: row.head, ...keys }) !== "stale" || restackCarries(null, rows, branch, failed.head, row.head);
13832
14743
  };
13833
14744
  const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head }, scope);
13834
14745
  const failureAuthorCache = new Map;
@@ -13884,6 +14795,7 @@ function checkVerdicts(cwd, branch, ledgerRows) {
13884
14795
  independent: independence.length === 0,
13885
14796
  accepted: reasons.length === 0,
13886
14797
  reasons,
14798
+ carry: basis === "carried" ? carryOf(row) : null,
13887
14799
  verdict: row
13888
14800
  });
13889
14801
  if (reasons.length === 0) {
@@ -13912,7 +14824,12 @@ function checkVerdicts(cwd, branch, ledgerRows) {
13912
14824
  verdict: failing?.verdict ?? null,
13913
14825
  reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
13914
14826
  };
13915
- const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
14827
+ 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))));
14828
+ const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
14829
+ const carryEntry = verdicts.find((entry) => entry.verdict === accepted.verdict && entry.accepted) ?? newestCurrent;
14830
+ const lastPass = accepted.accepted || failing || tampered.length ? undefined : verdicts.findLast((entry) => !entry.current && !untrusted(entry.reasons) && STRONG_RESULTS.has(String(entry.verdict.result)));
14831
+ const drift = lastPass ? driftOf(lastPass.verdict) : null;
14832
+ const reverify = drift && !drift.carries && drift.focused && drift.files ? { scope: "focused", since: drift.since, files: drift.files } : null;
13916
14833
  return {
13917
14834
  branch,
13918
14835
  head,
@@ -13921,6 +14838,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
13921
14838
  base: newestCurrent?.verdict.base ?? fallbackBase,
13922
14839
  patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
13923
14840
  current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
14841
+ carry: carryEntry?.carry ?? null,
14842
+ reverify,
13924
14843
  accepted,
13925
14844
  verdicts,
13926
14845
  authors: [...authors].toSorted(),
@@ -13962,6 +14881,7 @@ var RULES = {
13962
14881
  self: "verdict:self",
13963
14882
  nonAuthor: "verdict:non-author",
13964
14883
  verified: "verdict:verified",
14884
+ lane: "verdict:lane",
13965
14885
  product: "decision:product",
13966
14886
  plan: "plan"
13967
14887
  };
@@ -13984,6 +14904,7 @@ var normalizedScope = (scope) => ({
13984
14904
  function deriveRequirements(judgment, scope, constraints, verification = "self") {
13985
14905
  const out = [];
13986
14906
  const base = { scope, dependentAction: null };
14907
+ const proportional = verification === "proportional";
13987
14908
  if (judgment.behaviorChange)
13988
14909
  out.push(derived2({
13989
14910
  ...base,
@@ -13994,7 +14915,17 @@ function deriveRequirements(judgment, scope, constraints, verification = "self")
13994
14915
  before: "close",
13995
14916
  acceptanceAllowed: false
13996
14917
  }));
13997
- if (judgment.behaviorChange && judgment.riskTier === "normal")
14918
+ if (proportional && judgment.riskTier === "normal")
14919
+ out.push(derived2({
14920
+ ...base,
14921
+ ruleId: RULES.lane,
14922
+ dimension: "review",
14923
+ 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.",
14924
+ 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.",
14925
+ before: "close",
14926
+ acceptanceAllowed: false
14927
+ }));
14928
+ if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
13998
14929
  out.push(verification === "independent" ? derived2({
13999
14930
  ...base,
14000
14931
  ruleId: RULES.nonAuthor,
@@ -14097,7 +15028,7 @@ var productDecision = (task, ledger) => {
14097
15028
  };
14098
15029
  };
14099
15030
  var planRecorded = (task, root) => {
14100
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync4(path16.resolve(root, ref.path)));
15031
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(path17.resolve(root, ref.path)));
14101
15032
  if (cited)
14102
15033
  return {
14103
15034
  met: true,
@@ -14111,14 +15042,30 @@ var planRecorded = (task, root) => {
14111
15042
  decisionIds: []
14112
15043
  };
14113
15044
  };
14114
- var verdict = (requirement, root, ledger) => {
15045
+ var workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) });
15046
+ var laneVerdict = (task, root, ledger) => {
15047
+ if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
15048
+ return {
15049
+ met: true,
15050
+ reason: "no branch to read a lane from, and the judgment records no behavior change",
15051
+ decisionIds: []
15052
+ };
15053
+ const lane = workingLane(task, root);
15054
+ const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
15055
+ const need = laneNeed(lane.lane);
15056
+ if (need === "none")
15057
+ return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
15058
+ const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
15059
+ return { ...status, reason: `${label}: ${status.reason}` };
15060
+ };
15061
+ var verdict = (ruleId, root, ledger) => {
14115
15062
  const branch = ledger.branch();
14116
15063
  if (!branch)
14117
15064
  return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
14118
15065
  const check = checkVerdicts(root, branch, ledger.rows());
14119
15066
  const failing = check.accepted.reasons.includes("failing_verdict");
14120
15067
  const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
14121
- 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)));
15068
+ 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)));
14122
15069
  if (met)
14123
15070
  return {
14124
15071
  met: true,
@@ -14126,7 +15073,7 @@ var verdict = (requirement, root, ledger) => {
14126
15073
  decisionIds: []
14127
15074
  };
14128
15075
  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, " ");
14129
- 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>\``;
15076
+ 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>\``;
14130
15077
  return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
14131
15078
  };
14132
15079
  function derivedRuleStatus(task, requirement, root, ledger) {
@@ -14138,7 +15085,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
14138
15085
  case RULES.self:
14139
15086
  case RULES.nonAuthor:
14140
15087
  case RULES.verified:
14141
- return verdict(requirement, root, ledger);
15088
+ return verdict(requirement.ruleId, root, ledger);
15089
+ case RULES.lane:
15090
+ return laneVerdict(task, root, ledger);
14142
15091
  default:
14143
15092
  return null;
14144
15093
  }
@@ -14157,14 +15106,14 @@ function writeBlockers(task, root) {
14157
15106
 
14158
15107
  // packages/workit-core/src/core/task-evaluation.ts
14159
15108
  var digestBytes = (value) => createHash12("sha256").update(value).digest("hex");
14160
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path17.sep}`);
14161
- var relative = (root, candidate) => path17.relative(root, candidate).split(path17.sep).join("/") || ".";
15109
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path18.sep}`);
15110
+ var relative = (root, candidate) => path18.relative(root, candidate).split(path18.sep).join("/") || ".";
14162
15111
  var canonicalPath = (value) => {
14163
15112
  if (typeof value !== "string" || !value)
14164
15113
  return null;
14165
15114
  if (value.includes("\\") || value.split("/").includes(".."))
14166
15115
  return null;
14167
- const normalized = path17.posix.normalize(value);
15116
+ const normalized = path18.posix.normalize(value);
14168
15117
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
14169
15118
  return null;
14170
15119
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -14212,14 +15161,14 @@ var gitPaths = (root) => {
14212
15161
  git: !/not a git repository/i.test(stderr)
14213
15162
  };
14214
15163
  }
14215
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")) : [];
15164
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")) : [];
14216
15165
  const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
14217
15166
  if (stagedDeleted.status !== 0) {
14218
15167
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
14219
15168
  if (!/not a git repository/i.test(stderr))
14220
15169
  return { paths, uncertain: true, git: true };
14221
15170
  } else if (stagedDeleted.stdout) {
14222
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")));
15171
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")));
14223
15172
  }
14224
15173
  return { paths: [...new Set(paths)], uncertain: false, git: true };
14225
15174
  };
@@ -14234,7 +15183,7 @@ var walk = (root, directory, output) => {
14234
15183
  for (const name of names) {
14235
15184
  if (name === ".git" || name === ".workit")
14236
15185
  continue;
14237
- const target = path17.join(directory, name);
15186
+ const target = path18.join(directory, name);
14238
15187
  let stat;
14239
15188
  try {
14240
15189
  stat = fs14.lstatSync(target);
@@ -14252,12 +15201,12 @@ var walk = (root, directory, output) => {
14252
15201
  var scopeRoots = (root, scope) => {
14253
15202
  const roots = [];
14254
15203
  for (const item of scope.paths.length ? scope.paths : ["."]) {
14255
- const target = path17.resolve(root, item);
15204
+ const target = path18.resolve(root, item);
14256
15205
  if (!inside(root, target))
14257
15206
  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` }] });
14258
15207
  let ancestor = target;
14259
15208
  while (!fs14.existsSync(ancestor) && ancestor !== root)
14260
- ancestor = path17.dirname(ancestor);
15209
+ ancestor = path18.dirname(ancestor);
14261
15210
  try {
14262
15211
  if (!inside(root, fs14.realpathSync(ancestor)))
14263
15212
  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` }] });
@@ -14309,7 +15258,7 @@ function captureCandidate(root, scope, environment = []) {
14309
15258
  for (const item of [...names].toSorted()) {
14310
15259
  if (!scopeMatches(item, normalizedScope))
14311
15260
  continue;
14312
- const target = path17.join(checkout, item);
15261
+ const target = path18.join(checkout, item);
14313
15262
  try {
14314
15263
  const stat = fs14.lstatSync(target);
14315
15264
  if (stat.isSymbolicLink()) {
@@ -14493,23 +15442,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
14493
15442
  continue;
14494
15443
  if (!reference.digest)
14495
15444
  return failure2("invalid_input", "document references require a byte digest");
14496
- const target = path17.resolve(root, reference.path);
14497
- if (target !== root && !target.startsWith(`${root}${path17.sep}`))
15445
+ const target = path18.resolve(root, reference.path);
15446
+ if (target !== root && !target.startsWith(`${root}${path18.sep}`))
14498
15447
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
14499
15448
  fields: [
14500
15449
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
14501
15450
  ]
14502
15451
  });
14503
15452
  try {
14504
- const below = path17.relative(root, target);
15453
+ const below = path18.relative(root, target);
14505
15454
  let current = root;
14506
- for (const segment of below.split(path17.sep)) {
15455
+ for (const segment of below.split(path18.sep)) {
14507
15456
  if (!segment)
14508
15457
  continue;
14509
- current = path17.join(current, segment);
15458
+ current = path18.join(current, segment);
14510
15459
  const stat = fs14.lstatSync(current);
14511
15460
  const real = fs14.realpathSync(current);
14512
- if (real !== root && !real.startsWith(`${root}${path17.sep}`))
15461
+ if (real !== root && !real.startsWith(`${root}${path18.sep}`))
14513
15462
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
14514
15463
  fields: [
14515
15464
  {
@@ -14942,9 +15891,9 @@ Autonomy contract:
14942
15891
  Prose is a note: only a configured check the CLI ran (\`workit check <name>\`)
14943
15892
  satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
14944
15893
  - Author is not verifier: your own verdict is \`--self\`, shown as
14945
- self-reviewed, never verified. High risk, \`verification: independent\` and
14946
- merge need a verifier with its own host-derived session (a subagent's
14947
- \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
15894
+ self-reviewed, never verified. High risk, a focused or full lane and
15895
+ merge under \`verification: self|independent\` need a verifier with its
15896
+ own host-derived session (a subagent's \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
14948
15897
  - A local-commit endpoint does not imply PR readiness, and a local commit alone
14949
15898
  is not evidence of a requested remote push. Before reporting delivery,
14950
15899
  reconcile every requested item and observe it (\`workit verify-delivery\`).
@@ -14952,7 +15901,7 @@ Autonomy contract:
14952
15901
  CLI first: if a step has one right answer, use the \`workit\` verb instead of
14953
15902
  hand-running git, gh or glab (\`workit git|pr|ci|check|help\`;
14954
15903
  else \`npx -y @brainervirus/workit-cli\`).
14955
- Other checkout: \`workit --cwd <dir> <verb>\` (Codex: \`<verb> --cwd <dir>\`); one workit
15904
+ Other checkout: \`workit <verb> --cwd <dir> …\` (verb first: host rules match it); one workit
14956
15905
  verb per shell call, never \`cd <dir> && workit\`. zsh-safe: no \`echo =====\`
14957
15906
  (\`printf '%s\\n' ---\`), quote globs and URLs, no variable named \`status\` or \`path\`.
14958
15907
  Add Co-Authored-By via \`workit git commit --trailer "K: v" -- <paths>\`, never
@@ -14964,7 +15913,7 @@ authorizes an effect. Judge work once with
14964
15913
  \`workit policy assess --judge risk= behavior= product-choice= plan=\`.
14965
15914
  Across repositories, bind items to checkout, branch and endpoint, resolve
14966
15915
  competing targets before a mutation, and reconcile an uncertain
14967
- external effect before a retry.
15916
+ effect before a retry.
14968
15917
 
14969
15918
  Mid-task: never silently drop or resume an objective (workit-continue).
14970
15919
 
@@ -15107,20 +16056,20 @@ var exportDigest = (bundle) => sha256({
15107
16056
  });
15108
16057
  // packages/workit-core/src/core/route-intent.ts
15109
16058
  import { spawnSync as spawnSync12 } from "node:child_process";
15110
- import { existsSync as existsSync6 } from "node:fs";
15111
- import path20 from "node:path";
16059
+ import { existsSync as existsSync7 } from "node:fs";
16060
+ import path21 from "node:path";
15112
16061
 
15113
16062
  // packages/workit-core/src/hooks/raw-git.ts
15114
16063
  import { spawnSync as spawnSync11 } from "node:child_process";
15115
16064
  import { createHash as createHash14 } from "node:crypto";
15116
16065
  import fs16 from "node:fs";
15117
16066
  import os4 from "node:os";
15118
- import path19 from "node:path";
16067
+ import path20 from "node:path";
15119
16068
 
15120
16069
  // packages/workit-core/src/hooks/session-marker.ts
15121
16070
  import { createHash as createHash13 } from "node:crypto";
15122
16071
  import fs15 from "node:fs";
15123
- import path18 from "node:path";
16072
+ import path19 from "node:path";
15124
16073
  var sessionMarkerFile = (input, session, store) => {
15125
16074
  let dir = store;
15126
16075
  if (dir === undefined) {
@@ -15130,7 +16079,7 @@ var sessionMarkerFile = (input, session, store) => {
15130
16079
  dir = location.dir;
15131
16080
  }
15132
16081
  const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
15133
- return path18.join(dir, "hooks", `skills-${name}.json`);
16082
+ return path19.join(dir, "hooks", `skills-${name}.json`);
15134
16083
  };
15135
16084
  var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
15136
16085
  var EMPTY = { nudged: [], loaded: [], raw: [], stops: [], judges: [] };
@@ -15157,7 +16106,7 @@ var parseMarker = (text) => {
15157
16106
  };
15158
16107
  var readSessionMarker = (file) => parseMarker(readRaw(file));
15159
16108
  var writeAtomic = (file, marker) => {
15160
- fs15.mkdirSync(path18.dirname(file), { recursive: true });
16109
+ fs15.mkdirSync(path19.dirname(file), { recursive: true });
15161
16110
  const temp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
15162
16111
  try {
15163
16112
  fs15.writeFileSync(temp, JSON.stringify(marker));
@@ -15434,6 +16383,18 @@ var substitutions = (body) => {
15434
16383
  };
15435
16384
  var PLAIN_POSIX = /[^\s'"\\`$(){},<>|;&#]+/uy;
15436
16385
  var PLAIN_POWERSHELL = /[^\s'"`(){}<>|;&#]+/uy;
16386
+ var expandsInQuotes = (body, dialect) => {
16387
+ const escape = dialect === "posix" ? "\\" : "`";
16388
+ for (let index = 0;index < body.length; index++) {
16389
+ const char = body[index];
16390
+ if (char === escape)
16391
+ index++;
16392
+ else if (char === "$" || dialect === "posix" && char === "`")
16393
+ return true;
16394
+ }
16395
+ return false;
16396
+ };
16397
+ var EXPANDING_RUN = /[*?[]/u;
15437
16398
  var arithmeticEnd = (command, start) => {
15438
16399
  const end = substitutionEnd(command, start);
15439
16400
  return command[end - 1] === ")" && end > start + 2 ? end : 0;
@@ -15452,6 +16413,7 @@ var tokenize = (command, dialect) => {
15452
16413
  let current = { words: [], redirects: [] };
15453
16414
  let word = "";
15454
16415
  let inWord = false;
16416
+ let expanding = false;
15455
16417
  let pending = null;
15456
16418
  const heredocs = [];
15457
16419
  const escape = posix ? "\\" : "`";
@@ -15466,11 +16428,16 @@ var tokenize = (command, dialect) => {
15466
16428
  heredocs.push({ delimiter: text.replace(/^-/, ""), owner: current });
15467
16429
  else if (pending === "herestring")
15468
16430
  (current.stdin ??= []).push(text);
15469
- else if (pending !== "input")
16431
+ else if (pending !== "input") {
16432
+ const at = current.words.length;
15470
16433
  current.words.push(...MARKERS.test(word) ? expandBraces(word) : [word]);
16434
+ if (expanding || !posix && word.startsWith("@"))
16435
+ (current.expands ??= []).push(...current.words.slice(at).map((_, i) => at + i));
16436
+ }
15471
16437
  pending = null;
15472
16438
  word = "";
15473
16439
  inWord = false;
16440
+ expanding = false;
15474
16441
  };
15475
16442
  const flushSegment = () => {
15476
16443
  flushWord();
@@ -15485,6 +16452,7 @@ var tokenize = (command, dialect) => {
15485
16452
  };
15486
16453
  const substitution = (inner, text) => {
15487
16454
  (current.subs ??= []).push(inner);
16455
+ expanding = true;
15488
16456
  word += text;
15489
16457
  inWord = true;
15490
16458
  };
@@ -15495,6 +16463,8 @@ var tokenize = (command, dialect) => {
15495
16463
  if (run) {
15496
16464
  word += run[0];
15497
16465
  inWord = true;
16466
+ if (EXPANDING_RUN.test(run[0]))
16467
+ expanding = true;
15498
16468
  index += run[0].length - 1;
15499
16469
  continue;
15500
16470
  }
@@ -15523,6 +16493,8 @@ var tokenize = (command, dialect) => {
15523
16493
  const body = command.slice(index + 1, end < 0 ? undefined : end);
15524
16494
  if (posix && char === '"' && /\$\(|`/.test(body))
15525
16495
  (current.subs ??= []).push(...substitutions(body));
16496
+ if (char === '"' && expandsInQuotes(body, dialect))
16497
+ expanding = true;
15526
16498
  word += unquote(body, char, dialect);
15527
16499
  inWord = true;
15528
16500
  index = end < 0 ? command.length : end;
@@ -15568,6 +16540,8 @@ var tokenize = (command, dialect) => {
15568
16540
  if (char === "(" && inWord && word.endsWith("$")) {
15569
16541
  word = word.slice(0, -1);
15570
16542
  inWord = word !== "";
16543
+ if (!inWord)
16544
+ expanding = false;
15571
16545
  }
15572
16546
  group(char === "(" ? "open" : "close");
15573
16547
  } else if (!posix && (char === "{" || char === "}")) {
@@ -15621,6 +16595,8 @@ var tokenize = (command, dialect) => {
15621
16595
  else if (posix && char === "}" && word.includes(OPEN))
15622
16596
  word += CLOSE;
15623
16597
  else {
16598
+ if (char === "$")
16599
+ expanding = true;
15624
16600
  word += char;
15625
16601
  inWord = true;
15626
16602
  }
@@ -15678,14 +16654,14 @@ var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
15678
16654
  var ASSIGNMENT = /^([A-Za-z_]\w*)=([\s\S]*)$/;
15679
16655
  var MAX_DEPTH = 4;
15680
16656
  var MAX_EXPANSIONS = 16;
15681
- var isAbsolute = (value) => path19.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
16657
+ var isAbsolute = (value) => path20.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
15682
16658
  var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
15683
16659
  var joinDir = (base, raw) => {
15684
16660
  const next = raw.replace(HERE, ".");
15685
16661
  if (base === undefined || /[$`*?]/.test(next) || next === "-")
15686
16662
  return;
15687
- const expanded = next === "~" || next.startsWith("~/") ? path19.join(os4.homedir(), next.slice(1)) : next;
15688
- return base === null || isAbsolute(expanded) ? expanded : path19.join(base, expanded);
16663
+ const expanded = next === "~" || next.startsWith("~/") ? path20.join(os4.homedir(), next.slice(1)) : next;
16664
+ return base === null || isAbsolute(expanded) ? expanded : path20.join(base, expanded);
15689
16665
  };
15690
16666
  var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
15691
16667
  var cdTarget = (words) => {
@@ -16065,8 +17041,17 @@ function walk2(command, dialect, depth, seen) {
16065
17041
  visit(next, undefined, hops + 1);
16066
17042
  };
16067
17043
  const wrapped = seen ? null : workitCheckTail(words);
16068
- if (wrapped)
16069
- return "script" in wrapped ? nested(wrapped.script, "posix") : runs([wrapped.argv]);
17044
+ if (wrapped) {
17045
+ const outer = dir;
17046
+ if (wrapped.cwd !== null)
17047
+ dir = joinDir(dir, wrapped.cwd);
17048
+ if ("script" in wrapped)
17049
+ nested(wrapped.script, "posix");
17050
+ else
17051
+ runs([wrapped.argv]);
17052
+ dir = outer;
17053
+ return;
17054
+ }
16070
17055
  if (CD.has(name)) {
16071
17056
  const target = cdTarget(words);
16072
17057
  dir = target === null ? os4.homedir() : joinDir(dir, target);
@@ -16167,14 +17152,14 @@ function workitCheckTail(words) {
16167
17152
  return null;
16168
17153
  } else if (!/^workit(?:\.exe|\.cmd)?$/.test(commandName(words[0])))
16169
17154
  return null;
16170
- const args = words.slice(at + 1);
17155
+ const { rest: args, cwd } = skipWorkitGlobals(words.slice(at + 1));
16171
17156
  if (args[0] !== "check" && !(args[0] === "lock" && args[1] === "run"))
16172
17157
  return null;
16173
17158
  const split = args.indexOf("--");
16174
17159
  const tail = split < 0 ? [] : args.slice(split + 1);
16175
17160
  if (!tail.length)
16176
17161
  return null;
16177
- return args.slice(0, split).includes("--shell") ? { script: tail.join(" ") } : { argv: tail };
17162
+ return args.slice(0, split).includes("--shell") ? { script: tail.join(" "), cwd } : { argv: tail, cwd };
16178
17163
  }
16179
17164
  function simpleCommand(command, dialect = "posix", depth = 0) {
16180
17165
  let segments;
@@ -16235,7 +17220,21 @@ function skipWorkitGlobals(args) {
16235
17220
  } else if (args[at] !== "--json")
16236
17221
  break;
16237
17222
  }
16238
- return { rest: args.slice(at), cwd };
17223
+ const rest = [];
17224
+ for (;at < args.length; at++) {
17225
+ const arg = args[at];
17226
+ if (arg === "--") {
17227
+ rest.push(...args.slice(at));
17228
+ break;
17229
+ }
17230
+ if (arg.startsWith("--cwd="))
17231
+ cwd = arg.slice("--cwd=".length);
17232
+ else if (arg === "--cwd" && args[at + 1] !== undefined)
17233
+ cwd = args[++at];
17234
+ else
17235
+ rest.push(arg);
17236
+ }
17237
+ return { rest, cwd };
16239
17238
  }
16240
17239
  var PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
16241
17240
  var CURRENT_BRANCH = /^\$\(\s*git\s+(?:branch\s+--show-current|rev-parse\s+--abbrev-ref\s+HEAD|symbolic-ref\s+--short\s+HEAD)\s*\)$/;
@@ -16536,17 +17535,17 @@ var headSha2 = (repo) => {
16536
17535
  if (!git || git.reftable)
16537
17536
  return null;
16538
17537
  try {
16539
- const head = fs16.readFileSync(path19.join(git.gitDir, "HEAD"), "utf8").trim();
17538
+ const head = fs16.readFileSync(path20.join(git.gitDir, "HEAD"), "utf8").trim();
16540
17539
  if (/^[0-9a-f]{40,64}$/.test(head))
16541
17540
  return head;
16542
17541
  const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
16543
17542
  if (!ref)
16544
17543
  return null;
16545
- const common = path19.dirname(repo.location.dir);
17544
+ const common = path20.dirname(repo.location.dir);
16546
17545
  try {
16547
- return fs16.readFileSync(path19.join(common, ref), "utf8").trim() || null;
17546
+ return fs16.readFileSync(path20.join(common, ref), "utf8").trim() || null;
16548
17547
  } catch {}
16549
- const packed = fs16.readFileSync(path19.join(common, "packed-refs"), "utf8");
17548
+ const packed = fs16.readFileSync(path20.join(common, "packed-refs"), "utf8");
16550
17549
  return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
16551
17550
  } catch {
16552
17551
  return null;
@@ -16634,7 +17633,7 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
16634
17633
  const sub = invocation.args[0];
16635
17634
  if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
16636
17635
  return [invocation];
16637
- const dir = path19.resolve(root, invocation.dir ?? ".");
17636
+ const dir = path20.resolve(root, invocation.dir ?? ".");
16638
17637
  if (!fs16.existsSync(dir))
16639
17638
  return [invocation];
16640
17639
  const config = gitConfig(dir, invocation, cache);
@@ -16657,11 +17656,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
16657
17656
  var normalizedUrl = (url, dir) => {
16658
17657
  const trimmed = url.trim();
16659
17658
  if (/^file:\/\//i.test(trimmed))
16660
- return path19.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
17659
+ return path20.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
16661
17660
  const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
16662
17661
  const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
16663
17662
  if (!scheme && !scp)
16664
- return path19.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
17663
+ return path20.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
16665
17664
  const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
16666
17665
  return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
16667
17666
  };
@@ -16885,7 +17884,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
16885
17884
  const out = [];
16886
17885
  const repos = new Map;
16887
17886
  const repoFor = (relative) => {
16888
- const dir = path19.resolve(cwd, relative ?? ".");
17887
+ const dir = path20.resolve(cwd, relative ?? ".");
16889
17888
  if (!repos.has(dir)) {
16890
17889
  const repo = repoAt(dir);
16891
17890
  repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
@@ -16970,7 +17969,7 @@ var analysisDenial = (error) => ({
16970
17969
  unblock: SPLIT_IT
16971
17970
  });
16972
17971
  var MARKER_TTL_SECONDS = 3600;
16973
- var markerFile = (repo, session, key) => path19.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
17972
+ var markerFile = (repo, session, key) => path20.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
16974
17973
  var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
16975
17974
  function noteRawCommit(input, command, key) {
16976
17975
  try {
@@ -16981,7 +17980,7 @@ function noteRawCommit(input, command, key) {
16981
17980
  if (action.kind !== "commit")
16982
17981
  continue;
16983
17982
  const file = markerFile(repo, input.session.id, key);
16984
- fs16.mkdirSync(path19.dirname(file), { recursive: true });
17983
+ fs16.mkdirSync(path20.dirname(file), { recursive: true });
16985
17984
  fs16.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
16986
17985
  `);
16987
17986
  }
@@ -17200,8 +18199,8 @@ var judgedDir = (root, dir) => {
17200
18199
  return null;
17201
18200
  if (dir === null)
17202
18201
  return root;
17203
- const resolved = path20.resolve(root, dir);
17204
- return existsSync6(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
18202
+ const resolved = path21.resolve(root, dir);
18203
+ return existsSync7(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
17205
18204
  };
17206
18205
  var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
17207
18206
  let judged = false;
@@ -17436,7 +18435,7 @@ var judgeInputSummary = (raw) => {
17436
18435
 
17437
18436
  // packages/workit-core/src/autonomy.ts
17438
18437
  import os5 from "node:os";
17439
- import path21 from "node:path";
18438
+ import path22 from "node:path";
17440
18439
 
17441
18440
  // packages/workit-core/src/core/safe-write.ts
17442
18441
  import {
@@ -17470,10 +18469,10 @@ var accountHome = () => {
17470
18469
  }
17471
18470
  };
17472
18471
  var grantsHome = { resolve: accountHome };
17473
- var grantsDir = () => path21.join(grantsHome.resolve(), ".config", "workit");
18472
+ var grantsDir = () => path22.join(grantsHome.resolve(), ".config", "workit");
17474
18473
  var grantsOverride = () => {
17475
- const configured = path21.resolve(resolveConfigDir());
17476
- 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`;
18474
+ const configured = path22.resolve(resolveConfigDir());
18475
+ 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`;
17477
18476
  };
17478
18477
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
17479
18478
  var DEFAULT_GRANTS = Object.freeze({
@@ -17526,8 +18525,16 @@ function endpointView(entry, context) {
17526
18525
  ...issue ? { endpointIssue: issue } : {}
17527
18526
  };
17528
18527
  }
17529
- var DEFAULT_VERIFICATION = "self";
17530
- var verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self";
18528
+ var VERIFICATION_MODES = [
18529
+ "proportional",
18530
+ "self",
18531
+ "independent"
18532
+ ];
18533
+ var DEFAULT_VERIFICATION = "proportional";
18534
+ var verificationOf = (entry) => {
18535
+ const value = entry?.verification;
18536
+ return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
18537
+ };
17531
18538
  var isGrantValue = (value) => value === true || value === false || value === "verified";
17532
18539
  var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
17533
18540
  var fromAutoApprove = (value) => {
@@ -19275,8 +20282,8 @@ class WorkitCore {
19275
20282
  }
19276
20283
  // packages/workit-core/src/core/repo-context.ts
19277
20284
  import { spawnSync as spawnSync13 } from "node:child_process";
19278
- import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
19279
- import path22 from "node:path";
20285
+ import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "node:fs";
20286
+ import path23 from "node:path";
19280
20287
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
19281
20288
  try {
19282
20289
  gitRevisionParts(value);
@@ -19410,7 +20417,7 @@ var findPrTemplate = (cwd) => {
19410
20417
  ]) {
19411
20418
  const parts = rel.split("/");
19412
20419
  const base = parts[parts.length - 1];
19413
- const dir = parts.length > 1 ? path22.join(cwd, ...parts.slice(0, -1)) : cwd;
20420
+ const dir = parts.length > 1 ? path23.join(cwd, ...parts.slice(0, -1)) : cwd;
19414
20421
  const actual = probeCaseInsensitive(dir, base);
19415
20422
  if (actual)
19416
20423
  return [...parts.slice(0, -1), actual].join("/");
@@ -19441,11 +20448,11 @@ var diffStatForRange = (cwd, range) => {
19441
20448
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
19442
20449
  };
19443
20450
  var packageScripts = (cwd, keys) => {
19444
- const pkgPath = path22.join(cwd, "package.json");
19445
- if (!existsSync7(pkgPath))
20451
+ const pkgPath = path23.join(cwd, "package.json");
20452
+ if (!existsSync8(pkgPath))
19446
20453
  return [];
19447
20454
  try {
19448
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
20455
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
19449
20456
  return keys.filter((k) => pkg.scripts && typeof pkg.scripts[k] === "string").map((k) => `${k}: ${pkg.scripts?.[k]}`);
19450
20457
  } catch {
19451
20458
  return [];
@@ -19467,9 +20474,9 @@ var documentationFiles = (cwd) => {
19467
20474
  if (entry.isDirectory()) {
19468
20475
  if (excluded.has(entry.name))
19469
20476
  continue;
19470
- walk(path22.join(dir, entry.name), depth + 1);
20477
+ walk(path23.join(dir, entry.name), depth + 1);
19471
20478
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
19472
- matches.push("./" + path22.relative(cwd, path22.join(dir, entry.name)).replaceAll("\\", "/"));
20479
+ matches.push("./" + path23.relative(cwd, path23.join(dir, entry.name)).replaceAll("\\", "/"));
19473
20480
  }
19474
20481
  }
19475
20482
  };
@@ -19477,15 +20484,15 @@ var documentationFiles = (cwd) => {
19477
20484
  return matches.toSorted().slice(0, 200);
19478
20485
  };
19479
20486
  var readTrimmed2 = (file, maxLines) => {
19480
- if (!existsSync7(file))
20487
+ if (!existsSync8(file))
19481
20488
  return "";
19482
- const lines = readFileSync5(file, "utf8").split(`
20489
+ const lines = readFileSync6(file, "utf8").split(`
19483
20490
  `);
19484
20491
  return lines.slice(0, maxLines).join(`
19485
20492
  `);
19486
20493
  };
19487
20494
  function prReadyContext(root, range) {
19488
- const cwd = path22.resolve(repoRoot(root));
20495
+ const cwd = path23.resolve(repoRoot(root));
19489
20496
  if (range !== undefined && !isResolvableContextRange(cwd, range))
19490
20497
  return invalidRange(cwd);
19491
20498
  let stdout = "";
@@ -19543,7 +20550,7 @@ ${status.stderr}` : "");
19543
20550
  stdout += `template_path: ${template}
19544
20551
 
19545
20552
  `;
19546
- stdout += readTrimmed2(path22.join(cwd, template), 220);
20553
+ stdout += readTrimmed2(path23.join(cwd, template), 220);
19547
20554
  } else {
19548
20555
  stdout += `template_path: none
19549
20556
 
@@ -19553,7 +20560,7 @@ ${status.stderr}` : "");
19553
20560
  stdout += `
19554
20561
  `;
19555
20562
  stdout += printSection("Recent Validation Signals");
19556
- if (existsSync7(path22.join(cwd, "package.json"))) {
20563
+ if (existsSync8(path23.join(cwd, "package.json"))) {
19557
20564
  stdout += `package.json detected. Common scripts:
19558
20565
  `;
19559
20566
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -19562,7 +20569,7 @@ ${status.stderr}` : "");
19562
20569
  `) + `
19563
20570
  `;
19564
20571
  }
19565
- if (existsSync7(path22.join(cwd, "Cargo.toml")) || existsSync7(path22.join(cwd, "src-tauri/Cargo.toml"))) {
20572
+ if (existsSync8(path23.join(cwd, "Cargo.toml")) || existsSync8(path23.join(cwd, "src-tauri/Cargo.toml"))) {
19566
20573
  stdout += `Rust project detected.
19567
20574
  `;
19568
20575
  }
@@ -19595,7 +20602,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
19595
20602
  - Apply with the host-native editor, preserving the repository changelog conventions.
19596
20603
  - If Unreleased already has duplicate category headings, normalize_only first.`;
19597
20604
  function changelogContext(root, range) {
19598
- const cwd = path22.resolve(repoRoot(root));
20605
+ const cwd = path23.resolve(repoRoot(root));
19599
20606
  if (range !== undefined && !isResolvableContextRange(cwd, range))
19600
20607
  return invalidRange(cwd);
19601
20608
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -19613,8 +20620,8 @@ function changelogContext(root, range) {
19613
20620
  stdout += CHANGELOG_RULES + `
19614
20621
  `;
19615
20622
  stdout += printSection("Existing CHANGELOG.md");
19616
- const changelogPath = path22.join(cwd, "CHANGELOG.md");
19617
- if (existsSync7(changelogPath)) {
20623
+ const changelogPath = path23.join(cwd, "CHANGELOG.md");
20624
+ if (existsSync8(changelogPath)) {
19618
20625
  stdout += readTrimmed2(changelogPath, 260) + `
19619
20626
  `;
19620
20627
  } else {
@@ -19633,7 +20640,7 @@ function changelogContext(root, range) {
19633
20640
  return { stdout, stderr: "", exitCode: 0, cwd };
19634
20641
  }
19635
20642
  function docsRefreshContext(root, range) {
19636
- const cwd = path22.resolve(repoRoot(root));
20643
+ const cwd = path23.resolve(repoRoot(root));
19637
20644
  if (range !== undefined && !isResolvableContextRange(cwd, range))
19638
20645
  return invalidRange(cwd);
19639
20646
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -19655,8 +20662,8 @@ function docsRefreshContext(root, range) {
19655
20662
  `) + `
19656
20663
  `;
19657
20664
  stdout += printSection("README Preview");
19658
- const readme = path22.join(cwd, "README.md");
19659
- if (existsSync7(readme)) {
20665
+ const readme = path23.join(cwd, "README.md");
20666
+ if (existsSync8(readme)) {
19660
20667
  stdout += readTrimmed2(readme, 220) + `
19661
20668
  `;
19662
20669
  } else {
@@ -19664,10 +20671,10 @@ function docsRefreshContext(root, range) {
19664
20671
  `;
19665
20672
  }
19666
20673
  stdout += printSection("Package Scripts");
19667
- const pkgPath = path22.join(cwd, "package.json");
19668
- if (existsSync7(pkgPath)) {
20674
+ const pkgPath = path23.join(cwd, "package.json");
20675
+ if (existsSync8(pkgPath)) {
19669
20676
  try {
19670
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
20677
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
19671
20678
  stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
19672
20679
  `;
19673
20680
  } catch {}
@@ -19678,7 +20685,7 @@ function docsRefreshContext(root, range) {
19678
20685
  return { stdout, stderr: "", exitCode: 0, cwd };
19679
20686
  }
19680
20687
  function releaseNotesContext(root, rangeOrTag) {
19681
- const cwd = path22.resolve(repoRoot(root));
20688
+ const cwd = path23.resolve(repoRoot(root));
19682
20689
  if (!rangeOrTag) {
19683
20690
  return { stdout: "", stderr: `ERROR: release tag or range required
19684
20691
  `, exitCode: 1, cwd };
@@ -19716,7 +20723,7 @@ function releaseNotesContext(root, rangeOrTag) {
19716
20723
  `;
19717
20724
  stdout += printSection("Existing Release Files");
19718
20725
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
19719
- if (existsSync7(path22.join(cwd, rel)))
20726
+ if (existsSync8(path23.join(cwd, rel)))
19720
20727
  stdout += rel + `
19721
20728
  `;
19722
20729
  }
@@ -19748,7 +20755,7 @@ import { spawnSync as spawnSync15 } from "node:child_process";
19748
20755
 
19749
20756
  // packages/workit-core/src/core/pr-create.ts
19750
20757
  import fs17 from "node:fs";
19751
- import path23 from "node:path";
20758
+ import path24 from "node:path";
19752
20759
  import { spawnSync as spawnSync14 } from "node:child_process";
19753
20760
  import { pathToFileURL } from "node:url";
19754
20761
  // packages/workit-core/src/forge/pr-body.ts
@@ -19760,11 +20767,11 @@ function parseGhIssue(value) {
19760
20767
  // packages/workit-core/src/core/pr-create.ts
19761
20768
  function whichOnPath(tool) {
19762
20769
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
19763
- for (const dir of (process.env.PATH ?? "").split(path23.delimiter)) {
20770
+ for (const dir of (process.env.PATH ?? "").split(path24.delimiter)) {
19764
20771
  if (!dir)
19765
20772
  continue;
19766
20773
  for (const name of names) {
19767
- const candidate = path23.join(dir, name);
20774
+ const candidate = path24.join(dir, name);
19768
20775
  try {
19769
20776
  fs17.accessSync(candidate, fs17.constants.X_OK);
19770
20777
  return candidate;
@@ -19786,7 +20793,7 @@ var pushRemote = (cwd) => {
19786
20793
  };
19787
20794
  var safePushUrl = (remote) => {
19788
20795
  const raw = remote.trim();
19789
- if (path23.isAbsolute(raw))
20796
+ if (path24.isAbsolute(raw))
19790
20797
  return pathToFileURL(raw).href;
19791
20798
  if (!raw.includes("://")) {
19792
20799
  const scp = /^(?:([^@\s]+)@)?([^:]+):(.+)$/u.exec(raw);
@@ -19918,7 +20925,7 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
19918
20925
 
19919
20926
  // packages/workit-core/src/core/youtrack.ts
19920
20927
  import fs18 from "node:fs";
19921
- import path24 from "node:path";
20928
+ import path25 from "node:path";
19922
20929
 
19923
20930
  // packages/workit-core/src/core/templates.ts
19924
20931
  var repoRoot2 = assetRoot();
@@ -19931,8 +20938,8 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
19931
20938
  // packages/workit-core/src/core/youtrack.ts
19932
20939
  var ISSUE_RE = /^[A-Z]+-\d+$/;
19933
20940
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
19934
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path24.join(configDir(), "youtrack.json");
19935
- var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path24.join(resolveConfigDir(), "youtrack.json");
20941
+ var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path25.join(configDir(), "youtrack.json");
20942
+ var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path25.join(resolveConfigDir(), "youtrack.json");
19936
20943
  var youTrackTokenModeOk = (p) => {
19937
20944
  if (process.platform === "win32")
19938
20945
  return true;
@@ -20000,7 +21007,7 @@ var youTrackToken = () => {
20000
21007
  if ("error" in loaded)
20001
21008
  return loaded;
20002
21009
  const tokenFile = String(loaded.config.tokenFile ?? "");
20003
- const tokenPath = tokenFile ? path24.isAbsolute(tokenFile) ? path24.resolve(tokenFile) : path24.resolve(process.cwd(), tokenFile) : "";
21010
+ const tokenPath = tokenFile ? path25.isAbsolute(tokenFile) ? path25.resolve(tokenFile) : path25.resolve(process.cwd(), tokenFile) : "";
20004
21011
  if (!tokenPath || !fs18.existsSync(tokenPath))
20005
21012
  return { error: "missing youtrack.token" };
20006
21013
  if (!youTrackTokenModeOk(tokenPath))
@@ -20051,7 +21058,7 @@ function youTrackTokenCreateUrl() {
20051
21058
  const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
20052
21059
  const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
20053
21060
  const base = String(config.baseUrl ?? "").replace(/\/+$/, "");
20054
- const tokenFile = String(config.tokenFile ?? path24.join(path24.dirname(loaded.path), "youtrack.token"));
21061
+ const tokenFile = String(config.tokenFile ?? path25.join(path25.dirname(loaded.path), "youtrack.token"));
20055
21062
  const tab = String(defaults.profileTab ?? "account-security");
20056
21063
  const createUrl = base ? `${base}/users/me?${new URLSearchParams({ tab })}` : null;
20057
21064
  const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
@@ -20060,7 +21067,7 @@ function youTrackTokenCreateUrl() {
20060
21067
  tokenName: name,
20061
21068
  tokenDescription: desc,
20062
21069
  scopes,
20063
- tokenFile: path24.resolve(tokenFile),
21070
+ tokenFile: path25.resolve(tokenFile),
20064
21071
  createUrl,
20065
21072
  docsUrl,
20066
21073
  prefillSupported: false,
@@ -20400,15 +21407,15 @@ function resolveGitRevision(root, value) {
20400
21407
  }
20401
21408
  function resolveInside(root, candidate) {
20402
21409
  const base = realpathSync5(root);
20403
- const target = path25.resolve(base, candidate);
20404
- if (target !== base && !target.startsWith(base + path25.sep)) {
21410
+ const target = path26.resolve(base, candidate);
21411
+ if (target !== base && !target.startsWith(base + path26.sep)) {
20405
21412
  throw new Error("path must stay inside repository root");
20406
21413
  }
20407
21414
  let ancestor = target;
20408
- while (!existsSync8(ancestor))
20409
- ancestor = path25.dirname(ancestor);
21415
+ while (!existsSync9(ancestor))
21416
+ ancestor = path26.dirname(ancestor);
20410
21417
  const canonicalAncestor = realpathSync5(ancestor);
20411
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path25.sep)) {
21418
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path26.sep)) {
20412
21419
  throw new Error("path must stay inside repository root");
20413
21420
  }
20414
21421
  return target;
@@ -20452,7 +21459,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
20452
21459
  };
20453
21460
  });
20454
21461
  // packages/workit-core/src/hooks/context.ts
20455
- import path26 from "node:path";
21462
+ import path27 from "node:path";
20456
21463
  var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
20457
21464
  var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
20458
21465
  var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
@@ -20537,7 +21544,7 @@ function entryCompactContext(store, entry, session, context) {
20537
21544
  return null;
20538
21545
  const freshness = { mode: "signal", current: () => recentSignal(store.root) };
20539
21546
  const observed = task?.data.evidence.some(isObservedCheck) ?? false;
20540
- const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path26.resolve(store.root, ref.path)] : []));
21547
+ const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path27.resolve(store.root, ref.path)] : []));
20541
21548
  const observedFiles = filesKey(files);
20542
21549
  const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
20543
21550
  if (!compact.ok || !compact.data)
@@ -20567,10 +21574,61 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
20567
21574
  }
20568
21575
  return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
20569
21576
  };
21577
+ // packages/workit-core/src/hooks/delivery-verbs.ts
21578
+ var WORKIT_ALLOWED_VERBS = [
21579
+ "git",
21580
+ "pr",
21581
+ "ci",
21582
+ "stack",
21583
+ "release",
21584
+ "task",
21585
+ "ledger",
21586
+ "evidence",
21587
+ "finding",
21588
+ "decision",
21589
+ "worker",
21590
+ "verify-delivery",
21591
+ "policy",
21592
+ "doctor",
21593
+ "help"
21594
+ ];
21595
+ var WORKIT_BYPASS_FLAGS = ["--unverified", "--overwrite-unintegrated"];
21596
+ var ALLOWED = new Set(WORKIT_ALLOWED_VERBS);
21597
+ var isBypass = (word) => WORKIT_BYPASS_FLAGS.some((flag) => word === flag || word.startsWith(`${flag}=`));
21598
+ var isAllowedWorkitCall = (words, expands = []) => {
21599
+ if (words[0] !== "workit")
21600
+ return false;
21601
+ let at = 1;
21602
+ for (;; ) {
21603
+ const word = words[at];
21604
+ if (word === "--json")
21605
+ at += 1;
21606
+ else if (word === "--cwd")
21607
+ at += 2;
21608
+ else if (word?.startsWith("--cwd="))
21609
+ at += 1;
21610
+ else
21611
+ break;
21612
+ }
21613
+ const verb = words[at];
21614
+ if (verb === undefined)
21615
+ return false;
21616
+ if (verb === "--version")
21617
+ return at === words.length - 1;
21618
+ if (!ALLOWED.has(verb))
21619
+ return false;
21620
+ const end = words.indexOf("--");
21621
+ const limit = end < 0 ? words.length : end;
21622
+ return !words.slice(0, limit).some(isBypass) && !expands.some((index) => index < limit);
21623
+ };
21624
+ var isAllowedWorkitCommand = (command) => {
21625
+ const segments = segmentsOf(command);
21626
+ return segments.length > 0 && segments.every((s) => !s.unanalyzable && !s.group && !s.subs?.length && !s.stdin?.length && !s.redirects.length && isAllowedWorkitCall(s.words, s.expands));
21627
+ };
20570
21628
  // packages/workit-core/src/core/heavy-rules.ts
20571
21629
  import fs19 from "node:fs";
20572
21630
  import os6 from "node:os";
20573
- import path27 from "node:path";
21631
+ import path28 from "node:path";
20574
21632
  var EMPTY2 = () => ({ deny: [], allow: [] });
20575
21633
  var readText2 = (file) => {
20576
21634
  try {
@@ -20611,19 +21669,19 @@ function parseJsonc(text) {
20611
21669
  return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
20612
21670
  }
20613
21671
  function gitTop(cwd) {
20614
- for (let dir = path27.resolve(cwd);; dir = path27.dirname(dir)) {
20615
- if (fs19.existsSync(path27.join(dir, ".git")))
21672
+ for (let dir = path28.resolve(cwd);; dir = path28.dirname(dir)) {
21673
+ if (fs19.existsSync(path28.join(dir, ".git")))
20616
21674
  return dir;
20617
- if (path27.dirname(dir) === dir)
21675
+ if (path28.dirname(dir) === dir)
20618
21676
  return null;
20619
21677
  }
20620
21678
  }
20621
21679
  var projectDirs = (cwd) => {
20622
21680
  const top = gitTop(cwd);
20623
21681
  const dirs = [];
20624
- for (let dir = path27.resolve(cwd);; dir = path27.dirname(dir)) {
21682
+ for (let dir = path28.resolve(cwd);; dir = path28.dirname(dir)) {
20625
21683
  dirs.push(dir);
20626
- if (!top || dir === top || path27.dirname(dir) === dir)
21684
+ if (!top || dir === top || path28.dirname(dir) === dir)
20627
21685
  return dirs;
20628
21686
  }
20629
21687
  };
@@ -20635,12 +21693,12 @@ var CLAUDE_MANAGED = {
20635
21693
  };
20636
21694
  function claudeSettingsFiles(cwd, env, platform = process.platform) {
20637
21695
  const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
20638
- const user = env.CLAUDE_CONFIG_DIR || path27.join(homeOf(env), ".claude");
21696
+ const user = env.CLAUDE_CONFIG_DIR || path28.join(homeOf(env), ".claude");
20639
21697
  return [
20640
21698
  ...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
20641
- path27.join(user, "settings.json"),
20642
- path27.join(project, ".claude", "settings.json"),
20643
- path27.join(project, ".claude", "settings.local.json")
21699
+ path28.join(user, "settings.json"),
21700
+ path28.join(project, ".claude", "settings.json"),
21701
+ path28.join(project, ".claude", "settings.local.json")
20644
21702
  ];
20645
21703
  }
20646
21704
  var bashPattern = (entry) => {
@@ -20730,10 +21788,10 @@ var opencodeConfig = (value, rules) => {
20730
21788
  function opencodeRules(cwd, env = process.env) {
20731
21789
  const rules = EMPTY2();
20732
21790
  try {
20733
- const config = env.XDG_CONFIG_HOME || path27.join(homeOf(env), ".config");
21791
+ const config = env.XDG_CONFIG_HOME || path28.join(homeOf(env), ".config");
20734
21792
  const files = [
20735
- ...OPENCODE_NAMES.map((name) => path27.join(config, "opencode", name)),
20736
- ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path27.join(dir, name), path27.join(dir, ".opencode", name)])),
21793
+ ...OPENCODE_NAMES.map((name) => path28.join(config, "opencode", name)),
21794
+ ...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path28.join(dir, name), path28.join(dir, ".opencode", name)])),
20737
21795
  ...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
20738
21796
  ];
20739
21797
  for (const file of files) {
@@ -20777,9 +21835,9 @@ var codexRule = (body) => {
20777
21835
  function codexRules(cwd, env = process.env) {
20778
21836
  const rules = EMPTY2();
20779
21837
  try {
20780
- const home = env.CODEX_HOME || path27.join(homeOf(env), ".codex");
21838
+ const home = env.CODEX_HOME || path28.join(homeOf(env), ".codex");
20781
21839
  const top = gitTop(cwd) ?? cwd;
20782
- for (const dir of [path27.join(home, "rules"), path27.join(top, ".codex", "rules")]) {
21840
+ for (const dir of [path28.join(home, "rules"), path28.join(top, ".codex", "rules")]) {
20783
21841
  let files;
20784
21842
  try {
20785
21843
  files = fs19.readdirSync(dir).filter((name) => name.endsWith(".rules"));
@@ -20789,7 +21847,7 @@ function codexRules(cwd, env = process.env) {
20789
21847
  return "unreadable";
20790
21848
  }
20791
21849
  for (const name of files) {
20792
- const text = readText2(path27.join(dir, name)) ?? "";
21850
+ const text = readText2(path28.join(dir, name)) ?? "";
20793
21851
  for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
20794
21852
  const rule = codexRule(part);
20795
21853
  if (!rule)
@@ -20808,8 +21866,8 @@ function cursorRules(cwd, env = process.env) {
20808
21866
  try {
20809
21867
  const top = gitTop(cwd) ?? cwd;
20810
21868
  for (const file of [
20811
- path27.join(homeOf(env), ".cursor", "cli-config.json"),
20812
- path27.join(top, ".cursor", "cli.json")
21869
+ path28.join(homeOf(env), ".cursor", "cli-config.json"),
21870
+ path28.join(top, ".cursor", "cli.json")
20813
21871
  ]) {
20814
21872
  const text = readText2(file);
20815
21873
  if (text === null)
@@ -20885,21 +21943,21 @@ function wrapAllowed(read, words, command, options = {}) {
20885
21943
  // packages/workit-core/src/core/heavy.ts
20886
21944
  import fs20 from "node:fs";
20887
21945
  import os7 from "node:os";
20888
- import path28 from "node:path";
21946
+ import path29 from "node:path";
20889
21947
  function stateDir(env, platform = process.platform) {
20890
21948
  if (env.WORKFLOW_TOOLKIT_STATE)
20891
21949
  return env.WORKFLOW_TOOLKIT_STATE;
20892
21950
  if (env.XDG_STATE_HOME)
20893
- return path28.join(env.XDG_STATE_HOME, "workit");
21951
+ return path29.join(env.XDG_STATE_HOME, "workit");
20894
21952
  const home = env.HOME || os7.homedir();
20895
21953
  if (platform === "darwin")
20896
- return path28.join(home, "Library", "Application Support", "workit");
21954
+ return path29.join(home, "Library", "Application Support", "workit");
20897
21955
  if (platform === "win32")
20898
- return path28.join(env.LOCALAPPDATA ?? path28.join(home, "AppData", "Local"), "workit");
20899
- return path28.join(home, ".local", "state", "workit");
21956
+ return path29.join(env.LOCALAPPDATA ?? path29.join(home, "AppData", "Local"), "workit");
21957
+ return path29.join(home, ".local", "state", "workit");
20900
21958
  }
20901
21959
  var HEAVY_COMMANDS_FILE = "heavy-commands.json";
20902
- var heavyCommandsFile = (env) => path28.join(stateDir(env), HEAVY_COMMANDS_FILE);
21960
+ var heavyCommandsFile = (env) => path29.join(stateDir(env), HEAVY_COMMANDS_FILE);
20903
21961
  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;
20904
21962
  var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
20905
21963
  var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
@@ -21174,7 +22232,7 @@ function machinePatterns(env) {
21174
22232
  return isRecord5(value) ? stringList(value.patterns) : [];
21175
22233
  }
21176
22234
  function repoPatterns(cwd) {
21177
- return repoHeavyCommands(gitTop(cwd) ?? path28.resolve(cwd));
22235
+ return repoHeavyCommands(gitTop(cwd) ?? path29.resolve(cwd));
21178
22236
  }
21179
22237
  function heavyLines(lines, options) {
21180
22238
  const found = [];
@@ -21252,12 +22310,12 @@ function heavyPlan(command, options) {
21252
22310
  }
21253
22311
  function workitOnPath2(env = process.env) {
21254
22312
  const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
21255
- for (const dir of (env.PATH ?? "").split(path28.delimiter))
22313
+ for (const dir of (env.PATH ?? "").split(path29.delimiter))
21256
22314
  for (const name of names) {
21257
22315
  if (!dir)
21258
22316
  continue;
21259
22317
  try {
21260
- fs20.accessSync(path28.join(dir, name), fs20.constants.X_OK);
22318
+ fs20.accessSync(path29.join(dir, name), fs20.constants.X_OK);
21261
22319
  return true;
21262
22320
  } catch {}
21263
22321
  }
@@ -21276,20 +22334,20 @@ function leaseRewrite(command) {
21276
22334
  // packages/workit-core/src/core/heavy-words.ts
21277
22335
  import fs21 from "node:fs";
21278
22336
  import os8 from "node:os";
21279
- import path29 from "node:path";
22337
+ import path30 from "node:path";
21280
22338
  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(" "));
21281
22339
  var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
21282
22340
  var stateDir2 = (env) => {
21283
22341
  if (env.WORKFLOW_TOOLKIT_STATE)
21284
22342
  return env.WORKFLOW_TOOLKIT_STATE;
21285
22343
  if (env.XDG_STATE_HOME)
21286
- return path29.join(env.XDG_STATE_HOME, "workit");
22344
+ return path30.join(env.XDG_STATE_HOME, "workit");
21287
22345
  const home = env.HOME || os8.homedir();
21288
22346
  if (process.platform === "darwin")
21289
- return path29.join(home, "Library", "Application Support", "workit");
22347
+ return path30.join(home, "Library", "Application Support", "workit");
21290
22348
  if (process.platform === "win32")
21291
- return path29.join(env.LOCALAPPDATA ?? path29.join(home, "AppData", "Local"), "workit");
21292
- return path29.join(home, ".local", "state", "workit");
22349
+ return path30.join(env.LOCALAPPDATA ?? path30.join(home, "AppData", "Local"), "workit");
22350
+ return path30.join(home, ".local", "state", "workit");
21293
22351
  };
21294
22352
  var readJson2 = (file) => {
21295
22353
  try {
@@ -21300,15 +22358,15 @@ var readJson2 = (file) => {
21300
22358
  };
21301
22359
  var patternWords = (cwd, env) => {
21302
22360
  const patterns = [];
21303
- const machine = readJson2(path29.join(stateDir2(env), "heavy-commands.json"));
22361
+ const machine = readJson2(path30.join(stateDir2(env), "heavy-commands.json"));
21304
22362
  if (Array.isArray(machine?.patterns))
21305
22363
  patterns.push(...machine.patterns);
21306
- for (let dir = path29.resolve(cwd);; dir = path29.dirname(dir)) {
21307
- if (fs21.existsSync(path29.join(dir, ".git"))) {
22364
+ for (let dir = path30.resolve(cwd);; dir = path30.dirname(dir)) {
22365
+ if (fs21.existsSync(path30.join(dir, ".git"))) {
21308
22366
  patterns.push(...repoHeavyCommands(dir));
21309
22367
  break;
21310
22368
  }
21311
- if (path29.dirname(dir) === dir)
22369
+ if (path30.dirname(dir) === dir)
21312
22370
  break;
21313
22371
  }
21314
22372
  const words = new Set;
@@ -21367,16 +22425,16 @@ function heavyGate(cwd, command, options) {
21367
22425
  }
21368
22426
  }
21369
22427
  // packages/workit-core/src/hooks/write-gate.ts
21370
- import { existsSync as existsSync9, realpathSync as realpathSync6 } from "node:fs";
22428
+ import { existsSync as existsSync10, realpathSync as realpathSync6 } from "node:fs";
21371
22429
  import os10 from "node:os";
21372
- import path31 from "node:path";
22430
+ import path32 from "node:path";
21373
22431
 
21374
22432
  // packages/workit-core/src/core/live-session.ts
21375
22433
  import { spawnSync as spawnSync17 } from "node:child_process";
21376
22434
  import { createHash as createHash15 } from "node:crypto";
21377
22435
  import fs22 from "node:fs";
21378
22436
  import os9 from "node:os";
21379
- import path30 from "node:path";
22437
+ import path31 from "node:path";
21380
22438
  var LIVE_SESSION_TTL_MS = 15 * 60000;
21381
22439
  var HEARTBEAT_EVERY_MS = 30000;
21382
22440
  var liveProbe = {
@@ -21450,15 +22508,15 @@ var liveOwner = (input, transport, env = process.env) => {
21450
22508
  };
21451
22509
  };
21452
22510
  var checkoutOf = (target) => {
21453
- let dir = path30.resolve(target);
21454
- while (!fs22.existsSync(dir) && path30.dirname(dir) !== dir)
21455
- dir = path30.dirname(dir);
22511
+ let dir = path31.resolve(target);
22512
+ while (!fs22.existsSync(dir) && path31.dirname(dir) !== dir)
22513
+ dir = path31.dirname(dir);
21456
22514
  if (fs22.existsSync(dir) && !fs22.statSync(dir).isDirectory())
21457
- dir = path30.dirname(dir);
21458
- const parts = dir.split(path30.sep);
22515
+ dir = path31.dirname(dir);
22516
+ const parts = dir.split(path31.sep);
21459
22517
  const dotGit = parts.lastIndexOf(".git");
21460
22518
  if (dotGit > 0)
21461
- dir = parts.slice(0, dotGit).join(path30.sep) || path30.sep;
22519
+ dir = parts.slice(0, dotGit).join(path31.sep) || path31.sep;
21462
22520
  const location = resolveStore(dir);
21463
22521
  if (location instanceof Error || !location.shared || !location.top)
21464
22522
  return null;
@@ -21468,9 +22526,9 @@ var checkoutOf = (target) => {
21468
22526
  } catch {}
21469
22527
  return top;
21470
22528
  };
21471
- var markerFile2 = (top) => path30.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
22529
+ var markerFile2 = (top) => path31.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
21472
22530
  var checkoutKey = (top, platform = process.platform) => {
21473
- let key = path30.resolve(top);
22531
+ let key = path31.resolve(top);
21474
22532
  try {
21475
22533
  key = fs22.realpathSync.native(key);
21476
22534
  } catch {}
@@ -21565,7 +22623,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
21565
22623
  checkout,
21566
22624
  heartbeatAt: now.toISOString()
21567
22625
  };
21568
- fs22.mkdirSync(path30.dirname(file), { recursive: true });
22626
+ fs22.mkdirSync(path31.dirname(file), { recursive: true });
21569
22627
  const temp = `${file}.${process.pid}.tmp`;
21570
22628
  fs22.writeFileSync(temp, JSON.stringify(marker));
21571
22629
  fs22.renameSync(temp, file);
@@ -21577,22 +22635,22 @@ var NONE3 = { kind: "none" };
21577
22635
  var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
21578
22636
  var canonical2 = (file) => {
21579
22637
  let rest = "";
21580
- for (let dir = path31.resolve(file);; dir = path31.dirname(dir)) {
22638
+ for (let dir = path32.resolve(file);; dir = path32.dirname(dir)) {
21581
22639
  try {
21582
- return path31.join(realpathSync6.native(dir), rest);
22640
+ return path32.join(realpathSync6.native(dir), rest);
21583
22641
  } catch {
21584
- if (path31.dirname(dir) === dir)
21585
- return path31.resolve(file);
21586
- rest = rest ? path31.join(path31.basename(dir), rest) : path31.basename(dir);
22642
+ if (path32.dirname(dir) === dir)
22643
+ return path32.resolve(file);
22644
+ rest = rest ? path32.join(path32.basename(dir), rest) : path32.basename(dir);
21587
22645
  }
21588
22646
  }
21589
22647
  };
21590
22648
  var exempt = (cwd, root, file, plans) => {
21591
22649
  const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
21592
- const relative = path31.relative(fold(canonical2(root)), fold(canonical2(path31.resolve(cwd, file))));
21593
- if (relative.startsWith("..") || path31.isAbsolute(relative))
22650
+ const relative = path32.relative(fold(canonical2(root)), fold(canonical2(path32.resolve(cwd, file))));
22651
+ if (relative.startsWith("..") || path32.isAbsolute(relative))
21594
22652
  return true;
21595
- const posix = relative.split(path31.sep).join("/");
22653
+ const posix = relative.split(path32.sep).join("/");
21596
22654
  const segments = posix.split("/");
21597
22655
  return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
21598
22656
  };
@@ -21769,10 +22827,10 @@ var linkSources = (args, at) => {
21769
22827
  const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
21770
22828
  const target = dest === undefined ? null : at(dest);
21771
22829
  const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
21772
- const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync9(target) || /[\\/]$/.test(dest ?? "") ? target : path31.dirname(target);
22830
+ const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync10(target) || /[\\/]$/.test(dest ?? "") ? target : path32.dirname(target);
21773
22831
  return sources.flatMap((source) => {
21774
- if (symbolic && base !== null && !path31.isAbsolute(homeExpanded(source)))
21775
- return [path31.resolve(base, source)];
22832
+ if (symbolic && base !== null && !path32.isAbsolute(homeExpanded(source)))
22833
+ return [path32.resolve(base, source)];
21776
22834
  const resolved = at(source);
21777
22835
  return resolved ? [resolved] : [];
21778
22836
  });
@@ -21801,7 +22859,7 @@ var shellWrites = (command, depth = 0) => {
21801
22859
  return { writes: true, targets: unknown ? null : targets };
21802
22860
  };
21803
22861
  var cache2 = new Map;
21804
- var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path31.resolve(root, file)) ?? "-").join("\x00");
22862
+ var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path32.resolve(root, file)) ?? "-").join("\x00");
21805
22863
  var gateFor = (cwd) => {
21806
22864
  const store = new TaskStore(cwd);
21807
22865
  const listed = store.listTaskIndex();
@@ -21880,13 +22938,13 @@ var gitMutationDirs = (cwd, command, dialect) => {
21880
22938
  return [];
21881
22939
  if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
21882
22940
  return [];
21883
- return [path31.resolve(cwd, invocation.dir ?? ".")];
22941
+ return [path32.resolve(cwd, invocation.dir ?? ".")];
21884
22942
  });
21885
22943
  };
21886
22944
  var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
21887
22945
  var PUSHD2 = new Set(["pushd", "push-location"]);
21888
22946
  var POPD2 = new Set(["popd", "pop-location"]);
21889
- var UNKNOWN_DIR = path31.resolve(path31.parse(process.cwd()).root, ".workit-unknown-dir");
22947
+ var UNKNOWN_DIR = path32.resolve(path32.parse(process.cwd()).root, ".workit-unknown-dir");
21890
22948
  var cdInto = (dir, words, strict = false) => {
21891
22949
  const lost = strict ? UNKNOWN_DIR : undefined;
21892
22950
  const named = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
@@ -21895,7 +22953,7 @@ var cdInto = (dir, words, strict = false) => {
21895
22953
  return lost;
21896
22954
  if (dir === undefined)
21897
22955
  return lost;
21898
- return target === undefined ? os10.homedir() : path31.resolve(dir, target);
22956
+ return target === undefined ? os10.homedir() : path32.resolve(dir, target);
21899
22957
  };
21900
22958
  var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
21901
22959
  var homeExpanded = (file) => {
@@ -21909,7 +22967,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21909
22967
  const groups = [];
21910
22968
  const at = (file) => {
21911
22969
  const home = homeExpanded(file);
21912
- return path31.isAbsolute(home) ? home : dir === undefined ? null : path31.resolve(dir, home);
22970
+ return path32.isAbsolute(home) ? home : dir === undefined ? null : path32.resolve(dir, home);
21913
22971
  };
21914
22972
  for (const segment of segmentsOf(command, dialect)) {
21915
22973
  if (segment.group === "open") {
@@ -21940,7 +22998,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21940
22998
  }
21941
22999
  const inner = depth < 4 ? wrappedScript(words) ?? (strict ? shellScript2(words) : null) : null;
21942
23000
  if (inner !== null && dir !== undefined)
21943
- targets.push(...segmentTargets(dir, inner, "posix", depth + 1, strict));
23001
+ targets.push(...segmentTargets(workitRunDir(dir, words), inner, "posix", depth + 1, strict));
21944
23002
  if (strict && dir !== undefined) {
21945
23003
  const runIn = dir;
21946
23004
  for (const sub of depth < 4 ? segment.subs ?? [] : [])
@@ -21949,7 +23007,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21949
23007
  for (const file of opaque ?? []) {
21950
23008
  const resolved = at(file);
21951
23009
  if (resolved)
21952
- targets.push(file === "." ? path31.join(resolved, DIR_TARGET) : resolved);
23010
+ targets.push(file === "." ? path32.join(resolved, DIR_TARGET) : resolved);
21953
23011
  }
21954
23012
  const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
21955
23013
  if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
@@ -21964,7 +23022,7 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21964
23022
  const edits = editsOf(segment.words);
21965
23023
  if (edits === null) {
21966
23024
  if (dir !== undefined)
21967
- targets.push(path31.join(dir, DIR_TARGET));
23025
+ targets.push(path32.join(dir, DIR_TARGET));
21968
23026
  } else
21969
23027
  for (const file of edits) {
21970
23028
  const resolved = at(file);
@@ -21975,20 +23033,28 @@ var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
21975
23033
  for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
21976
23034
  const { at: runsIn, args } = workitCwd(dir, invocation);
21977
23035
  if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
21978
- targets.push(path31.join(runsIn, DIR_TARGET));
23036
+ targets.push(path32.join(runsIn, DIR_TARGET));
21979
23037
  }
21980
23038
  }
21981
23039
  return targets;
21982
23040
  };
23041
+ var workitRunDir = (dir, words) => {
23042
+ for (const invocation of workitInvocations(words.map(quoteWord2).join(" "), "posix")) {
23043
+ const { at, args } = workitCwd(dir, invocation);
23044
+ if (args[0] === "check" || args[0] === "lock" && args[1] === "run")
23045
+ return at;
23046
+ }
23047
+ return dir;
23048
+ };
21983
23049
  var workitCwd = (dir, args) => {
21984
23050
  const { rest, cwd } = skipWorkitGlobals(args);
21985
- return { at: cwd === null ? dir : path31.resolve(dir, homeExpanded(cwd)), args: rest };
23051
+ return { at: cwd === null ? dir : path32.resolve(dir, homeExpanded(cwd)), args: rest };
21986
23052
  };
21987
23053
  var shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
21988
23054
  try {
21989
23055
  return [
21990
23056
  ...segmentTargets(cwd, command, dialect, 0, strict),
21991
- ...gitMutationDirs(cwd, command, dialect).map((dir) => path31.join(dir, DIR_TARGET))
23057
+ ...gitMutationDirs(cwd, command, dialect).map((dir) => path32.join(dir, DIR_TARGET))
21992
23058
  ];
21993
23059
  } catch {
21994
23060
  return [];
@@ -22025,7 +23091,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
22025
23091
  var guarded = (targets) => {
22026
23092
  const seen = new Set;
22027
23093
  return targets.filter((target) => {
22028
- const dir = path31.dirname(target);
23094
+ const dir = path32.dirname(target);
22029
23095
  if (seen.has(dir))
22030
23096
  return false;
22031
23097
  seen.add(dir);
@@ -22046,7 +23112,7 @@ var claimAll = (owner, targets, deps) => {
22046
23112
  for (const target of guarded(targets))
22047
23113
  claimLiveSession(target, owner, deps);
22048
23114
  };
22049
- var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path31.resolve(cwd, file)) : [path31.join(cwd, DIR_TARGET)];
23115
+ var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path32.resolve(cwd, file)) : [path32.join(cwd, DIR_TARGET)];
22050
23116
  var issuedTarget = (cwd, targets) => {
22051
23117
  const state = resolveStateDir();
22052
23118
  const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
@@ -22054,7 +23120,7 @@ var issuedTarget = (cwd, targets) => {
22054
23120
  };
22055
23121
  var underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
22056
23122
  const win = platform === "win32";
22057
- const p = win ? path31.win32 : path31.posix;
23123
+ const p = win ? path32.win32 : path32.posix;
22058
23124
  const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
22059
23125
  const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
22060
23126
  const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
@@ -22145,6 +23211,24 @@ function beforeWriteGate(cwd, paths) {
22145
23211
  }
22146
23212
  // packages/workit-core/src/hooks/stop.ts
22147
23213
  import { spawnSync as spawnSync18 } from "node:child_process";
23214
+
23215
+ // packages/workit-core/src/core/policy/current-judgment.ts
23216
+ function currentJudgment(cwd) {
23217
+ try {
23218
+ const store = new TaskStore(cwd);
23219
+ const listed = store.listTaskIndex();
23220
+ const key = store.currentKey();
23221
+ if (!listed.ok || !key.ok)
23222
+ return null;
23223
+ const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
23224
+ const task = entry ? store.readTask(entry.id) : null;
23225
+ return task?.ok === true ? latestJudgment(task.data) : null;
23226
+ } catch {
23227
+ return null;
23228
+ }
23229
+ }
23230
+
23231
+ // packages/workit-core/src/hooks/stop.ts
22148
23232
  var NONE4 = { kind: "none" };
22149
23233
  var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
22150
23234
  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;
@@ -22237,29 +23321,32 @@ var redChecks = (rows, pr, head, endpoint) => {
22237
23321
  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)" : ""}.`
22238
23322
  };
22239
23323
  };
22240
- var taskJudgment = (cwd) => {
22241
- try {
22242
- const store = new TaskStore(cwd);
22243
- const listed = store.listTaskIndex();
22244
- const key = store.currentKey();
22245
- if (!listed.ok || !key.ok)
22246
- return null;
22247
- const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
22248
- const task = entry ? store.readTask(entry.id) : null;
22249
- return task?.ok === true ? latestJudgment(task.data) : null;
22250
- } catch {
22251
- return null;
22252
- }
23324
+ var prBase = (rows) => {
23325
+ const seen = rows.findLast((row) => (row.type === "pr.status" || row.type === "pr.created") && row.observer === "workit_cli" && typeof row.base === "string");
23326
+ return typeof seen?.base === "string" ? seen.base : null;
22253
23327
  };
22254
- var unverified = (cwd, branch, all, descriptor, verification) => {
23328
+ var unverified = (cwd, branch, all, descriptor, verification, head) => {
22255
23329
  const check = checkVerdicts(cwd, branch, all);
22256
23330
  if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
22257
23331
  return null;
22258
- const judgment = taskJudgment(cwd);
23332
+ const judgment = currentJudgment(cwd);
22259
23333
  const high = judgment?.riskTier === "high";
22260
- if (judgment && !judgment.behaviorChange && !high)
22261
- return null;
22262
- const independent = verification === "independent" || high;
23334
+ let independent;
23335
+ if (verification === "proportional") {
23336
+ const lane = branchLane(cwd, {
23337
+ base: prBase(all.filter((row) => row.branch === branch)),
23338
+ head,
23339
+ judgment
23340
+ });
23341
+ const need = laneNeed(lane.lane);
23342
+ if (need === "none")
23343
+ return null;
23344
+ independent = need === "non-author";
23345
+ } else {
23346
+ if (judgment && !judgment.behaviorChange && !high)
23347
+ return null;
23348
+ independent = verification === "independent" || high;
23349
+ }
22263
23350
  if (!independent && check.review === "self-reviewed")
22264
23351
  return null;
22265
23352
  const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
@@ -22306,7 +23393,7 @@ function stopObligation(input, descriptor) {
22306
23393
  }
22307
23394
  if (!delivered || endpoint !== "green" && endpoint !== "merged")
22308
23395
  return null;
22309
- const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
23396
+ const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
22310
23397
  return left ? { ...left, head } : null;
22311
23398
  } catch {
22312
23399
  return null;
@@ -22576,9 +23663,9 @@ var WRITE_TOOLS = new Set([
22576
23663
  ]);
22577
23664
  // packages/workit-core/src/hooks/standing.ts
22578
23665
  import fs23 from "node:fs";
22579
- import path32 from "node:path";
23666
+ import path33 from "node:path";
22580
23667
  var readPlans = (store) => {
22581
- const dir = path32.join(store, "fanouts");
23668
+ const dir = path33.join(store, "fanouts");
22582
23669
  let names;
22583
23670
  try {
22584
23671
  names = fs23.readdirSync(dir).filter((name) => name.endsWith(".json"));
@@ -22588,7 +23675,7 @@ var readPlans = (store) => {
22588
23675
  const plans = [];
22589
23676
  for (const name of names) {
22590
23677
  try {
22591
- const raw = JSON.parse(fs23.readFileSync(path32.join(dir, name), "utf8"));
23678
+ const raw = JSON.parse(fs23.readFileSync(path33.join(dir, name), "utf8"));
22592
23679
  if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
22593
23680
  continue;
22594
23681
  const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
@@ -22676,6 +23763,10 @@ var standingSinceStart = (input) => {
22676
23763
  }
22677
23764
  };
22678
23765
  // packages/workit-core/src/hooks/cwd-normalize.ts
23766
+ var DIR = String.raw`[A-Za-z0-9_./@%+:,=-]+|'[^'$${"`"}\\\n]*'|"[^"$${"`"}\\!\n]*"`;
23767
+ var COMPOUND = new RegExp(String.raw`^\s*cd\s+(${DIR})\s*&&\s*(workit(?:\s[\s\S]*)?)$`, "u");
23768
+ var GLOBALS_FIRST = new RegExp(String.raw`^(\s*workit)((?:\s+(?:--json|--cwd(?:=|\s+)(?:${DIR})))+)\s+([a-z][a-z-]*)(?=\s|$)`, "u");
23769
+ var VERBS = new Set(WORKIT_ALLOWED_VERBS);
22679
23770
  var FILTERS = new Set(["tail", "head", "grep"]);
22680
23771
 
22681
23772
  // packages/workit-core/src/hooks/worktree-confine.ts
@@ -22796,11 +23887,11 @@ var OPENCODE_DESCRIPTOR = {
22796
23887
  ]
22797
23888
  };
22798
23889
  // packages/workit-opencode/src/shared/init-apply.ts
22799
- import path37 from "node:path";
23890
+ import path38 from "node:path";
22800
23891
 
22801
23892
  // packages/workit-core/src/core/gitignore.ts
22802
- import { existsSync as existsSync10, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
22803
- import path33 from "node:path";
23893
+ import { existsSync as existsSync11, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
23894
+ import path34 from "node:path";
22804
23895
  var GITIGNORE_ENTRIES = [
22805
23896
  "# workit: SDD working state (never commit)",
22806
23897
  "docs/*/sdd/",
@@ -22820,8 +23911,8 @@ var GITIGNORE_ENTRIES = [
22820
23911
  var ensureProjectGitignore = (workspaceRoot, confirmed) => {
22821
23912
  if (!confirmed)
22822
23913
  return { ok: false, error: "confirmed: true required" };
22823
- const file = path33.join(workspaceRoot, ".gitignore");
22824
- const existing = existsSync10(file) ? readFileSync6(file, "utf8") : "";
23914
+ const file = path34.join(workspaceRoot, ".gitignore");
23915
+ const existing = existsSync11(file) ? readFileSync7(file, "utf8") : "";
22825
23916
  const existingLines = new Set(existing.split(`
22826
23917
  `).map((l) => l.trim()).filter(Boolean));
22827
23918
  const added = [];
@@ -22840,33 +23931,33 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
22840
23931
  ` : "") + append.join(`
22841
23932
  `) + `
22842
23933
  `, "utf8");
22843
- } else if (!existsSync10(file)) {
23934
+ } else if (!existsSync11(file)) {
22844
23935
  writeFileSync4(file, "", "utf8");
22845
23936
  }
22846
23937
  return { ok: true, path: file, added };
22847
23938
  };
22848
23939
 
22849
23940
  // packages/workit-core/src/core/hygiene.ts
22850
- import { existsSync as existsSync11, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
22851
- import path34 from "node:path";
23941
+ import { existsSync as existsSync12, readFileSync as readFileSync8, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
23942
+ import path35 from "node:path";
22852
23943
  var repoRoot3 = assetRoot();
22853
- var templatesDir = () => path34.join(repoRoot3, "templates", "hygiene");
23944
+ var templatesDir = () => path35.join(repoRoot3, "templates", "hygiene");
22854
23945
  var packageJson = (root) => {
22855
- if (!existsSync11(path34.join(root, "package.json")))
23946
+ if (!existsSync12(path35.join(root, "package.json")))
22856
23947
  return null;
22857
23948
  try {
22858
- return JSON.parse(readFileSync7(path34.join(root, "package.json"), "utf8"));
23949
+ return JSON.parse(readFileSync8(path35.join(root, "package.json"), "utf8"));
22859
23950
  } catch {
22860
23951
  return null;
22861
23952
  }
22862
23953
  };
22863
23954
  var isOpenSource = (root) => {
22864
- if (existsSync11(path34.join(root, "LICENSE")))
23955
+ if (existsSync12(path35.join(root, "LICENSE")))
22865
23956
  return true;
22866
23957
  const pkg = packageJson(root);
22867
23958
  if (pkg && !pkg.private)
22868
23959
  return true;
22869
- return path34.basename(root) === "workflow-toolkit";
23960
+ return path35.basename(root) === "workflow-toolkit";
22870
23961
  };
22871
23962
  var licenseHolder = (root) => {
22872
23963
  const pkg = packageJson(root);
@@ -22889,13 +23980,13 @@ var planHygieneFiles = (root, opts = {}) => {
22889
23980
  files.push("LICENSE", "CONTRIBUTING.md");
22890
23981
  const planned = [];
22891
23982
  for (const file of files) {
22892
- if (existsSync11(path34.join(root, file)))
23983
+ if (existsSync12(path35.join(root, file)))
22893
23984
  continue;
22894
- const tpl = path34.join(templatesDir(), file);
22895
- if (!existsSync11(tpl))
23985
+ const tpl = path35.join(templatesDir(), file);
23986
+ if (!existsSync12(tpl))
22896
23987
  continue;
22897
- const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path34.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
22898
- planned.push({ path: path34.join(root, file), content });
23988
+ const content = readFileSync8(tpl, "utf8").replace(/<PROJECT>/g, path35.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
23989
+ planned.push({ path: path35.join(root, file), content });
22899
23990
  }
22900
23991
  return planned;
22901
23992
  };
@@ -22905,30 +23996,30 @@ var ensureHygieneFiles = (root, opts) => {
22905
23996
  const created = [];
22906
23997
  for (const planned of planHygieneFiles(root, opts)) {
22907
23998
  writeFileSync5(planned.path, planned.content, "utf8");
22908
- created.push(path34.basename(planned.path));
23999
+ created.push(path35.basename(planned.path));
22909
24000
  }
22910
24001
  return { ok: true, created };
22911
24002
  };
22912
24003
 
22913
24004
  // packages/workit-core/src/core/init.ts
22914
24005
  import fs24 from "node:fs";
22915
- import path36 from "node:path";
24006
+ import path37 from "node:path";
22916
24007
  import { spawnSync as spawnSync19 } from "node:child_process";
22917
24008
 
22918
24009
  // packages/workit-core/src/core/workspace-branch-policy.ts
22919
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
22920
- import path35 from "node:path";
24010
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync9, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
24011
+ import path36 from "node:path";
22921
24012
  import { isDeepStrictEqual } from "node:util";
22922
24013
  var readFileSafe = (p) => {
22923
24014
  try {
22924
- return readFileSync8(p, "utf8");
24015
+ return readFileSync9(p, "utf8");
22925
24016
  } catch {
22926
24017
  return null;
22927
24018
  }
22928
24019
  };
22929
24020
  function applyWorkspaceBranchPolicy(opts) {
22930
24021
  const { workspace_root, env = process.env } = opts;
22931
- const dir = path35.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
24022
+ const dir = path36.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
22932
24023
  const {
22933
24024
  status,
22934
24025
  path: wsPath,
@@ -22940,7 +24031,7 @@ function applyWorkspaceBranchPolicy(opts) {
22940
24031
  if (status === "malformed" || status === "invalid")
22941
24032
  return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
22942
24033
  const detection = detectBranchPolicy(workspace_root);
22943
- const name = String(env.WORKFLOW_BP_NAME ?? path35.basename(workspace_root));
24034
+ const name = String(env.WORKFLOW_BP_NAME ?? path36.basename(workspace_root));
22944
24035
  const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
22945
24036
  const policy = {
22946
24037
  preset: detection.preset,
@@ -22989,7 +24080,7 @@ function applyWorkspaceBranchPolicy(opts) {
22989
24080
  ok: false,
22990
24081
  error: `workspaces.json changed while applying the branch policy: ${wsPath}`
22991
24082
  };
22992
- mkdirSync4(path35.dirname(wsPath), { recursive: true });
24083
+ mkdirSync4(path36.dirname(wsPath), { recursive: true });
22993
24084
  const tmp = `${wsPath}.${process.pid}.tmp`;
22994
24085
  try {
22995
24086
  writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
@@ -23054,7 +24145,7 @@ var youtrackJsonContent = (dir) => {
23054
24145
  const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
23055
24146
  return {
23056
24147
  baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
23057
- tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path36.join(dir, "youtrack.token"),
24148
+ tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path37.join(dir, "youtrack.token"),
23058
24149
  ...meetingIssue ? {
23059
24150
  meetingIssue,
23060
24151
  meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
@@ -23087,16 +24178,16 @@ function initApplyData(action, env = process.env) {
23087
24178
  fs24.mkdirSync(dir, { recursive: true });
23088
24179
  switch (action) {
23089
24180
  case "youtrack_json": {
23090
- const out = path36.join(dir, "youtrack.json");
24181
+ const out = path37.join(dir, "youtrack.json");
23091
24182
  fs24.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
23092
24183
  `, "utf8");
23093
24184
  return { action, ok: true, path: out };
23094
24185
  }
23095
24186
  case "youtrack_token_placeholder": {
23096
- const p = path36.join(dir, "youtrack.token");
24187
+ const p = path37.join(dir, "youtrack.token");
23097
24188
  const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
23098
24189
  `, 384) === "preserved";
23099
- const abs = path36.resolve(p);
24190
+ const abs = path37.resolve(p);
23100
24191
  return {
23101
24192
  action,
23102
24193
  ok: true,
@@ -23108,14 +24199,14 @@ function initApplyData(action, env = process.env) {
23108
24199
  };
23109
24200
  }
23110
24201
  case "youtrack_scaffold": {
23111
- const jsonOut = path36.join(dir, "youtrack.json");
23112
- const tokenOut = path36.join(dir, "youtrack.token");
24202
+ const jsonOut = path37.join(dir, "youtrack.json");
24203
+ const tokenOut = path37.join(dir, "youtrack.token");
23113
24204
  fs24.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
23114
24205
  `, "utf8");
23115
24206
  const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
23116
24207
  `, 384) === "preserved";
23117
- const configPath = path36.resolve(jsonOut);
23118
- const tokenPath = path36.resolve(tokenOut);
24208
+ const configPath = path37.resolve(jsonOut);
24209
+ const tokenPath = path37.resolve(tokenOut);
23119
24210
  const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
23120
24211
  process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
23121
24212
  let tokenCreate = {};
@@ -23167,10 +24258,10 @@ function initApplyData(action, env = process.env) {
23167
24258
  }
23168
24259
  }
23169
24260
  case "vcs_scaffold": {
23170
- const jsonOut = path36.join(dir, "vcs.json");
24261
+ const jsonOut = path37.join(dir, "vcs.json");
23171
24262
  fs24.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
23172
24263
  `, "utf8");
23173
- const configPath = path36.resolve(jsonOut);
24264
+ const configPath = path37.resolve(jsonOut);
23174
24265
  const prev = process.env.WORKFLOW_VCS_CONFIG;
23175
24266
  process.env.WORKFLOW_VCS_CONFIG = configPath;
23176
24267
  try {
@@ -23289,7 +24380,7 @@ var executeInitApply = (args, directory, runtime) => {
23289
24380
  protectedNames: args.branch_policy_protected
23290
24381
  }, current);
23291
24382
  writeConfig(next);
23292
- return output(ok({ action: "config", path: path37.join(configDir(), "config.json"), ...next }));
24383
+ return output(ok({ action: "config", path: path38.join(configDir(), "config.json"), ...next }));
23293
24384
  }
23294
24385
  const env = Object.fromEntries(Object.entries({
23295
24386
  WORKFLOW_YT_BASE_URL: args.base_url,
@@ -23306,14 +24397,14 @@ var executeInitApply = (args, directory, runtime) => {
23306
24397
 
23307
24398
  // packages/workit-opencode/src/shared/session.ts
23308
24399
  import { realpathSync as realpathSync7 } from "node:fs";
23309
- import path38 from "node:path";
24400
+ import path39 from "node:path";
23310
24401
  var sameWorkspace = (expected, observed) => {
23311
24402
  if (typeof observed !== "string" || !observed)
23312
24403
  return false;
23313
24404
  try {
23314
24405
  return realpathSync7(expected) === realpathSync7(observed);
23315
24406
  } catch {
23316
- return path38.resolve(expected) === path38.resolve(observed);
24407
+ return path39.resolve(expected) === path39.resolve(observed);
23317
24408
  }
23318
24409
  };
23319
24410
 
@@ -23385,7 +24476,7 @@ var workitFamilyOf = (toolName) => {
23385
24476
 
23386
24477
  // packages/workit-opencode/src/tools/context.ts
23387
24478
  import { realpathSync as realpathSync8 } from "node:fs";
23388
- import path39 from "node:path";
24479
+ import path40 from "node:path";
23389
24480
  var createContextTool = () => ({
23390
24481
  description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
23391
24482
  execute: async (args, context) => {
@@ -23395,7 +24486,7 @@ var createContextTool = () => ({
23395
24486
  let root;
23396
24487
  try {
23397
24488
  const cwd = parsed.data.cwd;
23398
- root = realpathSync8(cwd ? path39.resolve(context.directory, cwd) : context.directory);
24489
+ root = realpathSync8(cwd ? path40.resolve(context.directory, cwd) : context.directory);
23399
24490
  } catch {
23400
24491
  return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
23401
24492
  }
@@ -23427,18 +24518,18 @@ var workerCoordinatorFor = (task, worker) => {
23427
24518
  // packages/workit-core/src/fanout.ts
23428
24519
  import { createHash as createHash17 } from "node:crypto";
23429
24520
  import fs26 from "node:fs";
23430
- import path42 from "node:path";
24521
+ import path43 from "node:path";
23431
24522
 
23432
24523
  // packages/workit-core/src/stack.ts
23433
24524
  import { spawnSync as spawnSync21 } from "node:child_process";
23434
24525
  import { createHash as createHash16, randomBytes as randomBytes4 } from "node:crypto";
23435
24526
  import fs25 from "node:fs";
23436
- import path41 from "node:path";
24527
+ import path42 from "node:path";
23437
24528
 
23438
24529
  // packages/workit-core/src/git/ops.ts
23439
24530
  import { spawnSync as spawnSync20 } from "node:child_process";
23440
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
23441
- import path40 from "node:path";
24531
+ import { existsSync as existsSync13, readFileSync as readFileSync10 } from "node:fs";
24532
+ import path41 from "node:path";
23442
24533
 
23443
24534
  // packages/workit-core/src/forge/redact.ts
23444
24535
  var KEY = String.raw`[A-Za-z0-9_.-]*?(?:token|secret|password|passwd|pwd|api[_-]?key|access[_-]?key|account[_-]?key|private[_-]?key|client[_-]?secret|credentials?|authorization|cookie|signature)[A-Za-z0-9_.-]*`;
@@ -23482,18 +24573,18 @@ function operationInProgress(cwd) {
23482
24573
  if (!run.ok)
23483
24574
  return null;
23484
24575
  const found = run.stdout.split(`
23485
- `).map((line) => path40.resolve(cwd, line.trim()));
24576
+ `).map((line) => path41.resolve(cwd, line.trim()));
23486
24577
  const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
23487
24578
  for (const [index, [, operation]] of markers.entries()) {
23488
24579
  const marker = found[index];
23489
- if (!marker || !existsSync12(marker))
24580
+ if (!marker || !existsSync13(marker))
23490
24581
  continue;
23491
- if (index === 1 && existsSync12(path40.join(marker, "applying")))
24582
+ if (index === 1 && existsSync13(path41.join(marker, "applying")))
23492
24583
  return { operation: "am", commitAllowed: false, branch: null };
23493
- if (index === 0 && existsSync12(path40.join(marker, "amend")) && !conflicted()) {
24584
+ if (index === 0 && existsSync13(path41.join(marker, "amend")) && !conflicted()) {
23494
24585
  let headName = "";
23495
24586
  try {
23496
- headName = readFileSync9(path40.join(marker, "head-name"), "utf8").trim();
24587
+ headName = readFileSync10(path41.join(marker, "head-name"), "utf8").trim();
23497
24588
  } catch {}
23498
24589
  const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
23499
24590
  return { operation, commitAllowed: branch !== null, branch };
@@ -23620,7 +24711,7 @@ function rebaseInProgress(dir) {
23620
24711
  }
23621
24712
  var scratchDir = (cwd) => {
23622
24713
  const dir = stacksDir(cwd);
23623
- return dir.ok ? path41.join(dir.data, "worktrees") : null;
24714
+ return dir.ok ? path42.join(dir.data, "worktrees") : null;
23624
24715
  };
23625
24716
  function gitRestack(cwd, input) {
23626
24717
  const owner = worktreeOf(cwd, input.branch);
@@ -23630,7 +24721,7 @@ function gitRestack(cwd, input) {
23630
24721
  if (!scratch)
23631
24722
  return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
23632
24723
  fs25.mkdirSync(scratch, { recursive: true });
23633
- dir = path41.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
24724
+ dir = path42.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
23634
24725
  const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
23635
24726
  if (!added.ok)
23636
24727
  return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
@@ -23674,7 +24765,7 @@ function stacksDir(cwd) {
23674
24765
  const root = storeRoot(cwd);
23675
24766
  if (!root.ok)
23676
24767
  return stackFail(root.code, root.error, root.unblock);
23677
- return { ok: true, data: path41.join(root.value.root, "stacks") };
24768
+ return { ok: true, data: path42.join(root.value.root, "stacks") };
23678
24769
  }
23679
24770
  var HEARTBEAT_STALE_MS = 2 * 60000;
23680
24771
 
@@ -23785,7 +24876,7 @@ function fanoutsDir(cwd) {
23785
24876
  const root = storeRoot(cwd);
23786
24877
  if (!root.ok)
23787
24878
  return fanoutFail(root.code, root.error, root.unblock);
23788
- return { ok: true, data: path42.join(root.value.root, "fanouts") };
24879
+ return { ok: true, data: path43.join(root.value.root, "fanouts") };
23789
24880
  }
23790
24881
  function parseStored(raw) {
23791
24882
  if (!isRecord7(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
@@ -23853,7 +24944,7 @@ function readFanout(cwd, name) {
23853
24944
  const dir = fanoutsDir(cwd);
23854
24945
  if (!dir.ok)
23855
24946
  return dir;
23856
- const file = path42.join(dir.data, stackFileName(name));
24947
+ const file = path43.join(dir.data, stackFileName(name));
23857
24948
  let parsed = null;
23858
24949
  try {
23859
24950
  parsed = parseStored(JSON.parse(fs26.readFileSync(file, "utf8")));
@@ -23878,7 +24969,7 @@ function listFanouts(cwd) {
23878
24969
  for (const entry of names) {
23879
24970
  let parsed = null;
23880
24971
  try {
23881
- parsed = parseStored(JSON.parse(fs26.readFileSync(path42.join(dir.data, entry), "utf8")));
24972
+ parsed = parseStored(JSON.parse(fs26.readFileSync(path43.join(dir.data, entry), "utf8")));
23882
24973
  } catch {
23883
24974
  parsed = null;
23884
24975
  }
@@ -24548,6 +25639,8 @@ var evaluateShellPermission = (root, event, workdirs = new Map) => {
24548
25639
  return deny(plan.reason);
24549
25640
  }
24550
25641
  }
25642
+ if (event.effect === "ask" && resources.length > 0 && resources.length === event.resources.length && resources.every(isAllowedWorkitCommand))
25643
+ event.effect = "allow";
24551
25644
  };
24552
25645
 
24553
25646
  // packages/workit-opencode/src/v2/child-identity.ts
@@ -24570,7 +25663,7 @@ var childTaskMissing = (store, input) => {
24570
25663
  var childDenial = (identity) => `OpenCode child session has no validated Workit worker: as ${identity} it may only inspect the task summary (workit_task action "inspect") or record evidence or findings (workit_evidence/workit_finding action "record") on an existing task; for anything else the lead assigns it a worker with workit_worker before launching it`;
24571
25664
 
24572
25665
  // packages/workit-opencode/src/v2/shell.ts
24573
- import path43 from "node:path";
25666
+ import path44 from "node:path";
24574
25667
  var commandOf = (input) => {
24575
25668
  if (input === null || typeof input !== "object")
24576
25669
  return null;
@@ -24579,7 +25672,7 @@ var commandOf = (input) => {
24579
25672
  };
24580
25673
  var hookInput = (root, sessionID, id, shell) => ({
24581
25674
  host: "opencode",
24582
- cwd: shell.workdir ? path43.resolve(root, shell.workdir) : root,
25675
+ cwd: shell.workdir ? path44.resolve(root, shell.workdir) : root,
24583
25676
  session: { id: sessionID, agentId: null, agentType: null, parentId: null },
24584
25677
  permissionMode: null,
24585
25678
  transcriptPath: null,
@@ -24785,38 +25878,38 @@ var createStopControl = (root, port) => {
24785
25878
  };
24786
25879
 
24787
25880
  // packages/workit-opencode/src/v2/registry.ts
24788
- import { existsSync as existsSync14, readFileSync as readFileSync10 } from "node:fs";
24789
- import path45 from "node:path";
25881
+ import { existsSync as existsSync15, readFileSync as readFileSync11 } from "node:fs";
25882
+ import path46 from "node:path";
24790
25883
 
24791
25884
  // packages/workit-opencode/src/shared/assets.ts
24792
- import { existsSync as existsSync13, realpathSync as realpathSync9 } from "node:fs";
24793
- import path44 from "node:path";
25885
+ import { existsSync as existsSync14, realpathSync as realpathSync9 } from "node:fs";
25886
+ import path45 from "node:path";
24794
25887
  import { fileURLToPath as fileURLToPath2 } from "node:url";
24795
25888
  var moduleDir = () => {
24796
25889
  try {
24797
- return realpathSync9(path44.dirname(fileURLToPath2(import.meta.url)));
25890
+ return realpathSync9(path45.dirname(fileURLToPath2(import.meta.url)));
24798
25891
  } catch {
24799
- return path44.dirname(fileURLToPath2(import.meta.url));
25892
+ return path45.dirname(fileURLToPath2(import.meta.url));
24800
25893
  }
24801
25894
  };
24802
25895
  var packageRoot2 = () => {
24803
25896
  const dir = moduleDir();
24804
25897
  for (const candidate of [
24805
- path44.resolve(dir, ".."),
24806
- path44.resolve(dir, "..", ".."),
24807
- path44.resolve(dir, "..", "..", "..")
25898
+ path45.resolve(dir, ".."),
25899
+ path45.resolve(dir, "..", ".."),
25900
+ path45.resolve(dir, "..", "..", "..")
24808
25901
  ]) {
24809
- if (existsSync13(path44.join(candidate, "package.json")))
25902
+ if (existsSync14(path45.join(candidate, "package.json")))
24810
25903
  return candidate;
24811
25904
  }
24812
- return path44.resolve(dir, "..");
25905
+ return path45.resolve(dir, "..");
24813
25906
  };
24814
25907
  var skillsRoot = () => {
24815
25908
  const root = packageRoot2();
24816
- const canonical = path44.resolve(root, "..", "workit-core", "skills");
24817
- if (existsSync13(path44.join(root, "src")) && existsSync13(canonical))
25909
+ const canonical = path45.resolve(root, "..", "workit-core", "skills");
25910
+ if (existsSync14(path45.join(root, "src")) && existsSync14(canonical))
24818
25911
  return canonical;
24819
- return path44.join(root, "assets", "skills");
25912
+ return path45.join(root, "assets", "skills");
24820
25913
  };
24821
25914
 
24822
25915
  // packages/workit-opencode/src/v2/registry.ts
@@ -24840,15 +25933,15 @@ var parseFrontmatter = (markdown) => {
24840
25933
  };
24841
25934
  var registerSkills = async (ctx) => {
24842
25935
  const skillsDir = skillsRoot();
24843
- if (!existsSync14(skillsDir))
25936
+ if (!existsSync15(skillsDir))
24844
25937
  return new Set;
24845
25938
  const listed = await ctx.skill.list().catch(() => ({ data: [] }));
24846
- const own = (skill) => typeof skill.path === "string" && path45.resolve(skill.path).startsWith(skillsDir + path45.sep);
25939
+ const own = (skill) => typeof skill.path === "string" && path46.resolve(skill.path).startsWith(skillsDir + path46.sep);
24847
25940
  const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
24848
25941
  const packaged = new Map;
24849
25942
  for (const id of WORKIT_METHOD_SKILLS) {
24850
- const file = path45.join(skillsDir, id, "SKILL.md");
24851
- if (!userSkills.has(id) && existsSync14(file))
25943
+ const file = path46.join(skillsDir, id, "SKILL.md");
25944
+ if (!userSkills.has(id) && existsSync15(file))
24852
25945
  packaged.set(id, file);
24853
25946
  }
24854
25947
  await ctx.skill.transform((editor) => {
@@ -24856,7 +25949,7 @@ var registerSkills = async (ctx) => {
24856
25949
  for (const [id, file] of packaged) {
24857
25950
  if (existing.has(id))
24858
25951
  continue;
24859
- const parsed = parseFrontmatter(readFileSync10(file, "utf8"));
25952
+ const parsed = parseFrontmatter(readFileSync11(file, "utf8"));
24860
25953
  editor.add({
24861
25954
  id,
24862
25955
  name: parsed.name || id,
@@ -24894,25 +25987,25 @@ var registerCommands = async (ctx, registeredSkills) => {
24894
25987
  };
24895
25988
 
24896
25989
  // packages/workit-opencode/src/stale-sources.ts
24897
- import { existsSync as existsSync15, readdirSync as readdirSync6 } from "node:fs";
24898
- import path46 from "node:path";
25990
+ import { existsSync as existsSync16, readdirSync as readdirSync6 } from "node:fs";
25991
+ import path47 from "node:path";
24899
25992
  import { fileURLToPath as fileURLToPath3 } from "node:url";
24900
25993
  var typescriptFiles = (directory) => {
24901
- if (!existsSync15(directory))
25994
+ if (!existsSync16(directory))
24902
25995
  return [];
24903
25996
  return readdirSync6(directory, { withFileTypes: true }).flatMap((entry) => {
24904
- const file = path46.join(directory, entry.name);
25997
+ const file = path47.join(directory, entry.name);
24905
25998
  return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
24906
25999
  });
24907
26000
  };
24908
26001
  var pluginSourceFiles = [
24909
26002
  ...new Set([
24910
26003
  fileURLToPath3(import.meta.url),
24911
- ...typescriptFiles(path46.join(packageRoot2(), "src")),
24912
- path46.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
24913
- ...typescriptFiles(path46.join(packageRoot2(), "..", "workit-core", "src", "core"))
26004
+ ...typescriptFiles(path47.join(packageRoot2(), "src")),
26005
+ path47.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
26006
+ ...typescriptFiles(path47.join(packageRoot2(), "..", "workit-core", "src", "core"))
24914
26007
  ])
24915
- ].filter((file) => existsSync15(file)).toSorted();
26008
+ ].filter((file) => existsSync16(file)).toSorted();
24916
26009
 
24917
26010
  // packages/workit-opencode/src/v2/plugin.ts
24918
26011
  var sessionFacts = async (ctx, sessionID) => {