@brainervirus/workit-codex 8.35.2 → 8.37.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.
@@ -6621,14 +6621,14 @@ var require_dist = __commonJS(function(exports, module) {
6621
6621
  });
6622
6622
 
6623
6623
  // packages/workit-codex/scripts/launch-mcp.ts
6624
- import { existsSync as existsSync11, realpathSync as realpathSync7, statSync as statSync3 } from "node:fs";
6625
- import path26 from "node:path";
6624
+ import { existsSync as existsSync12, realpathSync as realpathSync8, statSync as statSync4 } from "node:fs";
6625
+ import path29 from "node:path";
6626
6626
  import { fileURLToPath as fileURLToPath3 } from "node:url";
6627
6627
 
6628
6628
  // packages/workit-core/src/core.ts
6629
6629
  import { spawnSync as spawnSync14 } from "node:child_process";
6630
6630
  import { existsSync as existsSync8, realpathSync as realpathSync5 } from "node:fs";
6631
- import path22 from "node:path";
6631
+ import path23 from "node:path";
6632
6632
 
6633
6633
  // packages/workit-core/src/core/task-contract.ts
6634
6634
  import { createHash, randomUUID } from "node:crypto";
@@ -19194,9 +19194,9 @@ var workerBlocksTransition = (transition, state) => transition === "pause" ? fal
19194
19194
 
19195
19195
  // packages/workit-core/src/core/task-evaluation.ts
19196
19196
  import { createHash as createHash10 } from "node:crypto";
19197
- import * as fs13 from "node:fs";
19197
+ import * as fs14 from "node:fs";
19198
19198
  import { spawnSync as spawnSync10 } from "node:child_process";
19199
- import path17 from "node:path";
19199
+ import path18 from "node:path";
19200
19200
 
19201
19201
  // packages/workit-core/src/check-config.ts
19202
19202
  import { spawnSync as spawnSync8 } from "node:child_process";
@@ -19430,13 +19430,13 @@ function checkCommand(args, version, onPath = workitAvailable()) {
19430
19430
 
19431
19431
  // packages/workit-core/src/core/policy/derive.ts
19432
19432
  import { existsSync as existsSync5 } from "node:fs";
19433
- import path16 from "node:path";
19433
+ import path17 from "node:path";
19434
19434
 
19435
19435
  // packages/workit-core/src/ledger.ts
19436
19436
  import { spawnSync as spawnSync9 } from "node:child_process";
19437
19437
  import { createHash as createHash9, randomBytes as randomBytes3 } from "node:crypto";
19438
- import fs12 from "node:fs";
19439
- import path15 from "node:path";
19438
+ import fs13 from "node:fs";
19439
+ import path16 from "node:path";
19440
19440
 
19441
19441
  // packages/workit-core/src/core/policy/lane.ts
19442
19442
  import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
@@ -20179,6 +20179,34 @@ function verdictDrift(cwd, input) {
20179
20179
  return stale(lane.reasons[0] ?? `lane ${lane.lane}`, paths, lane.lane === "self");
20180
20180
  }
20181
20181
 
20182
+ // packages/workit-core/src/hooks/standing-signal.ts
20183
+ import fs12 from "node:fs";
20184
+ import path15 from "node:path";
20185
+ var gitStoreDir = (cwd) => {
20186
+ try {
20187
+ for (let dir = path15.resolve(cwd);; dir = path15.dirname(dir)) {
20188
+ const dotGit = path15.join(dir, ".git");
20189
+ if (fs12.existsSync(dotGit)) {
20190
+ let gitDir = dotGit;
20191
+ if (fs12.statSync(dotGit).isFile()) {
20192
+ const target = /^gitdir:\s*(.+)$/mu.exec(fs12.readFileSync(dotGit, "utf8"))?.[1]?.trim();
20193
+ if (!target)
20194
+ return null;
20195
+ gitDir = path15.resolve(dir, target);
20196
+ const common = path15.join(gitDir, "commondir");
20197
+ if (fs12.existsSync(common))
20198
+ gitDir = path15.resolve(gitDir, fs12.readFileSync(common, "utf8").trim());
20199
+ }
20200
+ return path15.join(gitDir, "workit");
20201
+ }
20202
+ if (path15.dirname(dir) === dir)
20203
+ return null;
20204
+ }
20205
+ } catch {
20206
+ return null;
20207
+ }
20208
+ };
20209
+
20182
20210
  // packages/workit-core/src/ledger.ts
20183
20211
  var LEDGER_VERSION = 1;
20184
20212
  var MAX_LINE_BYTES = 4096;
@@ -20280,14 +20308,14 @@ function cleanRebase(cwd, range) {
20280
20308
  return Boolean(tree) && tree === git4(cwd, ["rev-parse", "--verify", "-q", `${range.to}^{tree}`]);
20281
20309
  }
20282
20310
  function storeRoot(cwd) {
20283
- const location = resolveStore(path15.resolve(cwd));
20311
+ const location = resolveStore(path16.resolve(cwd));
20284
20312
  if (location instanceof Error)
20285
20313
  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");
20286
20314
  return { ok: true, value: { root: location.dir, shared: location.shared } };
20287
20315
  }
20288
20316
  function ledgerPath(cwd) {
20289
20317
  const root = storeRoot(cwd);
20290
- return root.ok ? { ok: true, value: path15.join(root.value.root, "ledger", "ledger.jsonl") } : root;
20318
+ return root.ok ? { ok: true, value: path16.join(root.value.root, "ledger", "ledger.jsonl") } : root;
20291
20319
  }
20292
20320
  var newId2 = () => `${Date.now().toString(36)}-${randomBytes3(6).toString("hex")}`;
20293
20321
  var NETWORK_FS_TYPES = new Set([
@@ -20307,7 +20335,7 @@ var onNetworkFs = (dir) => {
20307
20335
  if (process.platform !== "linux")
20308
20336
  return false;
20309
20337
  try {
20310
- return isNetworkFsType(fs12.statfsSync(dir).type);
20338
+ return isNetworkFsType(fs13.statfsSync(dir).type);
20311
20339
  } catch {
20312
20340
  return false;
20313
20341
  }
@@ -20317,15 +20345,15 @@ var LOCK_STALE_MS = 30000;
20317
20345
  var LOCK_OWNER = "owner";
20318
20346
  var lockToken = (lock) => {
20319
20347
  try {
20320
- return fs12.readFileSync(path15.join(lock, LOCK_OWNER), "utf8");
20348
+ return fs13.readFileSync(path16.join(lock, LOCK_OWNER), "utf8");
20321
20349
  } catch {
20322
20350
  return null;
20323
20351
  }
20324
20352
  };
20325
20353
  var lockAgeMs = (lock) => {
20326
- for (const target of [path15.join(lock, LOCK_OWNER), lock]) {
20354
+ for (const target of [path16.join(lock, LOCK_OWNER), lock]) {
20327
20355
  try {
20328
- return Date.now() - fs12.statSync(target).mtimeMs;
20356
+ return Date.now() - fs13.statSync(target).mtimeMs;
20329
20357
  } catch {}
20330
20358
  }
20331
20359
  return null;
@@ -20333,14 +20361,14 @@ var lockAgeMs = (lock) => {
20333
20361
  var ledgerLock = {
20334
20362
  acquire(lock) {
20335
20363
  try {
20336
- fs12.mkdirSync(lock);
20364
+ fs13.mkdirSync(lock);
20337
20365
  } catch (error) {
20338
20366
  if (error.code === "EEXIST")
20339
20367
  return null;
20340
20368
  throw error;
20341
20369
  }
20342
20370
  const token = `${process.pid}-${randomBytes3(8).toString("hex")}`;
20343
- fs12.writeFileSync(path15.join(lock, LOCK_OWNER), token);
20371
+ fs13.writeFileSync(path16.join(lock, LOCK_OWNER), token);
20344
20372
  return token;
20345
20373
  },
20346
20374
  release(lock, token) {
@@ -20348,35 +20376,35 @@ var ledgerLock = {
20348
20376
  return false;
20349
20377
  const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
20350
20378
  try {
20351
- fs12.renameSync(lock, tomb);
20379
+ fs13.renameSync(lock, tomb);
20352
20380
  } catch {
20353
20381
  return false;
20354
20382
  }
20355
20383
  if (lockToken(tomb) !== token) {
20356
20384
  try {
20357
- fs12.renameSync(tomb, lock);
20385
+ fs13.renameSync(tomb, lock);
20358
20386
  } catch {}
20359
20387
  return false;
20360
20388
  }
20361
- fs12.rmSync(tomb, { recursive: true, force: true });
20389
+ fs13.rmSync(tomb, { recursive: true, force: true });
20362
20390
  return true;
20363
20391
  },
20364
20392
  token: lockToken,
20365
20393
  reap(lock, observed) {
20366
20394
  const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
20367
20395
  try {
20368
- fs12.renameSync(lock, tomb);
20396
+ fs13.renameSync(lock, tomb);
20369
20397
  } catch {
20370
20398
  return false;
20371
20399
  }
20372
20400
  if (lockToken(tomb) === observed) {
20373
- fs12.rmSync(tomb, { recursive: true, force: true });
20401
+ fs13.rmSync(tomb, { recursive: true, force: true });
20374
20402
  return true;
20375
20403
  }
20376
20404
  try {
20377
- fs12.renameSync(tomb, lock);
20405
+ fs13.renameSync(tomb, lock);
20378
20406
  } catch {
20379
- fs12.rmSync(tomb, { recursive: true, force: true });
20407
+ fs13.rmSync(tomb, { recursive: true, force: true });
20380
20408
  }
20381
20409
  return false;
20382
20410
  }
@@ -20410,7 +20438,7 @@ var TAIL_BYTES = 64 * 1024;
20410
20438
  var lastLine = (fd, size) => {
20411
20439
  for (let span = Math.min(size, TAIL_BYTES);span > 0; span = Math.min(size, span * 2)) {
20412
20440
  const buffer = Buffer.alloc(span);
20413
- fs12.readSync(fd, buffer, 0, span, size - span);
20441
+ fs13.readSync(fd, buffer, 0, span, size - span);
20414
20442
  const end = buffer.lastIndexOf(10);
20415
20443
  if (end < 0) {
20416
20444
  if (span === size)
@@ -20441,21 +20469,21 @@ var lastLine = (fd, size) => {
20441
20469
  };
20442
20470
  var tighten = (fd) => {
20443
20471
  try {
20444
- if ((fs12.fstatSync(fd).mode & 63) !== 0)
20445
- fs12.fchmodSync(fd, 384);
20472
+ if ((fs13.fstatSync(fd).mode & 63) !== 0)
20473
+ fs13.fchmodSync(fd, 384);
20446
20474
  } catch {}
20447
20475
  };
20448
20476
  function appendLine(file, build, options) {
20449
20477
  const write = () => {
20450
- const fd = fs12.openSync(file, "a+", 384);
20478
+ const fd = fs13.openSync(file, "a+", 384);
20451
20479
  try {
20452
20480
  if (process.platform !== "win32")
20453
20481
  tighten(fd);
20454
- const size = fs12.fstatSync(fd).size;
20482
+ const size = fs13.fstatSync(fd).size;
20455
20483
  let fence = "";
20456
20484
  if (size > 0) {
20457
20485
  const last = Buffer.alloc(1);
20458
- fs12.readSync(fd, last, 0, 1, size - 1);
20486
+ fs13.readSync(fd, last, 0, 1, size - 1);
20459
20487
  if (last[0] !== 10)
20460
20488
  fence = `
20461
20489
  `;
@@ -20464,17 +20492,17 @@ function appendLine(file, build, options) {
20464
20492
  if (!line.ok)
20465
20493
  return line;
20466
20494
  const bytes = Buffer.from(fence + line.value);
20467
- const written = fs12.writeSync(fd, bytes);
20495
+ const written = fs13.writeSync(fd, bytes);
20468
20496
  if (written !== bytes.length)
20469
20497
  return err("unavailable", `short write to ${file}: ${written} of ${bytes.length} bytes; the row is torn and will be skipped`);
20470
20498
  return { ok: true, value: undefined };
20471
20499
  } finally {
20472
- fs12.closeSync(fd);
20500
+ fs13.closeSync(fd);
20473
20501
  }
20474
20502
  };
20475
20503
  try {
20476
- fs12.mkdirSync(path15.dirname(file), { recursive: true });
20477
- if (!options.forceLock && !onNetworkFs(path15.dirname(file)))
20504
+ fs13.mkdirSync(path16.dirname(file), { recursive: true });
20505
+ if (!options.forceLock && !onNetworkFs(path16.dirname(file)))
20478
20506
  return write();
20479
20507
  const locked = withLedgerLock(file, write);
20480
20508
  return locked ?? err("busy", `ledger lock ${file}.lock is held`, "retry; remove the .lock directory if no workit process is running");
@@ -20544,8 +20572,8 @@ function readLedger(cwd, options = {}) {
20544
20572
  let text = "";
20545
20573
  let stat = null;
20546
20574
  try {
20547
- stat = fs12.statSync(file.value);
20548
- text = fs12.readFileSync(file.value, "utf8");
20575
+ stat = fs13.statSync(file.value);
20576
+ text = fs13.readFileSync(file.value, "utf8");
20549
20577
  } catch (error) {
20550
20578
  if (error.code === "ENOENT")
20551
20579
  return {
@@ -20685,7 +20713,7 @@ function ledgerTampering(cwd) {
20685
20713
  if (!file.ok)
20686
20714
  return [];
20687
20715
  try {
20688
- const stat = fs12.statSync(file.value);
20716
+ const stat = fs13.statSync(file.value);
20689
20717
  const cached = tamperCache.get(file.value);
20690
20718
  if (cached && cached.size === stat.size && cached.stamp === fileStamp(stat))
20691
20719
  return cached.breaks;
@@ -21267,7 +21295,7 @@ var productDecision = (task, ledger) => {
21267
21295
  };
21268
21296
  };
21269
21297
  var planRecorded = (task, root) => {
21270
- const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(path16.resolve(root, ref.path)));
21298
+ const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync5(path17.resolve(root, ref.path)));
21271
21299
  if (cited)
21272
21300
  return {
21273
21301
  met: true,
@@ -21334,14 +21362,14 @@ function derivedRuleStatus(task, requirement, root, ledger) {
21334
21362
 
21335
21363
  // packages/workit-core/src/core/task-evaluation.ts
21336
21364
  var digestBytes = (value) => createHash10("sha256").update(value).digest("hex");
21337
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path17.sep}`);
21338
- var relative = (root, candidate) => path17.relative(root, candidate).split(path17.sep).join("/") || ".";
21365
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path18.sep}`);
21366
+ var relative = (root, candidate) => path18.relative(root, candidate).split(path18.sep).join("/") || ".";
21339
21367
  var canonicalPath = (value) => {
21340
21368
  if (typeof value !== "string" || !value)
21341
21369
  return null;
21342
21370
  if (value.includes("\\") || value.split("/").includes(".."))
21343
21371
  return null;
21344
- const normalized = path17.posix.normalize(value);
21372
+ const normalized = path18.posix.normalize(value);
21345
21373
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
21346
21374
  return null;
21347
21375
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -21389,21 +21417,21 @@ var gitPaths = (root) => {
21389
21417
  git: !/not a git repository/i.test(stderr)
21390
21418
  };
21391
21419
  }
21392
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")) : [];
21420
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")) : [];
21393
21421
  const stagedDeleted = spawnSync10("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
21394
21422
  if (stagedDeleted.status !== 0) {
21395
21423
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
21396
21424
  if (!/not a git repository/i.test(stderr))
21397
21425
  return { paths, uncertain: true, git: true };
21398
21426
  } else if (stagedDeleted.stdout) {
21399
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path17.sep).join("/")));
21427
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path18.sep).join("/")));
21400
21428
  }
21401
21429
  return { paths: [...new Set(paths)], uncertain: false, git: true };
21402
21430
  };
21403
21431
  var walk = (root, directory, output) => {
21404
21432
  let names;
21405
21433
  try {
21406
- names = fs13.readdirSync(directory);
21434
+ names = fs14.readdirSync(directory);
21407
21435
  } catch {
21408
21436
  return true;
21409
21437
  }
@@ -21411,10 +21439,10 @@ var walk = (root, directory, output) => {
21411
21439
  for (const name of names) {
21412
21440
  if (name === ".git" || name === ".workit")
21413
21441
  continue;
21414
- const target = path17.join(directory, name);
21442
+ const target = path18.join(directory, name);
21415
21443
  let stat;
21416
21444
  try {
21417
- stat = fs13.lstatSync(target);
21445
+ stat = fs14.lstatSync(target);
21418
21446
  } catch {
21419
21447
  uncertain = true;
21420
21448
  continue;
@@ -21429,14 +21457,14 @@ var walk = (root, directory, output) => {
21429
21457
  var scopeRoots = (root, scope) => {
21430
21458
  const roots = [];
21431
21459
  for (const item of scope.paths.length ? scope.paths : ["."]) {
21432
- const target = path17.resolve(root, item);
21460
+ const target = path18.resolve(root, item);
21433
21461
  if (!inside(root, target))
21434
21462
  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` }] });
21435
21463
  let ancestor = target;
21436
- while (!fs13.existsSync(ancestor) && ancestor !== root)
21437
- ancestor = path17.dirname(ancestor);
21464
+ while (!fs14.existsSync(ancestor) && ancestor !== root)
21465
+ ancestor = path18.dirname(ancestor);
21438
21466
  try {
21439
- if (!inside(root, fs13.realpathSync(ancestor)))
21467
+ if (!inside(root, fs14.realpathSync(ancestor)))
21440
21468
  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` }] });
21441
21469
  } catch {
21442
21470
  return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
@@ -21448,7 +21476,7 @@ var scopeRoots = (root, scope) => {
21448
21476
  function captureCandidate(root, scope, environment = []) {
21449
21477
  let checkout;
21450
21478
  try {
21451
- checkout = fs13.realpathSync(root);
21479
+ checkout = fs14.realpathSync(root);
21452
21480
  } catch {
21453
21481
  return failure2("invalid_input", "candidate root does not exist");
21454
21482
  }
@@ -21466,11 +21494,11 @@ function captureCandidate(root, scope, environment = []) {
21466
21494
  if (item === ".git" || item === ".workit")
21467
21495
  continue;
21468
21496
  names.add(item);
21469
- if (!fs13.existsSync(target))
21497
+ if (!fs14.existsSync(target))
21470
21498
  continue;
21471
21499
  let stat;
21472
21500
  try {
21473
- stat = fs13.lstatSync(target);
21501
+ stat = fs14.lstatSync(target);
21474
21502
  } catch {
21475
21503
  uncertain = true;
21476
21504
  continue;
@@ -21486,17 +21514,17 @@ function captureCandidate(root, scope, environment = []) {
21486
21514
  for (const item of [...names].toSorted()) {
21487
21515
  if (!scopeMatches(item, normalizedScope))
21488
21516
  continue;
21489
- const target = path17.join(checkout, item);
21517
+ const target = path18.join(checkout, item);
21490
21518
  try {
21491
- const stat = fs13.lstatSync(target);
21519
+ const stat = fs14.lstatSync(target);
21492
21520
  if (stat.isSymbolicLink()) {
21493
- const link = fs13.readlinkSync(target);
21521
+ const link = fs14.readlinkSync(target);
21494
21522
  files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
21495
21523
  } else if (stat.isFile()) {
21496
21524
  files.push({
21497
21525
  path: item,
21498
21526
  kind: "file",
21499
- digest: digestBytes(fs13.readFileSync(target)),
21527
+ digest: digestBytes(fs14.readFileSync(target)),
21500
21528
  executable: (stat.mode & 73) !== 0
21501
21529
  });
21502
21530
  } else if (!stat.isDirectory()) {
@@ -21661,7 +21689,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
21661
21689
  var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
21662
21690
  let root;
21663
21691
  try {
21664
- root = fs13.realpathSync(checkoutRoot);
21692
+ root = fs14.realpathSync(checkoutRoot);
21665
21693
  } catch {
21666
21694
  return failure2("permission_denied", "checkout root is unavailable");
21667
21695
  }
@@ -21670,23 +21698,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
21670
21698
  continue;
21671
21699
  if (!reference.digest)
21672
21700
  return failure2("invalid_input", "document references require a byte digest");
21673
- const target = path17.resolve(root, reference.path);
21674
- if (target !== root && !target.startsWith(`${root}${path17.sep}`))
21701
+ const target = path18.resolve(root, reference.path);
21702
+ if (target !== root && !target.startsWith(`${root}${path18.sep}`))
21675
21703
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
21676
21704
  fields: [
21677
21705
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
21678
21706
  ]
21679
21707
  });
21680
21708
  try {
21681
- const below = path17.relative(root, target);
21709
+ const below = path18.relative(root, target);
21682
21710
  let current = root;
21683
- for (const segment of below.split(path17.sep)) {
21711
+ for (const segment of below.split(path18.sep)) {
21684
21712
  if (!segment)
21685
21713
  continue;
21686
- current = path17.join(current, segment);
21687
- const stat = fs13.lstatSync(current);
21688
- const real = fs13.realpathSync(current);
21689
- if (real !== root && !real.startsWith(`${root}${path17.sep}`))
21714
+ current = path18.join(current, segment);
21715
+ const stat = fs14.lstatSync(current);
21716
+ const real = fs14.realpathSync(current);
21717
+ if (real !== root && !real.startsWith(`${root}${path18.sep}`))
21690
21718
  return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
21691
21719
  fields: [
21692
21720
  {
@@ -21702,7 +21730,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
21702
21730
  if (current !== target && !stat.isDirectory())
21703
21731
  return failure2("invalid_input", "document reference ancestor is not a directory");
21704
21732
  }
21705
- if (digestBytes(fs13.readFileSync(target)) !== reference.digest)
21733
+ if (digestBytes(fs14.readFileSync(target)) !== reference.digest)
21706
21734
  return failure2("permission_denied", "decision document bytes have changed");
21707
21735
  } catch {
21708
21736
  return failure2("invalid_input", "decision document is unavailable");
@@ -22546,7 +22574,7 @@ var judgeInputSummary = (raw) => {
22546
22574
 
22547
22575
  // packages/workit-core/src/autonomy.ts
22548
22576
  import os4 from "node:os";
22549
- import path18 from "node:path";
22577
+ import path19 from "node:path";
22550
22578
  var accountHome = () => {
22551
22579
  try {
22552
22580
  return os4.userInfo().homedir;
@@ -22555,10 +22583,10 @@ var accountHome = () => {
22555
22583
  }
22556
22584
  };
22557
22585
  var grantsHome = { resolve: accountHome };
22558
- var grantsDir = () => path18.join(grantsHome.resolve(), ".config", "workit");
22586
+ var grantsDir = () => path19.join(grantsHome.resolve(), ".config", "workit");
22559
22587
  var grantsOverride = () => {
22560
- const configured = path18.resolve(resolveConfigDir());
22561
- return configured === path18.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 ${path18.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
22588
+ const configured = path19.resolve(resolveConfigDir());
22589
+ return configured === path19.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 ${path19.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
22562
22590
  };
22563
22591
  var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
22564
22592
  var DEFAULT_GRANTS = Object.freeze({
@@ -24369,7 +24397,7 @@ class WorkitCore {
24369
24397
  // packages/workit-core/src/core/repo-context.ts
24370
24398
  import { spawnSync as spawnSync11 } from "node:child_process";
24371
24399
  import { existsSync as existsSync7, readFileSync as readFileSync6, readdirSync as readdirSync4 } from "node:fs";
24372
- import path19 from "node:path";
24400
+ import path20 from "node:path";
24373
24401
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
24374
24402
  try {
24375
24403
  gitRevisionParts(value);
@@ -24503,7 +24531,7 @@ var findPrTemplate = (cwd) => {
24503
24531
  ]) {
24504
24532
  const parts = rel.split("/");
24505
24533
  const base = parts[parts.length - 1];
24506
- const dir = parts.length > 1 ? path19.join(cwd, ...parts.slice(0, -1)) : cwd;
24534
+ const dir = parts.length > 1 ? path20.join(cwd, ...parts.slice(0, -1)) : cwd;
24507
24535
  const actual = probeCaseInsensitive(dir, base);
24508
24536
  if (actual)
24509
24537
  return [...parts.slice(0, -1), actual].join("/");
@@ -24534,7 +24562,7 @@ var diffStatForRange = (cwd, range) => {
24534
24562
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
24535
24563
  };
24536
24564
  var packageScripts = (cwd, keys) => {
24537
- const pkgPath = path19.join(cwd, "package.json");
24565
+ const pkgPath = path20.join(cwd, "package.json");
24538
24566
  if (!existsSync7(pkgPath))
24539
24567
  return [];
24540
24568
  try {
@@ -24560,9 +24588,9 @@ var documentationFiles = (cwd) => {
24560
24588
  if (entry.isDirectory()) {
24561
24589
  if (excluded.has(entry.name))
24562
24590
  continue;
24563
- walk(path19.join(dir, entry.name), depth + 1);
24591
+ walk(path20.join(dir, entry.name), depth + 1);
24564
24592
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
24565
- matches.push("./" + path19.relative(cwd, path19.join(dir, entry.name)).replaceAll("\\", "/"));
24593
+ matches.push("./" + path20.relative(cwd, path20.join(dir, entry.name)).replaceAll("\\", "/"));
24566
24594
  }
24567
24595
  }
24568
24596
  };
@@ -24578,7 +24606,7 @@ var readTrimmed2 = (file, maxLines) => {
24578
24606
  `);
24579
24607
  };
24580
24608
  function prReadyContext(root, range) {
24581
- const cwd = path19.resolve(repoRoot(root));
24609
+ const cwd = path20.resolve(repoRoot(root));
24582
24610
  if (range !== undefined && !isResolvableContextRange(cwd, range))
24583
24611
  return invalidRange(cwd);
24584
24612
  let stdout = "";
@@ -24636,7 +24664,7 @@ ${status.stderr}` : "");
24636
24664
  stdout += `template_path: ${template}
24637
24665
 
24638
24666
  `;
24639
- stdout += readTrimmed2(path19.join(cwd, template), 220);
24667
+ stdout += readTrimmed2(path20.join(cwd, template), 220);
24640
24668
  } else {
24641
24669
  stdout += `template_path: none
24642
24670
 
@@ -24646,7 +24674,7 @@ ${status.stderr}` : "");
24646
24674
  stdout += `
24647
24675
  `;
24648
24676
  stdout += printSection("Recent Validation Signals");
24649
- if (existsSync7(path19.join(cwd, "package.json"))) {
24677
+ if (existsSync7(path20.join(cwd, "package.json"))) {
24650
24678
  stdout += `package.json detected. Common scripts:
24651
24679
  `;
24652
24680
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -24655,7 +24683,7 @@ ${status.stderr}` : "");
24655
24683
  `) + `
24656
24684
  `;
24657
24685
  }
24658
- if (existsSync7(path19.join(cwd, "Cargo.toml")) || existsSync7(path19.join(cwd, "src-tauri/Cargo.toml"))) {
24686
+ if (existsSync7(path20.join(cwd, "Cargo.toml")) || existsSync7(path20.join(cwd, "src-tauri/Cargo.toml"))) {
24659
24687
  stdout += `Rust project detected.
24660
24688
  `;
24661
24689
  }
@@ -24688,7 +24716,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
24688
24716
  - Apply with the host-native editor, preserving the repository changelog conventions.
24689
24717
  - If Unreleased already has duplicate category headings, normalize_only first.`;
24690
24718
  function changelogContext(root, range) {
24691
- const cwd = path19.resolve(repoRoot(root));
24719
+ const cwd = path20.resolve(repoRoot(root));
24692
24720
  if (range !== undefined && !isResolvableContextRange(cwd, range))
24693
24721
  return invalidRange(cwd);
24694
24722
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -24706,7 +24734,7 @@ function changelogContext(root, range) {
24706
24734
  stdout += CHANGELOG_RULES + `
24707
24735
  `;
24708
24736
  stdout += printSection("Existing CHANGELOG.md");
24709
- const changelogPath = path19.join(cwd, "CHANGELOG.md");
24737
+ const changelogPath = path20.join(cwd, "CHANGELOG.md");
24710
24738
  if (existsSync7(changelogPath)) {
24711
24739
  stdout += readTrimmed2(changelogPath, 260) + `
24712
24740
  `;
@@ -24726,7 +24754,7 @@ function changelogContext(root, range) {
24726
24754
  return { stdout, stderr: "", exitCode: 0, cwd };
24727
24755
  }
24728
24756
  function docsRefreshContext(root, range) {
24729
- const cwd = path19.resolve(repoRoot(root));
24757
+ const cwd = path20.resolve(repoRoot(root));
24730
24758
  if (range !== undefined && !isResolvableContextRange(cwd, range))
24731
24759
  return invalidRange(cwd);
24732
24760
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -24748,7 +24776,7 @@ function docsRefreshContext(root, range) {
24748
24776
  `) + `
24749
24777
  `;
24750
24778
  stdout += printSection("README Preview");
24751
- const readme = path19.join(cwd, "README.md");
24779
+ const readme = path20.join(cwd, "README.md");
24752
24780
  if (existsSync7(readme)) {
24753
24781
  stdout += readTrimmed2(readme, 220) + `
24754
24782
  `;
@@ -24757,7 +24785,7 @@ function docsRefreshContext(root, range) {
24757
24785
  `;
24758
24786
  }
24759
24787
  stdout += printSection("Package Scripts");
24760
- const pkgPath = path19.join(cwd, "package.json");
24788
+ const pkgPath = path20.join(cwd, "package.json");
24761
24789
  if (existsSync7(pkgPath)) {
24762
24790
  try {
24763
24791
  const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
@@ -24771,7 +24799,7 @@ function docsRefreshContext(root, range) {
24771
24799
  return { stdout, stderr: "", exitCode: 0, cwd };
24772
24800
  }
24773
24801
  function releaseNotesContext(root, rangeOrTag) {
24774
- const cwd = path19.resolve(repoRoot(root));
24802
+ const cwd = path20.resolve(repoRoot(root));
24775
24803
  if (!rangeOrTag) {
24776
24804
  return { stdout: "", stderr: `ERROR: release tag or range required
24777
24805
  `, exitCode: 1, cwd };
@@ -24809,7 +24837,7 @@ function releaseNotesContext(root, rangeOrTag) {
24809
24837
  `;
24810
24838
  stdout += printSection("Existing Release Files");
24811
24839
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
24812
- if (existsSync7(path19.join(cwd, rel)))
24840
+ if (existsSync7(path20.join(cwd, rel)))
24813
24841
  stdout += rel + `
24814
24842
  `;
24815
24843
  }
@@ -24840,8 +24868,8 @@ function releaseNotesContext(root, rangeOrTag) {
24840
24868
  import { spawnSync as spawnSync13 } from "node:child_process";
24841
24869
 
24842
24870
  // packages/workit-core/src/core/pr-create.ts
24843
- import fs14 from "node:fs";
24844
- import path20 from "node:path";
24871
+ import fs15 from "node:fs";
24872
+ import path21 from "node:path";
24845
24873
  import { spawnSync as spawnSync12 } from "node:child_process";
24846
24874
  // packages/workit-core/src/forge/pr-body.ts
24847
24875
  function parseGhIssue(value) {
@@ -24852,13 +24880,13 @@ function parseGhIssue(value) {
24852
24880
  // packages/workit-core/src/core/pr-create.ts
24853
24881
  function whichOnPath(tool) {
24854
24882
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
24855
- for (const dir of (process.env.PATH ?? "").split(path20.delimiter)) {
24883
+ for (const dir of (process.env.PATH ?? "").split(path21.delimiter)) {
24856
24884
  if (!dir)
24857
24885
  continue;
24858
24886
  for (const name of names) {
24859
- const candidate = path20.join(dir, name);
24887
+ const candidate = path21.join(dir, name);
24860
24888
  try {
24861
- fs14.accessSync(candidate, fs14.constants.X_OK);
24889
+ fs15.accessSync(candidate, fs15.constants.X_OK);
24862
24890
  return candidate;
24863
24891
  } catch {}
24864
24892
  }
@@ -24976,8 +25004,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
24976
25004
  };
24977
25005
 
24978
25006
  // packages/workit-core/src/core/youtrack.ts
24979
- import fs15 from "node:fs";
24980
- import path21 from "node:path";
25007
+ import fs16 from "node:fs";
25008
+ import path22 from "node:path";
24981
25009
 
24982
25010
  // packages/workit-core/src/core/templates.ts
24983
25011
  var repoRoot2 = assetRoot();
@@ -24990,22 +25018,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
24990
25018
  // packages/workit-core/src/core/youtrack.ts
24991
25019
  var ISSUE_RE = /^[A-Z]+-\d+$/;
24992
25020
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
24993
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path21.join(configDir(), "youtrack.json");
24994
- var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path21.join(resolveConfigDir(), "youtrack.json");
25021
+ var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path22.join(configDir(), "youtrack.json");
25022
+ var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path22.join(resolveConfigDir(), "youtrack.json");
24995
25023
  var youTrackTokenModeOk = (p) => {
24996
25024
  if (process.platform === "win32")
24997
25025
  return true;
24998
- const mode = fs15.statSync(p).mode & 511;
25026
+ const mode = fs16.statSync(p).mode & 511;
24999
25027
  return mode === 384;
25000
25028
  };
25001
25029
  function readYouTrackConfig(required) {
25002
25030
  const cfgPath = youTrackConfigPath();
25003
- if (!fs15.existsSync(cfgPath)) {
25031
+ if (!fs16.existsSync(cfgPath)) {
25004
25032
  return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
25005
25033
  }
25006
25034
  let parsed;
25007
25035
  try {
25008
- parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
25036
+ parsed = JSON.parse(fs16.readFileSync(cfgPath, "utf8"));
25009
25037
  } catch {
25010
25038
  return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
25011
25039
  }
@@ -25016,11 +25044,11 @@ function readYouTrackConfig(required) {
25016
25044
  }
25017
25045
  var readYouTrackContextConfig = () => {
25018
25046
  const cfgPath = youTrackReadOnlyConfigPath();
25019
- if (!fs15.existsSync(cfgPath))
25047
+ if (!fs16.existsSync(cfgPath))
25020
25048
  return { data: {} };
25021
25049
  let parsed;
25022
25050
  try {
25023
- parsed = JSON.parse(fs15.readFileSync(cfgPath, "utf8"));
25051
+ parsed = JSON.parse(fs16.readFileSync(cfgPath, "utf8"));
25024
25052
  } catch {
25025
25053
  return { error: "YouTrack configuration is unavailable" };
25026
25054
  }
@@ -25059,12 +25087,12 @@ var youTrackToken = () => {
25059
25087
  if ("error" in loaded)
25060
25088
  return loaded;
25061
25089
  const tokenFile = String(loaded.config.tokenFile ?? "");
25062
- const tokenPath = tokenFile ? path21.isAbsolute(tokenFile) ? path21.resolve(tokenFile) : path21.resolve(process.cwd(), tokenFile) : "";
25063
- if (!tokenPath || !fs15.existsSync(tokenPath))
25090
+ const tokenPath = tokenFile ? path22.isAbsolute(tokenFile) ? path22.resolve(tokenFile) : path22.resolve(process.cwd(), tokenFile) : "";
25091
+ if (!tokenPath || !fs16.existsSync(tokenPath))
25064
25092
  return { error: "missing youtrack.token" };
25065
25093
  if (!youTrackTokenModeOk(tokenPath))
25066
25094
  return { error: "youtrack.token mode must be 0600" };
25067
- const token = fs15.readFileSync(tokenPath, "utf8").trim();
25095
+ const token = fs16.readFileSync(tokenPath, "utf8").trim();
25068
25096
  if (!token)
25069
25097
  return { error: "empty token file" };
25070
25098
  if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
@@ -25129,9 +25157,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
25129
25157
  } catch {
25130
25158
  continue;
25131
25159
  }
25132
- if (!fs15.existsSync(full))
25160
+ if (!fs16.existsSync(full))
25133
25161
  continue;
25134
- const text = fs15.readFileSync(full, "utf8");
25162
+ const text = fs16.readFileSync(full, "utf8");
25135
25163
  const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
25136
25164
  if (m)
25137
25165
  return m[1];
@@ -25401,15 +25429,15 @@ function resolveGitRevision(root, value) {
25401
25429
  }
25402
25430
  function resolveInside(root, candidate) {
25403
25431
  const base = realpathSync5(root);
25404
- const target = path22.resolve(base, candidate);
25405
- if (target !== base && !target.startsWith(base + path22.sep)) {
25432
+ const target = path23.resolve(base, candidate);
25433
+ if (target !== base && !target.startsWith(base + path23.sep)) {
25406
25434
  throw new Error("path must stay inside repository root");
25407
25435
  }
25408
25436
  let ancestor = target;
25409
25437
  while (!existsSync8(ancestor))
25410
- ancestor = path22.dirname(ancestor);
25438
+ ancestor = path23.dirname(ancestor);
25411
25439
  const canonicalAncestor = realpathSync5(ancestor);
25412
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path22.sep)) {
25440
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path23.sep)) {
25413
25441
  throw new Error("path must stay inside repository root");
25414
25442
  }
25415
25443
  return target;
@@ -25441,7 +25469,7 @@ var SUPPORT_MATRIX = {
25441
25469
 
25442
25470
  // packages/workit-mcp/src/server.ts
25443
25471
  import { existsSync as existsSync9, readFileSync as readFileSync7, realpathSync as realpathSync6 } from "node:fs";
25444
- import path23 from "node:path";
25472
+ import path24 from "node:path";
25445
25473
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
25446
25474
  function isZ4Schema(s) {
25447
25475
  const schema = s;
@@ -27889,13 +27917,13 @@ var rootVariants = (workspaceRoot) => {
27889
27917
  if (!workspaceRoot)
27890
27918
  return [];
27891
27919
  const roots = new Set;
27892
- for (const candidate of [workspaceRoot, path23.resolve(workspaceRoot)]) {
27893
- if (path23.isAbsolute(candidate) && candidate !== path23.parse(candidate).root)
27920
+ for (const candidate of [workspaceRoot, path24.resolve(workspaceRoot)]) {
27921
+ if (path24.isAbsolute(candidate) && candidate !== path24.parse(candidate).root)
27894
27922
  roots.add(candidate);
27895
27923
  }
27896
27924
  try {
27897
27925
  const canonical = realpathSync6(workspaceRoot);
27898
- if (canonical !== path23.parse(canonical).root)
27926
+ if (canonical !== path24.parse(canonical).root)
27899
27927
  roots.add(canonical);
27900
27928
  } catch {}
27901
27929
  return [...roots].toSorted((left, right) => right.length - left.length);
@@ -27977,11 +28005,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
27977
28005
  };
27978
28006
  function createMcpServer(host, contextProvider) {
27979
28007
  assertMcpHost(host);
27980
- const here = path23.dirname(fileURLToPath2(import.meta.url));
28008
+ const here = path24.dirname(fileURLToPath2(import.meta.url));
27981
28009
  const sourceMarker = markSourcesLoaded([
27982
- path23.join(here, "server.ts"),
27983
- path23.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
27984
- path23.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
28010
+ path24.join(here, "server.ts"),
28011
+ path24.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
28012
+ path24.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
27985
28013
  ].filter((file) => existsSync9(file)));
27986
28014
  let staleWarned = false;
27987
28015
  const failures = new Map;
@@ -28320,7 +28348,7 @@ var EXEC = new Set(["exec", "x", "dlx"]);
28320
28348
  // packages/workit-core/src/core/heavy-words.ts
28321
28349
  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(" "));
28322
28350
  // packages/workit-core/src/hooks/write-gate.ts
28323
- import path24 from "node:path";
28351
+ import path25 from "node:path";
28324
28352
 
28325
28353
  // packages/workit-core/src/core/live-session.ts
28326
28354
  import { spawnSync as spawnSync15 } from "node:child_process";
@@ -28361,7 +28389,7 @@ var MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
28361
28389
  var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
28362
28390
  var PUSHD2 = new Set(["pushd", "push-location"]);
28363
28391
  var POPD2 = new Set(["popd", "pop-location"]);
28364
- var UNKNOWN_DIR = path24.resolve(path24.parse(process.cwd()).root, ".workit-unknown-dir");
28392
+ var UNKNOWN_DIR = path25.resolve(path25.parse(process.cwd()).root, ".workit-unknown-dir");
28365
28393
  // packages/workit-core/src/core/skill-manifests.ts
28366
28394
  if (false) {}
28367
28395
 
@@ -28381,7 +28409,42 @@ var READERS = new Set([
28381
28409
  "type"
28382
28410
  ]);
28383
28411
  // packages/workit-core/src/hooks/hosts/fields.ts
28412
+ import { existsSync as existsSync10, realpathSync as realpathSync7, statSync as statSync3 } from "node:fs";
28413
+ import path26 from "node:path";
28414
+ var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
28415
+ var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
28416
+ var optionalText = (value) => typeof value === "string" ? value : null;
28417
+ var existingDirectory = (value) => {
28418
+ if (!nonEmpty2(value) || !path26.isAbsolute(value) || !existsSync10(value))
28419
+ return null;
28420
+ try {
28421
+ return statSync3(value).isDirectory() ? realpathSync7(value) : null;
28422
+ } catch {
28423
+ return null;
28424
+ }
28425
+ };
28384
28426
  var SHELLS2 = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
28427
+ var SCRIPT_FLAG = /^(?:-\w*c|-command|\/c)$/i;
28428
+ var quoteArg = (arg) => /^[\w@%+=:,./-]+$/.test(arg) ? arg : JSON.stringify(arg);
28429
+ var commandText = (value) => {
28430
+ if (nonEmpty2(value))
28431
+ return value;
28432
+ if (!Array.isArray(value) || value.length === 0)
28433
+ return null;
28434
+ const argv = value;
28435
+ if (!argv.every((arg) => typeof arg === "string"))
28436
+ return null;
28437
+ const shell = argv[0].split(/[\\/]/).at(-1)?.replace(/\.exe$/i, "").toLowerCase() ?? "";
28438
+ if (SHELLS2.has(shell))
28439
+ for (let index = 1;index < argv.length - 1; index++) {
28440
+ if (SCRIPT_FLAG.test(argv[index]))
28441
+ return nonEmpty2(argv[index + 1]) ? argv[index + 1] : null;
28442
+ if (!/^[-/]/.test(argv[index]))
28443
+ break;
28444
+ }
28445
+ const joined = argv.map(quoteArg).join(" ");
28446
+ return nonEmpty2(joined) ? joined : null;
28447
+ };
28385
28448
  var WRITE_TOOLS = new Set([
28386
28449
  "write",
28387
28450
  "edit",
@@ -28401,6 +28464,23 @@ var WRITE_TOOLS = new Set([
28401
28464
  "editfile",
28402
28465
  "writefile"
28403
28466
  ]);
28467
+ var isWriteTool = (name) => typeof name === "string" && WRITE_TOOLS.has(name.toLowerCase().replace(/[_-]/g, ""));
28468
+ var PATH_KEYS = ["file_path", "filePath", "notebook_path", "path", "target_file", "file"];
28469
+ var PATCH_HEADER = /^[ \t]*\*\*\* (?:(?:Add|Update|Delete) File|Move to): (.+)$/gm;
28470
+ var writePaths = (input) => {
28471
+ if (!isRecord4(input))
28472
+ return [];
28473
+ const paths = PATH_KEYS.map((key) => input[key]).filter(nonEmpty2);
28474
+ for (const value of Object.values(input))
28475
+ if (typeof value === "string")
28476
+ for (const match of value.matchAll(PATCH_HEADER))
28477
+ paths.push(match[1].trim());
28478
+ return [...new Set(paths)];
28479
+ };
28480
+ var writeTargets = (input) => {
28481
+ const paths = writePaths(input);
28482
+ return paths.length > 0 ? paths : null;
28483
+ };
28404
28484
  // packages/workit-core/src/hooks/standing.ts
28405
28485
  var sinceStart = new Map;
28406
28486
  // packages/workit-core/src/hooks/cwd-normalize.ts
@@ -28647,8 +28727,168 @@ var CODEX_DESCRIPTOR = {
28647
28727
  ]
28648
28728
  };
28649
28729
  var codexDescriptor = (host) => ({ ...CODEX_DESCRIPTOR, host });
28730
+ var EVENTS = {
28731
+ SessionStart: "session.start",
28732
+ UserPromptSubmit: "context.turn",
28733
+ PreToolUse: "shell.pre",
28734
+ PostToolUse: "shell.post",
28735
+ SubagentStart: "subagent.start",
28736
+ SubagentStop: "subagent.stop",
28737
+ Stop: "stop"
28738
+ };
28650
28739
  var SHELL_TOOLS2 = new Set(["bash", "unified-exec"]);
28740
+ var isShellTool = (name) => SHELL_TOOLS2.has(String(name).toLowerCase());
28651
28741
  var SOURCES2 = new Set(["startup", "resume", "clear", "compact", "fork"]);
28742
+ var parseCodexHookInput = (value) => {
28743
+ if (!isRecord4(value) || !Object.hasOwn(EVENTS, String(value.hook_event_name)))
28744
+ return { ok: false, error: "hook_event_name is required" };
28745
+ const event = value.hook_event_name;
28746
+ const cwd = existingDirectory(value.cwd);
28747
+ if (!cwd)
28748
+ return { ok: false, error: "cwd must be an existing absolute directory" };
28749
+ const toolEvent = event === "PreToolUse" || event === "PostToolUse";
28750
+ if (toolEvent && !nonEmpty2(value.tool_name))
28751
+ return { ok: false, error: "tool_name is required" };
28752
+ const command = isRecord4(value.tool_input) ? commandText(value.tool_input.command) : null;
28753
+ if (toolEvent && isShellTool(value.tool_name) && !command)
28754
+ return { ok: false, error: "tool_input.command is required for shell tools" };
28755
+ if (event === "SubagentStart" && !nonEmpty2(value.agent_id))
28756
+ return { ok: false, error: "agent_id is required" };
28757
+ return {
28758
+ ok: true,
28759
+ data: {
28760
+ hook_event_name: event,
28761
+ session_id: nonEmpty2(value.session_id) ? value.session_id : "",
28762
+ model: optionalText(value.model),
28763
+ permission_mode: optionalText(value.permission_mode),
28764
+ transcript_path: optionalText(value.transcript_path),
28765
+ cwd,
28766
+ ...event === "SessionStart" ? { source: SOURCES2.has(String(value.source)) ? value.source : "resume" } : {},
28767
+ ...nonEmpty2(value.turn_id) ? { turn_id: value.turn_id } : {},
28768
+ ...event === "UserPromptSubmit" ? { prompt: optionalText(value.prompt) } : {},
28769
+ ...nonEmpty2(value.tool_name) ? { tool_name: value.tool_name } : {},
28770
+ ...toolEvent ? { tool_input: value.tool_input } : {},
28771
+ ...event === "PostToolUse" && typeof value.tool_response === "string" ? { tool_response: value.tool_response } : {},
28772
+ ...command ? { command } : {},
28773
+ ...nonEmpty2(value.tool_use_id) ? { tool_use_id: value.tool_use_id } : {},
28774
+ ...nonEmpty2(value.agent_id) ? { agent_id: value.agent_id } : {},
28775
+ ...nonEmpty2(value.agent_type) ? { agent_type: value.agent_type } : {},
28776
+ ...event === "Stop" ? {
28777
+ last_assistant_message: optionalText(value.last_assistant_message),
28778
+ stop_hook_active: value.stop_hook_active === true
28779
+ } : {},
28780
+ ...event === "SubagentStop" ? {
28781
+ agent_transcript_path: optionalText(value.agent_transcript_path),
28782
+ last_assistant_message: optionalText(value.last_assistant_message),
28783
+ stop_hook_active: value.stop_hook_active === true
28784
+ } : {}
28785
+ }
28786
+ };
28787
+ };
28788
+ var protocolEvent = (input) => {
28789
+ switch (input.hook_event_name) {
28790
+ case "SessionStart":
28791
+ return { kind: "session.start", source: input.source };
28792
+ case "UserPromptSubmit":
28793
+ return { kind: "context.turn", prompt: input.prompt ?? null };
28794
+ case "PreToolUse": {
28795
+ const toolUseId = input.tool_use_id ?? null;
28796
+ if (isShellTool(input.tool_name))
28797
+ return { kind: "shell.pre", command: input.command ?? "", toolUseId };
28798
+ if (isWriteTool(input.tool_name))
28799
+ return {
28800
+ kind: "write.pre",
28801
+ tool: input.tool_name,
28802
+ paths: writeTargets(input.tool_input),
28803
+ toolUseId
28804
+ };
28805
+ return { kind: "tool.pre", tool: input.tool_name, toolUseId };
28806
+ }
28807
+ case "PostToolUse": {
28808
+ const toolUseId = input.tool_use_id ?? null;
28809
+ return isShellTool(input.tool_name) ? {
28810
+ kind: "shell.post",
28811
+ command: input.command ?? "",
28812
+ stdout: input.tool_response ?? "",
28813
+ exitCode: null,
28814
+ toolUseId
28815
+ } : { kind: "tool.pre", tool: input.tool_name, toolUseId };
28816
+ }
28817
+ case "SubagentStart":
28818
+ return {
28819
+ kind: "subagent.start",
28820
+ agentId: input.agent_id,
28821
+ agentType: input.agent_type ?? "unknown",
28822
+ task: null
28823
+ };
28824
+ case "SubagentStop":
28825
+ return {
28826
+ kind: "subagent.stop",
28827
+ agentId: input.agent_id ?? null,
28828
+ agentType: input.agent_type ?? null,
28829
+ lastMessage: input.last_assistant_message ?? null,
28830
+ stopHookActive: input.stop_hook_active ?? false
28831
+ };
28832
+ case "Stop":
28833
+ return {
28834
+ kind: "stop",
28835
+ lastMessage: input.last_assistant_message ?? null,
28836
+ stopHookActive: input.stop_hook_active ?? false
28837
+ };
28838
+ }
28839
+ };
28840
+ var output = (event, extra = {}) => ({
28841
+ hookSpecificOutput: { hookEventName: event, ...extra }
28842
+ });
28843
+ var render = (decision, native) => {
28844
+ if (native === "Stop" && decision.kind === "continue")
28845
+ return { json: { decision: "block", reason: decision.reason }, exitCode: 0 };
28846
+ if (native === "SubagentStop" || native === "Stop" || native === null)
28847
+ return { json: {}, exitCode: 0 };
28848
+ if (decision.kind === "deny")
28849
+ return {
28850
+ json: output(native, {
28851
+ permissionDecision: "deny",
28852
+ permissionDecisionReason: decision.reason
28853
+ }),
28854
+ exitCode: 0
28855
+ };
28856
+ if (decision.kind === "context")
28857
+ return { json: output(native, { additionalContext: decision.text }), exitCode: 0 };
28858
+ return { json: output(native), exitCode: 0 };
28859
+ };
28860
+ var codexAdapter = {
28861
+ descriptor: CODEX_DESCRIPTOR,
28862
+ parse(raw, env) {
28863
+ const host = detectCodexSurface(env);
28864
+ const parsed = parseCodexHookInput(raw);
28865
+ if (!parsed.ok) {
28866
+ const native = isRecord4(raw) && Object.hasOwn(EVENTS, String(raw.hook_event_name)) ? raw.hook_event_name : null;
28867
+ return { ok: false, error: parsed.error, native, event: native ? EVENTS[native] : null };
28868
+ }
28869
+ const input = parsed.data;
28870
+ return {
28871
+ ok: true,
28872
+ native: input.hook_event_name,
28873
+ input: {
28874
+ host,
28875
+ cwd: input.cwd,
28876
+ session: {
28877
+ id: input.session_id,
28878
+ agentId: input.agent_id ?? null,
28879
+ agentType: input.agent_type ?? null,
28880
+ parentId: null
28881
+ },
28882
+ permissionMode: input.permission_mode,
28883
+ transcriptPath: input.transcript_path,
28884
+ model: input.model,
28885
+ event: protocolEvent(input)
28886
+ }
28887
+ };
28888
+ },
28889
+ render,
28890
+ addendum: (input) => `<workit-codex-mutations>Codex MCP is read-only: unattested callers cannot mutate. Run workit verbs with the workit CLI on the shell (node_modules/.bin/workit, or npx -y @brainervirus/workit-cli): check, git branch|commit|push, pr, ci, stack, ledger, handoff; task-family mutations take --json${input.session.id ? ` --actor ${input.session.id}` : ""}. workit acts as this thread (CODEX_THREAD_ID${input.session.id ? `=${input.session.id}` : ""}) unless WORKIT_SESSION_ID is set. A verifier or reviewer records workit ledger verdict under its own session, never the author's: a workit-verifier or workit-reviewer agent uses the --session its SubagentStart context names; any other passes --as verifier (an id derived from its own thread). A hand-minted id such as <lead>-v1 is refused as forged_session. Merge and release need a workspace grant (workit grant show); a question answer is not host permission.</workit-codex-mutations>`
28891
+ };
28652
28892
 
28653
28893
  // packages/workit-core/src/hooks/hosts/cursor.ts
28654
28894
  var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
@@ -28656,6 +28896,112 @@ var BLOCKING2 = new Set(["preToolUse", "beforeShellExecution", "subagentStart"])
28656
28896
 
28657
28897
  // packages/workit-core/src/hooks/run.ts
28658
28898
  if (false) {}
28899
+ // packages/workit-core/src/hooks/budget-signal.ts
28900
+ import { createHash as createHash12 } from "node:crypto";
28901
+ import fs17 from "node:fs";
28902
+ import path27 from "node:path";
28903
+ var hash2 = (text) => createHash12("sha256").update(text).digest("hex").slice(0, 32);
28904
+ var budgetDir = (store) => path27.join(store, "budget");
28905
+ var agentBase = (store, session, agentId) => path27.join(budgetDir(store), hash2(`${session}\x00${agentId ?? ""}`));
28906
+ var spawnsFile = (store, session) => path27.join(budgetDir(store), `spawns-${hash2(session)}.jsonl`);
28907
+ var spawnBudget = (prompt) => {
28908
+ const found = /^TIMEBOX:[^\n]*?\b(\d+)\s+tool calls\b/mu.exec(prompt);
28909
+ const n = found ? Number(found[1]) : NaN;
28910
+ return Number.isInteger(n) && n > 0 ? n : null;
28911
+ };
28912
+ var noteSpawnBudget = (cwd, session, type, prompt) => {
28913
+ try {
28914
+ const store = session ? gitStoreDir(cwd) : null;
28915
+ if (!store)
28916
+ return;
28917
+ fs17.mkdirSync(budgetDir(store), { recursive: true });
28918
+ fs17.appendFileSync(spawnsFile(store, session), `${JSON.stringify({ type: type ?? "", n: spawnBudget(prompt) })}
28919
+ `);
28920
+ } catch {}
28921
+ };
28922
+ var readBudget = (file) => {
28923
+ try {
28924
+ const { limit, told } = JSON.parse(fs17.readFileSync(file, "utf8"));
28925
+ return typeof limit === "number" && limit > 0 ? { limit, told: Number(told) || 0 } : null;
28926
+ } catch {
28927
+ return null;
28928
+ }
28929
+ };
28930
+ var claimSpawnBudget = (cwd, session, agentId, agentType) => {
28931
+ try {
28932
+ const store = session ? gitStoreDir(cwd) : null;
28933
+ if (!store)
28934
+ return null;
28935
+ const file = spawnsFile(store, session);
28936
+ const entries = fs17.readFileSync(file, "utf8").split(`
28937
+ `).filter(Boolean).map((line) => JSON.parse(line));
28938
+ const at = entries.findIndex((entry) => entry.type === (agentType ?? ""));
28939
+ if (at < 0)
28940
+ return null;
28941
+ const [{ n }] = entries.splice(at, 1);
28942
+ fs17.writeFileSync(file, entries.map((entry) => `${JSON.stringify(entry)}
28943
+ `).join(""));
28944
+ if (typeof n !== "number" || n <= 0)
28945
+ return null;
28946
+ startBudget(store, session, agentId, n);
28947
+ return n;
28948
+ } catch {
28949
+ return null;
28950
+ }
28951
+ };
28952
+ var startBudget = (store, session, agentId, n) => {
28953
+ const dir = budgetDir(store);
28954
+ fs17.mkdirSync(dir, { recursive: true });
28955
+ const cutoff = Date.now() - 604800000;
28956
+ for (const name of fs17.readdirSync(dir))
28957
+ try {
28958
+ if (fs17.statSync(path27.join(dir, name)).mtimeMs < cutoff)
28959
+ fs17.rmSync(path27.join(dir, name));
28960
+ } catch {}
28961
+ const base = agentBase(store, session, agentId);
28962
+ fs17.writeFileSync(`${base}.json`, JSON.stringify({ limit: n, told: 0 }));
28963
+ fs17.rmSync(`${base}.calls`, { force: true });
28964
+ };
28965
+ var lineFor = (calls) => `Tool-call budget reached (${calls}): commit your work, write your report, stop.`;
28966
+ var reachedOf = (cwd, session, agentId) => {
28967
+ const store = session ? gitStoreDir(cwd) : null;
28968
+ const base = store && agentBase(store, session, agentId);
28969
+ const budget = base && readBudget(`${base}.json`);
28970
+ if (!budget)
28971
+ return null;
28972
+ return { base, budget, reached: Math.floor(fs17.statSync(`${base}.calls`).size / budget.limit) };
28973
+ };
28974
+ var dropBudget = (cwd, session, agentId) => {
28975
+ const store = session ? gitStoreDir(cwd) : null;
28976
+ if (store)
28977
+ for (const ext of [".json", ".calls"])
28978
+ fs17.rmSync(agentBase(store, session, agentId) + ext, { force: true });
28979
+ };
28980
+ var budgetLine = (cwd, session, agentId) => {
28981
+ try {
28982
+ const at = reachedOf(cwd, session, agentId);
28983
+ if (!at || at.reached <= at.budget.told)
28984
+ return null;
28985
+ fs17.writeFileSync(`${at.base}.json`, JSON.stringify({ ...at.budget, told: at.reached }));
28986
+ return lineFor(at.budget.limit * at.reached);
28987
+ } catch {
28988
+ return null;
28989
+ }
28990
+ };
28991
+ var CODEX_GATED = new Set(["bash", "unifiedexec", "shell", "applypatch", "edit", "write"]);
28992
+ var budgetHooks = {
28993
+ line: (input) => budgetLine(input.cwd, input.session.id ?? "", input.session.agentId),
28994
+ start: (input, agentId, agentType) => void claimSpawnBudget(input.cwd, input.session.id ?? "", agentId, agentType),
28995
+ spawn: (input, spawn) => {
28996
+ if (!input.session.agentId)
28997
+ noteSpawnBudget(input.cwd, input.session.id ?? "", String(spawn.subagent_type || "general-purpose"), String(spawn.prompt ?? ""));
28998
+ },
28999
+ stop: (input, agentId) => {
29000
+ try {
29001
+ dropBudget(input.cwd, input.session.id ?? "", agentId);
29002
+ } catch {}
29003
+ }
29004
+ };
28659
29005
  // packages/workit-codex/hooks/workit-hook.ts
28660
29006
  var codexCapabilities = (host, availability = {}) => capabilitiesFor(codexDescriptor(host), {
28661
29007
  "session.start": availability.sessionStart,
@@ -28663,12 +29009,13 @@ var codexCapabilities = (host, availability = {}) => capabilitiesFor(codexDescri
28663
29009
  "subagent.start": availability.subagentStart,
28664
29010
  "subagent.stop": availability.subagentStop
28665
29011
  });
29012
+ var adapter = { ...codexAdapter, budget: budgetHooks };
28666
29013
  if (false)
28667
29014
  ;
28668
29015
 
28669
29016
  // packages/workit-codex/scripts/agents.ts
28670
29017
  import {
28671
- existsSync as existsSync10,
29018
+ existsSync as existsSync11,
28672
29019
  lstatSync as lstatSync3,
28673
29020
  mkdirSync as mkdirSync3,
28674
29021
  readdirSync as readdirSync5,
@@ -28678,18 +29025,18 @@ import {
28678
29025
  writeFileSync as writeFileSync3
28679
29026
  } from "node:fs";
28680
29027
  import os5 from "node:os";
28681
- import path25 from "node:path";
29028
+ import path28 from "node:path";
28682
29029
  var CODEX_AGENT_ROLES = ["verifier", "reviewer", "implementer"];
28683
29030
  var CODEX_AGENT_MARKER = "# Generated by Workit";
28684
29031
  var codexAgentName = (role) => `workit-${role}`;
28685
29032
  var codexAgentFile = (role) => `${codexAgentName(role)}.toml`;
28686
29033
  var tomlString = (value) => JSON.stringify(value);
28687
29034
  var codexHomeOfInstall = (pluginRoot) => {
28688
- const parts = path25.resolve(pluginRoot).split(path25.sep);
29035
+ const parts = path28.resolve(pluginRoot).split(path28.sep);
28689
29036
  const cache = parts.length - 4;
28690
- return cache >= 2 && parts[cache] === "cache" && parts[cache - 1] === "plugins" ? parts.slice(0, cache - 1).join(path25.sep) || path25.sep : null;
29037
+ return cache >= 2 && parts[cache] === "cache" && parts[cache - 1] === "plugins" ? parts.slice(0, cache - 1).join(path28.sep) || path28.sep : null;
28691
29038
  };
28692
- var codexHomeFor = (pluginRoot, env) => codexHomeOfInstall(pluginRoot) ?? (env.CODEX_HOME || path25.join(env.HOME || os5.homedir(), ".codex"));
29039
+ var codexHomeFor = (pluginRoot, env) => codexHomeOfInstall(pluginRoot) ?? (env.CODEX_HOME || path28.join(env.HOME || os5.homedir(), ".codex"));
28693
29040
  var withRoleModel = (content, file, models) => {
28694
29041
  const role = CODEX_AGENT_ROLES.find((candidate) => codexAgentFile(candidate) === file);
28695
29042
  const chosen = role ? models[role] : undefined;
@@ -28712,7 +29059,7 @@ var versionAtLeast = (version, min) => {
28712
29059
  };
28713
29060
  var pluginVersion = (pluginRoot) => {
28714
29061
  try {
28715
- const version = JSON.parse(readFileSync8(path25.join(pluginRoot, "package.json"), "utf8")).version;
29062
+ const version = JSON.parse(readFileSync8(path28.join(pluginRoot, "package.json"), "utf8")).version;
28716
29063
  return typeof version === "string" && /^\d+\.\d+\.\d+/.test(version) ? version.match(/^\d+\.\d+\.\d+/)[0] : null;
28717
29064
  } catch {
28718
29065
  return null;
@@ -28726,15 +29073,15 @@ var lstatOrNull = (file) => {
28726
29073
  }
28727
29074
  };
28728
29075
  var installCodexAgents = (pluginRoot, codexHome, models = {}) => {
28729
- const source = path25.join(pluginRoot, "agents");
28730
- if (!existsSync10(source))
29076
+ const source = path28.join(pluginRoot, "agents");
29077
+ if (!existsSync11(source))
28731
29078
  return [];
28732
29079
  const version = pluginVersion(pluginRoot);
28733
- const target = path25.join(codexHome, "agents");
29080
+ const target = path28.join(codexHome, "agents");
28734
29081
  const results = [];
28735
29082
  for (const file of readdirSync5(source).filter((name) => name.endsWith(".toml")).toSorted()) {
28736
- const content = withRoleModel(stampCodexAgent(readFileSync8(path25.join(source, file), "utf8"), version), file, models);
28737
- const dest = path25.join(target, file);
29083
+ const content = withRoleModel(stampCodexAgent(readFileSync8(path28.join(source, file), "utf8"), version), file, models);
29084
+ const dest = path28.join(target, file);
28738
29085
  const stat = lstatOrNull(dest);
28739
29086
  if (stat?.isSymbolicLink()) {
28740
29087
  results.push({ file: dest, action: "skipped-symlink" });
@@ -28770,7 +29117,7 @@ var installCodexAgents = (pluginRoot, codexHome, models = {}) => {
28770
29117
  results.push({ file: dest, action: "kept-newer-version" });
28771
29118
  continue;
28772
29119
  }
28773
- const temp = path25.join(target, `.${file}.${process.pid}.${Date.now()}.tmp`);
29120
+ const temp = path28.join(target, `.${file}.${process.pid}.${Date.now()}.tmp`);
28774
29121
  writeFileSync3(temp, content, { flag: "wx" });
28775
29122
  try {
28776
29123
  renameSync2(temp, dest);
@@ -28792,14 +29139,14 @@ var codexQualification = (host) => host === "codex_cli" ? { surface: host, cli:
28792
29139
  var resolveCodexWorkspaceRoot = (pluginRoot, env) => {
28793
29140
  const explicit = env.WORKFLOW_WORKSPACE_ROOT;
28794
29141
  const inherited = env.PWD;
28795
- const candidate = explicit ?? (inherited && path26.resolve(inherited) !== path26.resolve(pluginRoot) ? inherited : undefined);
28796
- if (!candidate || !path26.isAbsolute(candidate) || !existsSync11(candidate))
29142
+ const candidate = explicit ?? (inherited && path29.resolve(inherited) !== path29.resolve(pluginRoot) ? inherited : undefined);
29143
+ if (!candidate || !path29.isAbsolute(candidate) || !existsSync12(candidate))
28797
29144
  return null;
28798
29145
  try {
28799
- if (!statSync3(candidate).isDirectory())
29146
+ if (!statSync4(candidate).isDirectory())
28800
29147
  return null;
28801
- const canonical = realpathSync7(candidate);
28802
- if (canonical === realpathSync7(pluginRoot)) {
29148
+ const canonical = realpathSync8(candidate);
29149
+ if (canonical === realpathSync8(pluginRoot)) {
28803
29150
  if (!explicit)
28804
29151
  return null;
28805
29152
  const workspace = new TaskStore(canonical).readWorkspace();
@@ -28832,7 +29179,7 @@ var codexContextProvider = (host = detectCodexSurface(process.env), root) => ({
28832
29179
  };
28833
29180
  }
28834
29181
  });
28835
- var pluginRoot = () => path26.resolve(path26.dirname(fileURLToPath3(import.meta.url)), "..");
29182
+ var pluginRoot = () => path29.resolve(path29.dirname(fileURLToPath3(import.meta.url)), "..");
28836
29183
  var codexRoleModels = (cwd) => {
28837
29184
  const out = {};
28838
29185
  for (const role of ["verifier", "reviewer"]) {