@brainervirus/workit-codex 2.6.0 → 2.8.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.
@@ -6622,12 +6622,12 @@ var require_dist = __commonJS(function(exports, module) {
6622
6622
 
6623
6623
  // packages/workit-codex/scripts/launch-mcp.ts
6624
6624
  import { existsSync as existsSync8, realpathSync as realpathSync9, statSync as statSync3 } from "node:fs";
6625
- import path16 from "node:path";
6625
+ import path18 from "node:path";
6626
6626
 
6627
6627
  // packages/workit-core/src/core.ts
6628
- import { spawnSync as spawnSync4 } from "node:child_process";
6628
+ import { spawnSync as spawnSync6 } from "node:child_process";
6629
6629
  import { existsSync as existsSync5, realpathSync as realpathSync7 } from "node:fs";
6630
- import path11 from "node:path";
6630
+ import path13 from "node:path";
6631
6631
 
6632
6632
  // packages/workit-core/src/core/task-contract.ts
6633
6633
  import { createHash, randomUUID } from "node:crypto";
@@ -14803,7 +14803,37 @@ var candidateSchema = object({
14803
14803
  path: ["id"]
14804
14804
  });
14805
14805
  });
14806
- var evidenceSchema = object({
14806
+ var checkObservationSchema = object({
14807
+ observer: literal("workit_cli"),
14808
+ name: nonEmpty.nullable(),
14809
+ configured: boolean2(),
14810
+ argv: array(text).min(1),
14811
+ shell: boolean2(),
14812
+ cwd: nonEmpty,
14813
+ exitCode: safeInteger,
14814
+ durationMs: safeInteger.min(0),
14815
+ timedOut: boolean2(),
14816
+ head: text.nullable(),
14817
+ tree: text.nullable(),
14818
+ dirty: boolean2().nullable(),
14819
+ signal: text.nullable(),
14820
+ treeAfter: text.nullable(),
14821
+ modifiedWorktree: boolean2(),
14822
+ base: text.nullable(),
14823
+ patchId: text.nullable(),
14824
+ environment: object({
14825
+ platform: nonEmpty,
14826
+ arch: nonEmpty,
14827
+ vars: record(nonEmpty, text.nullable())
14828
+ }).strict(),
14829
+ logDigest: text.nullable(),
14830
+ logRef: text.nullable(),
14831
+ logTail: array(text).max(40),
14832
+ ledgerRowId: text.nullable(),
14833
+ attestation: object({ host: nonEmpty, session: text.nullable(), agentId: text.nullable() }).strict().nullable()
14834
+ }).strict();
14835
+ var CHECK_OBSERVATION_PATH = "evidence.*.data.observation";
14836
+ var evidenceFields = {
14807
14837
  kind: _enum(["check", "review", "investigation", "artifact"]),
14808
14838
  claim: text,
14809
14839
  requirementIds: array(digest),
@@ -14814,7 +14844,9 @@ var evidenceSchema = object({
14814
14844
  refs: array(refSchema),
14815
14845
  exitCode: safeInteger.nullable(),
14816
14846
  reviewContext: refSchema.nullable()
14817
- }).strict();
14847
+ };
14848
+ var evidenceSchema = object({ ...evidenceFields, observation: checkObservationSchema.optional() }).strict();
14849
+ var reportedEvidenceSchema = object(evidenceFields).strict();
14818
14850
  var evidenceEvaluationSchema = object({
14819
14851
  evidenceId: id,
14820
14852
  status: _enum(["passed", "failed", "missing", "skipped", "stale"]),
@@ -15125,7 +15157,7 @@ var evidenceOperations = {
15125
15157
  action: literal("record"),
15126
15158
  ...taskId,
15127
15159
  expectedRevision: revision.optional(),
15128
- evidence: evidenceSchema
15160
+ evidence: reportedEvidenceSchema
15129
15161
  })
15130
15162
  };
15131
15163
  var findingOperations = {
@@ -17625,10 +17657,10 @@ function assertProductWriteAllowed(input) {
17625
17657
  }
17626
17658
 
17627
17659
  // packages/workit-core/src/core/task-evaluation.ts
17628
- import { createHash as createHash6 } from "node:crypto";
17629
- import * as fs8 from "node:fs";
17630
- import { spawnSync as spawnSync3 } from "node:child_process";
17631
- import path10 from "node:path";
17660
+ import { createHash as createHash7 } from "node:crypto";
17661
+ import * as fs10 from "node:fs";
17662
+ import { spawnSync as spawnSync5 } from "node:child_process";
17663
+ import path12 from "node:path";
17632
17664
 
17633
17665
  // packages/workit-core/src/core/authority.ts
17634
17666
  import { createHash as createHash5 } from "node:crypto";
@@ -19323,16 +19355,346 @@ function reconcileAction(input) {
19323
19355
  var bindingCovers = scopeCovers;
19324
19356
  var verifyDecisionContent = verifyContentRefs;
19325
19357
 
19358
+ // packages/workit-core/src/check-config.ts
19359
+ import { spawnSync as spawnSync3 } from "node:child_process";
19360
+ import fs8 from "node:fs";
19361
+ import path10 from "node:path";
19362
+ var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
19363
+ var CHECKS_FILE = "workit.checks.json";
19364
+ var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
19365
+ var SHELL_META = /[|&;<>()$`\\*?[\]{}~!#]/u;
19366
+ function splitCommand(command) {
19367
+ const argv = [];
19368
+ let current = "";
19369
+ let quote = null;
19370
+ let started = false;
19371
+ for (const char of command) {
19372
+ if (quote) {
19373
+ if (char === quote)
19374
+ quote = null;
19375
+ else
19376
+ current += char;
19377
+ continue;
19378
+ }
19379
+ if (char === '"' || char === "'") {
19380
+ quote = char;
19381
+ started = true;
19382
+ continue;
19383
+ }
19384
+ if (/\s/u.test(char)) {
19385
+ if (started)
19386
+ argv.push(current);
19387
+ current = "";
19388
+ started = false;
19389
+ continue;
19390
+ }
19391
+ if (SHELL_META.test(char))
19392
+ return new Error(`"${command}" uses shell syntax (${char}); configure an argv array instead`);
19393
+ current += char;
19394
+ started = true;
19395
+ }
19396
+ if (quote)
19397
+ return new Error(`"${command}" has an unterminated quote`);
19398
+ if (started)
19399
+ argv.push(current);
19400
+ return argv.length ? argv : new Error("empty command");
19401
+ }
19402
+ var git = (cwd, args) => {
19403
+ const run = spawnSync3("git", args, {
19404
+ cwd,
19405
+ encoding: "utf8",
19406
+ timeout: 20000,
19407
+ killSignal: "SIGKILL",
19408
+ windowsHide: true
19409
+ });
19410
+ const value = run.status === 0 ? (run.stdout ?? "").trim() : "";
19411
+ return value || null;
19412
+ };
19413
+ function checkRoot(cwd) {
19414
+ return path10.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
19415
+ }
19416
+ var packageManager = (root) => {
19417
+ const has = (file) => fs8.existsSync(path10.join(root, file));
19418
+ if (has("bun.lock") || has("bun.lockb"))
19419
+ return "bun";
19420
+ if (has("pnpm-lock.yaml"))
19421
+ return "pnpm";
19422
+ if (has("yarn.lock"))
19423
+ return "yarn";
19424
+ return "npm";
19425
+ };
19426
+ var scriptCheck = (pm, name) => {
19427
+ const argv = [pm, "run", name];
19428
+ const accepts = [argv];
19429
+ if (pm === "pnpm" || pm === "yarn" || pm === "npm" && name === "test")
19430
+ accepts.push([pm, name]);
19431
+ return { name, argv, accepts };
19432
+ };
19433
+ var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
19434
+ function detectedChecks(root) {
19435
+ const read = (file) => {
19436
+ try {
19437
+ return fs8.readFileSync(path10.join(root, file), "utf8");
19438
+ } catch {
19439
+ return null;
19440
+ }
19441
+ };
19442
+ const checks = [];
19443
+ const add = (check) => {
19444
+ if (!checks.some((item) => item.name === check.name))
19445
+ checks.push(check);
19446
+ };
19447
+ const manifestText = read("package.json");
19448
+ if (manifestText !== null) {
19449
+ let manifest = null;
19450
+ try {
19451
+ manifest = JSON.parse(manifestText);
19452
+ } catch {}
19453
+ const scripts = isRecord2(manifest) && isRecord2(manifest.scripts) ? manifest.scripts : {};
19454
+ const pm = packageManager(root);
19455
+ for (const name of SCRIPT_CHECKS) {
19456
+ const script = scripts[name];
19457
+ if (typeof script === "string" && script.trim() !== "")
19458
+ add(scriptCheck(pm, name));
19459
+ }
19460
+ }
19461
+ const plain = (name, argv) => ({ name, argv, accepts: [argv] });
19462
+ if (read("go.mod") !== null)
19463
+ add(plain("test", ["go", "test", "./..."]));
19464
+ if (read("Cargo.toml") !== null)
19465
+ add(plain("test", ["cargo", "test"]));
19466
+ if (read("pytest.ini") !== null || /\bpytest\b/u.test(read("pyproject.toml") ?? ""))
19467
+ add(plain("test", ["pytest"]));
19468
+ const makefile = read("Makefile") ?? read("makefile") ?? read("GNUmakefile");
19469
+ if (makefile !== null && /^test\s*:(?!=)/mu.test(makefile))
19470
+ add(plain("test", ["make", "test"]));
19471
+ return checks;
19472
+ }
19473
+ var GATES = ["testing", "verification"];
19474
+ function loadCheckConfig(cwd) {
19475
+ const root = checkRoot(cwd);
19476
+ const file = path10.join(root, CHECKS_FILE);
19477
+ const none = (error = null) => ({
19478
+ source: error ? CHECKS_FILE : "none",
19479
+ root,
19480
+ checks: [],
19481
+ gates: {},
19482
+ error
19483
+ });
19484
+ let text = null;
19485
+ try {
19486
+ text = fs8.readFileSync(file, "utf8");
19487
+ } catch (error) {
19488
+ if (error.code !== "ENOENT")
19489
+ return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
19490
+ }
19491
+ if (text === null) {
19492
+ const checks = detectedChecks(root);
19493
+ return checks.length ? { source: "defaults", root, checks, gates: {}, error: null } : none();
19494
+ }
19495
+ let parsed;
19496
+ try {
19497
+ parsed = JSON.parse(text);
19498
+ } catch {
19499
+ return none(`${CHECKS_FILE} is not valid JSON`);
19500
+ }
19501
+ if (!isRecord2(parsed) || !isRecord2(parsed.checks))
19502
+ return none(`${CHECKS_FILE} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
19503
+ const checks = [];
19504
+ for (const [name, value] of Object.entries(parsed.checks)) {
19505
+ if (!CHECK_NAME.test(name))
19506
+ return none(`${CHECKS_FILE}: check name "${name}" must match ${CHECK_NAME.source}`);
19507
+ const argv = typeof value === "string" ? splitCommand(value) : Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && item.length > 0) ? value : new Error("expected a command string or a non-empty argv array");
19508
+ if (argv instanceof Error)
19509
+ return none(`${CHECKS_FILE}: check "${name}": ${argv.message}`);
19510
+ checks.push({ name, argv, accepts: [argv] });
19511
+ }
19512
+ const gates = {};
19513
+ if (parsed.gates !== undefined) {
19514
+ if (!isRecord2(parsed.gates))
19515
+ return none(`${CHECKS_FILE}: gates must be {"testing":"<name>"|["<name>",…]}`);
19516
+ for (const [gate, value] of Object.entries(parsed.gates)) {
19517
+ if (!GATES.includes(gate))
19518
+ return none(`${CHECKS_FILE}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
19519
+ const names = typeof value === "string" ? [value] : value;
19520
+ if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
19521
+ return none(`${CHECKS_FILE}: gates.${gate} must name configured checks`);
19522
+ gates[gate] = names;
19523
+ }
19524
+ }
19525
+ return { source: CHECKS_FILE, root, checks, gates, error: null };
19526
+ }
19527
+ var sameArgv = (left, right) => left.length === right.length && left.every((item, index) => item === right[index]);
19528
+ function matchesNamedCheck(config, name, argv, cwd) {
19529
+ if (config.error || name === null || cwd !== ".")
19530
+ return false;
19531
+ const check = config.checks.find((item) => item.name === name);
19532
+ return Boolean(check?.accepts.some((accepted) => sameArgv(accepted, argv)));
19533
+ }
19534
+ function gateCheckNames(config, dimension) {
19535
+ const explicit = config.gates[dimension];
19536
+ if (explicit)
19537
+ return explicit;
19538
+ const names = config.checks.map((check) => check.name);
19539
+ if (dimension === "testing")
19540
+ return names.includes("test") ? ["test"] : [];
19541
+ return names;
19542
+ }
19543
+ var workitOnPath;
19544
+ function workitAvailable(env = process.env) {
19545
+ if (workitOnPath !== undefined)
19546
+ return workitOnPath;
19547
+ const windows = process.platform === "win32";
19548
+ const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
19549
+ const dirs = (env.PATH ?? env.Path ?? "").split(path10.delimiter).filter(Boolean);
19550
+ workitOnPath = dirs.some((dir) => exts.some((ext) => {
19551
+ try {
19552
+ return fs8.statSync(path10.join(dir, `workit${ext}`)).isFile();
19553
+ } catch {
19554
+ return false;
19555
+ }
19556
+ }));
19557
+ return workitOnPath;
19558
+ }
19559
+ function checkCommand(args, version, onPath = workitAvailable()) {
19560
+ return onPath ? `workit check ${args}` : `npx -y @brainervirus/workit-cli@${version} check ${args}`;
19561
+ }
19562
+
19563
+ // packages/workit-core/src/git/rev.ts
19564
+ import { spawnSync as spawnSync4 } from "node:child_process";
19565
+ import { createHash as createHash6 } from "node:crypto";
19566
+ import fs9 from "node:fs";
19567
+ import os2 from "node:os";
19568
+ import path11 from "node:path";
19569
+ var MAX_BUFFER = 256 * 1024 * 1024;
19570
+ var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
19571
+ var git2 = (cwd, args, options = {}) => {
19572
+ const result = spawnSync4("git", args, {
19573
+ cwd,
19574
+ encoding: "utf8",
19575
+ env: options.env ?? process.env,
19576
+ input: options.input,
19577
+ maxBuffer: MAX_BUFFER,
19578
+ timeout: options.timeoutMs ?? GIT_TIMEOUTS.local,
19579
+ killSignal: "SIGKILL",
19580
+ windowsHide: true
19581
+ });
19582
+ const error = result.error;
19583
+ return {
19584
+ ok: result.status === 0,
19585
+ stdout: result.stdout ?? "",
19586
+ stderr: result.stderr ?? "",
19587
+ timedOut: error?.code === "ETIMEDOUT"
19588
+ };
19589
+ };
19590
+ var line = (run) => {
19591
+ if (!run.ok)
19592
+ return null;
19593
+ const value = run.stdout.trim();
19594
+ return value ? value : null;
19595
+ };
19596
+ function headSha(cwd) {
19597
+ return line(git2(cwd, ["rev-parse", "--verify", "-q", "HEAD^{commit}"]));
19598
+ }
19599
+ var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
19600
+ var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
19601
+ var seedIndex = (from, to) => {
19602
+ fs9.copyFileSync(from, to);
19603
+ const { atimeNs, mtimeNs } = fs9.statSync(from, { bigint: true });
19604
+ const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
19605
+ fs9.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
19606
+ };
19607
+ function worktreeTree(cwd, options = {}) {
19608
+ const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
19609
+ const indexPath = line(git2(cwd, ["rev-parse", "--path-format=absolute", "--git-path", "index"]));
19610
+ if (!top || !indexPath)
19611
+ return null;
19612
+ const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
19613
+ const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
19614
+ const scratch = fs9.mkdtempSync(path11.join(os2.tmpdir(), "workit-tree-"));
19615
+ try {
19616
+ const tempIndex = path11.join(scratch, "index");
19617
+ if (fs9.existsSync(indexPath))
19618
+ seedIndex(indexPath, tempIndex);
19619
+ const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
19620
+ const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
19621
+ env,
19622
+ timeoutMs
19623
+ });
19624
+ if (!untracked.ok)
19625
+ return null;
19626
+ const skipped = [];
19627
+ for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
19628
+ try {
19629
+ const stat = fs9.statSync(path11.join(top, file));
19630
+ if (stat.isFile() && stat.size > cap)
19631
+ skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
19632
+ } catch {}
19633
+ }
19634
+ skipped.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
19635
+ const added = skipped.length ? git2(top, ["add", "-A", "--pathspec-from-file=-", "--pathspec-file-nul"], {
19636
+ env,
19637
+ timeoutMs,
19638
+ input: [".", ...skipped.map((file) => `:(exclude,literal)${file.path}`)].join("\x00")
19639
+ }) : git2(top, ["add", "-A"], { env, timeoutMs });
19640
+ if (!added.ok)
19641
+ return null;
19642
+ const tree = line(git2(top, ["write-tree"], { env, timeoutMs }));
19643
+ if (!tree)
19644
+ return null;
19645
+ const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
19646
+ const key = skipped.length ? `sha256:${createHash6("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
19647
+ return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
19648
+ } finally {
19649
+ fs9.rmSync(scratch, { recursive: true, force: true });
19650
+ }
19651
+ }
19652
+ function worktreeSignal(cwd, options = {}) {
19653
+ const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
19654
+ if (!top)
19655
+ return null;
19656
+ const status = git2(top, [
19657
+ "status",
19658
+ "--porcelain=v1",
19659
+ "-z",
19660
+ "--untracked-files=all",
19661
+ "--no-renames",
19662
+ "--ignore-submodules=dirty"
19663
+ ], {
19664
+ env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
19665
+ timeoutMs: options.timeoutMs ?? GIT_TIMEOUTS.worktree
19666
+ });
19667
+ if (!status.ok)
19668
+ return null;
19669
+ const hash = createHash6("sha256");
19670
+ hash.update(`${headSha(top) ?? "unborn"}\x00`);
19671
+ for (const entry of status.stdout.split("\x00")) {
19672
+ if (entry.length < 4)
19673
+ continue;
19674
+ const file = entry.slice(3);
19675
+ const absolute = path11.join(top, file);
19676
+ let stat = "-";
19677
+ try {
19678
+ const info = fs9.lstatSync(absolute, { bigint: true });
19679
+ stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
19680
+ if (info.isDirectory())
19681
+ stat += `:${headSha(absolute) ?? "-"}`;
19682
+ } catch {}
19683
+ hash.update(`${entry.slice(0, 2)}\x00${file}\x00${stat}\x00`);
19684
+ }
19685
+ return `sig:${hash.digest("hex")}`;
19686
+ }
19687
+
19326
19688
  // packages/workit-core/src/core/task-evaluation.ts
19327
- var digestBytes3 = (value) => createHash6("sha256").update(value).digest("hex");
19328
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path10.sep}`);
19329
- var relative = (root, candidate) => path10.relative(root, candidate).split(path10.sep).join("/") || ".";
19689
+ var digestBytes3 = (value) => createHash7("sha256").update(value).digest("hex");
19690
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path12.sep}`);
19691
+ var relative = (root, candidate) => path12.relative(root, candidate).split(path12.sep).join("/") || ".";
19330
19692
  var canonicalPath = (value) => {
19331
19693
  if (typeof value !== "string" || !value)
19332
19694
  return null;
19333
19695
  if (value.includes("\\") || value.split("/").includes(".."))
19334
19696
  return null;
19335
- const normalized = path10.posix.normalize(value);
19697
+ const normalized = path12.posix.normalize(value);
19336
19698
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
19337
19699
  return null;
19338
19700
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -19368,7 +19730,7 @@ var compareCodeUnits2 = (left, right) => {
19368
19730
  return left.length - right.length;
19369
19731
  };
19370
19732
  var gitPaths = (root) => {
19371
- const result = spawnSync3("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
19733
+ const result = spawnSync5("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
19372
19734
  cwd: root,
19373
19735
  encoding: "buffer"
19374
19736
  });
@@ -19380,21 +19742,21 @@ var gitPaths = (root) => {
19380
19742
  git: !/not a git repository/i.test(stderr)
19381
19743
  };
19382
19744
  }
19383
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")) : [];
19384
- const stagedDeleted = spawnSync3("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
19745
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path12.sep).join("/")) : [];
19746
+ const stagedDeleted = spawnSync5("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
19385
19747
  if (stagedDeleted.status !== 0) {
19386
19748
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
19387
19749
  if (!/not a git repository/i.test(stderr))
19388
19750
  return { paths, uncertain: true, git: true };
19389
19751
  } else if (stagedDeleted.stdout) {
19390
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")));
19752
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path12.sep).join("/")));
19391
19753
  }
19392
19754
  return { paths: [...new Set(paths)], uncertain: false, git: true };
19393
19755
  };
19394
19756
  var walk = (root, directory, output) => {
19395
19757
  let names;
19396
19758
  try {
19397
- names = fs8.readdirSync(directory);
19759
+ names = fs10.readdirSync(directory);
19398
19760
  } catch {
19399
19761
  return true;
19400
19762
  }
@@ -19402,10 +19764,10 @@ var walk = (root, directory, output) => {
19402
19764
  for (const name of names) {
19403
19765
  if (name === ".git" || name === ".workit")
19404
19766
  continue;
19405
- const target = path10.join(directory, name);
19767
+ const target = path12.join(directory, name);
19406
19768
  let stat;
19407
19769
  try {
19408
- stat = fs8.lstatSync(target);
19770
+ stat = fs10.lstatSync(target);
19409
19771
  } catch {
19410
19772
  uncertain = true;
19411
19773
  continue;
@@ -19420,14 +19782,14 @@ var walk = (root, directory, output) => {
19420
19782
  var scopeRoots = (root, scope) => {
19421
19783
  const roots = [];
19422
19784
  for (const item of scope.paths.length ? scope.paths : ["."]) {
19423
- const target = path10.resolve(root, item);
19785
+ const target = path12.resolve(root, item);
19424
19786
  if (!inside(root, target))
19425
19787
  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` }] });
19426
19788
  let ancestor = target;
19427
- while (!fs8.existsSync(ancestor) && ancestor !== root)
19428
- ancestor = path10.dirname(ancestor);
19789
+ while (!fs10.existsSync(ancestor) && ancestor !== root)
19790
+ ancestor = path12.dirname(ancestor);
19429
19791
  try {
19430
- if (!inside(root, fs8.realpathSync(ancestor)))
19792
+ if (!inside(root, fs10.realpathSync(ancestor)))
19431
19793
  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` }] });
19432
19794
  } catch {
19433
19795
  return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
@@ -19439,7 +19801,7 @@ var scopeRoots = (root, scope) => {
19439
19801
  function captureCandidate(root, scope, environment = []) {
19440
19802
  let checkout;
19441
19803
  try {
19442
- checkout = fs8.realpathSync(root);
19804
+ checkout = fs10.realpathSync(root);
19443
19805
  } catch {
19444
19806
  return failure2("invalid_input", "candidate root does not exist");
19445
19807
  }
@@ -19457,11 +19819,11 @@ function captureCandidate(root, scope, environment = []) {
19457
19819
  if (item === ".git" || item === ".workit")
19458
19820
  continue;
19459
19821
  names.add(item);
19460
- if (!fs8.existsSync(target))
19822
+ if (!fs10.existsSync(target))
19461
19823
  continue;
19462
19824
  let stat;
19463
19825
  try {
19464
- stat = fs8.lstatSync(target);
19826
+ stat = fs10.lstatSync(target);
19465
19827
  } catch {
19466
19828
  uncertain = true;
19467
19829
  continue;
@@ -19477,17 +19839,17 @@ function captureCandidate(root, scope, environment = []) {
19477
19839
  for (const item of [...names].toSorted()) {
19478
19840
  if (!scopeMatches(item, normalizedScope))
19479
19841
  continue;
19480
- const target = path10.join(checkout, item);
19842
+ const target = path12.join(checkout, item);
19481
19843
  try {
19482
- const stat = fs8.lstatSync(target);
19844
+ const stat = fs10.lstatSync(target);
19483
19845
  if (stat.isSymbolicLink()) {
19484
- const link = fs8.readlinkSync(target);
19846
+ const link = fs10.readlinkSync(target);
19485
19847
  files.push({ path: item, kind: "symlink", digest: digestBytes3(link), executable: null });
19486
19848
  } else if (stat.isFile()) {
19487
19849
  files.push({
19488
19850
  path: item,
19489
19851
  kind: "file",
19490
- digest: digestBytes3(fs8.readFileSync(target)),
19852
+ digest: digestBytes3(fs10.readFileSync(target)),
19491
19853
  executable: (stat.mode & 73) !== 0
19492
19854
  });
19493
19855
  } else if (!stat.isDirectory()) {
@@ -19512,7 +19874,7 @@ function captureCandidate(root, scope, environment = []) {
19512
19874
  const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
19513
19875
  if (values.some(({ value }) => value === null))
19514
19876
  completeness = "uncertain";
19515
- const headResult = spawnSync3("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
19877
+ const headResult = spawnSync5("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
19516
19878
  if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
19517
19879
  completeness = "uncertain";
19518
19880
  const head = headResult.status === 0 ? headResult.stdout.trim() : null;
@@ -19550,7 +19912,24 @@ var sameSession = (left, right) => {
19550
19912
  return left !== null && other !== null && left.host === other.host && left.handle === other.handle;
19551
19913
  };
19552
19914
  var evidenceScopes = (task, ids) => (task.policy?.requirements ?? []).filter((requirement) => ids.includes(requirement.id)).map((requirement) => requirement.scope);
19553
- function evaluateEvidence(task, candidate) {
19915
+ var memo2 = (compute) => {
19916
+ let value;
19917
+ return () => {
19918
+ if (value === undefined)
19919
+ value = compute();
19920
+ return value;
19921
+ };
19922
+ };
19923
+ var treeFreshness = (root) => ({
19924
+ mode: "tree",
19925
+ current: memo2(() => worktreeTree(root)?.key ?? null)
19926
+ });
19927
+ var signalFreshness = (root) => ({
19928
+ mode: "signal",
19929
+ current: memo2(() => worktreeSignal(root))
19930
+ });
19931
+ var isObservedCheck = (entry) => entry.data.kind === "check" && entry.data.observation?.observer === "workit_cli" && entry.provenance?.kind === "host_observed" && entry.provenance.host === "workit_cli";
19932
+ function evaluateEvidence(task, candidate, freshness) {
19554
19933
  const current = candidate ?? task.candidates.at(-1) ?? null;
19555
19934
  return task.evidence.map((entry) => {
19556
19935
  const evidence = entry.data;
@@ -19560,6 +19939,25 @@ function evaluateEvidence(task, candidate) {
19560
19939
  status: "failed",
19561
19940
  reason: "failed evidence remains historical"
19562
19941
  };
19942
+ const observation = isObservedCheck(entry) ? evidence.observation : undefined;
19943
+ if (observation && observation.tree !== null) {
19944
+ if (task.status === "closed")
19945
+ return { evidenceId: entry.id, status: evidence.result, reason: "recorded before close" };
19946
+ if (observation.modifiedWorktree)
19947
+ return {
19948
+ evidenceId: entry.id,
19949
+ status: "stale",
19950
+ reason: "the check changed the worktree while it ran; ignore or clean its outputs"
19951
+ };
19952
+ const recorded = freshness?.mode === "signal" ? observation.signal : observation.tree;
19953
+ if (recorded !== null && freshness && recorded === freshness.current())
19954
+ return { evidenceId: entry.id, status: evidence.result, reason: "worktree is unchanged" };
19955
+ return {
19956
+ evidenceId: entry.id,
19957
+ status: "stale",
19958
+ reason: freshness?.mode === "signal" ? "the worktree may have changed since the check ran (quick signal; inspect re-checks exactly)" : "the worktree changed since the check ran"
19959
+ };
19960
+ }
19563
19961
  if (evidence.kind === "check" || evidence.kind === "review") {
19564
19962
  if (!evidence.beforeCandidateId || !evidence.candidateId)
19565
19963
  return {
@@ -19643,8 +20041,45 @@ var expectedKinds = (dimension, ruleId) => {
19643
20041
  return "decision";
19644
20042
  return "worker report";
19645
20043
  };
19646
- function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller) {
19647
- const evidence = evaluateEvidence(task, candidate);
20044
+ var observedGate = (requirement) => (requirement.dimension === "testing" || requirement.dimension === "verification") && requirement.before === "close";
20045
+ function evaluateObservedGate(task, requirement, statuses, config) {
20046
+ const names = gateCheckNames(config, requirement.dimension);
20047
+ const observed = task.evidence.filter(isObservedCheck);
20048
+ const fresh = observed.filter((entry) => statuses.get(entry.id) === "passed" && entry.data.observation?.exitCode === 0);
20049
+ const named = (entry) => {
20050
+ const observation = entry.data.observation;
20051
+ return observation.configured && !observation.shell && names.includes(observation.name ?? "") && matchesNamedCheck(config, observation.name, observation.argv, observation.cwd);
20052
+ };
20053
+ const considered = observed.map((entry) => entry.id);
20054
+ const run = checkCommand(names[0] ?? "<name>", runtimeVersion());
20055
+ if (config.error)
20056
+ return {
20057
+ satisfied: [],
20058
+ considered,
20059
+ reason: `check config is invalid (${config.error}); fix it, then run ${run}`
20060
+ };
20061
+ if (!names.length)
20062
+ return {
20063
+ satisfied: [],
20064
+ considered,
20065
+ reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE} (committed, so it shows in the diff), e.g. {"checks":{"test":"<your test command>"}}, then run ${checkCommand("test", runtimeVersion())}`
20066
+ };
20067
+ const satisfied = fresh.filter(named);
20068
+ if (satisfied.length)
20069
+ return {
20070
+ satisfied: satisfied.map((entry) => entry.id),
20071
+ considered,
20072
+ reason: `a fresh passing configured check (${names.join("|")}) was observed by workit check`
20073
+ };
20074
+ const reported = task.evidence.some((entry) => entry.data.kind === "check" && !isObservedCheck(entry) && entry.data.requirementIds.includes(requirement.id));
20075
+ const reason = fresh.length ? `ad-hoc checks do not satisfy a configured gate; run ${run}` : observed.some((entry) => statuses.get(entry.id) === "stale") ? `observed checks are stale (the worktree changed); run ${run}` : observed.some((entry) => statuses.get(entry.id) === "failed") ? `the observed check failed; fix it and run ${run}` : reported ? `agent-reported checks are notes, not evidence; run ${run}` : `no CLI-observed check (needs kind:check observed by workit check); run ${run}`;
20076
+ return { satisfied: [], considered, reason };
20077
+ }
20078
+ function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller, freshness = treeFreshness(checkoutRoot)) {
20079
+ const evidence = evaluateEvidence(task, candidate, freshness);
20080
+ const statuses = new Map(evidence.map((item) => [item.evidenceId, item.status]));
20081
+ let checkConfig;
20082
+ const configFor = () => checkConfig ??= loadCheckConfig(checkoutRoot);
19648
20083
  if (task.policy && task.policy.policyVersion !== POLICY_VERSION)
19649
20084
  return task.policy.requirements.map((requirement) => ({
19650
20085
  requirementId: requirement.id,
@@ -19654,8 +20089,19 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
19654
20089
  reason: "stored policy version is unsupported; reassessment is required"
19655
20090
  }));
19656
20091
  return (task.policy?.requirements ?? []).map((requirement) => {
20092
+ const gate = observedGate(requirement) ? evaluateObservedGate(task, requirement, statuses, configFor()) : null;
20093
+ if (gate?.satisfied.length)
20094
+ return {
20095
+ requirementId: requirement.id,
20096
+ status: "satisfied",
20097
+ evidenceIds: gate.satisfied,
20098
+ decisionIds: [],
20099
+ reason: gate.reason
20100
+ };
19657
20101
  const related = task.evidence.map((entry, index) => ({ entry, evaluation: evidence[index] })).filter(({ entry }) => entry.data.requirementIds.includes(requirement.id));
19658
20102
  const passed = related.filter(({ entry, evaluation }) => {
20103
+ if (gate)
20104
+ return false;
19659
20105
  if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension, requirement.ruleId))
19660
20106
  return false;
19661
20107
  if ((entry.data.kind === "check" || entry.data.kind === "review") && (!entry.data.beforeCandidateId || !entry.data.candidateId || entry.data.beforeCandidateId !== entry.data.candidateId))
@@ -19718,6 +20164,14 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
19718
20164
  reason: "an applicable approved limitation permits the missing evidence"
19719
20165
  };
19720
20166
  const unavailable = capabilities.some((capability) => capability.assurance === "unavailable" && (capability.name === requirement.dimension || capability.surface === requirement.dimension));
20167
+ if (gate)
20168
+ return {
20169
+ requirementId: requirement.id,
20170
+ status: unavailable ? "unavailable" : "unsatisfied",
20171
+ evidenceIds: [...new Set([...gate.considered, ...related.map(({ entry }) => entry.id)])],
20172
+ decisionIds: [],
20173
+ reason: unavailable ? "required capability is unavailable" : gate.reason
20174
+ };
19721
20175
  if (!unavailable && related.length > 0 && !passed.length)
19722
20176
  return {
19723
20177
  requirementId: requirement.id,
@@ -19870,7 +20324,7 @@ function reconcileResume(view, observations = []) {
19870
20324
  const candidate = captureCandidate(view.workspace.root, view.task.intent.data.scope, []);
19871
20325
  if (!candidate.ok)
19872
20326
  return candidate;
19873
- const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
20327
+ const staleEvidenceIds = evaluateEvidence(view.task, candidate.data, treeFreshness(view.workspace.root)).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
19874
20328
  const blockers = [...view.task.progress.blockers];
19875
20329
  if (view.task.workers.some((entry) => isUncertainWorker(entry.data.state)))
19876
20330
  blockers.push({
@@ -21210,10 +21664,7 @@ class WorkitCore {
21210
21664
  if (finding.data.disposition !== "fixed")
21211
21665
  return finding;
21212
21666
  const withEntry = { ...current, candidates, evidence: [...current.evidence, entry] };
21213
- const statuses = new Map(evaluateEvidence(withEntry, currentCandidate.data).map((item) => [
21214
- item.evidenceId,
21215
- item.status
21216
- ]));
21667
+ const statuses = new Map(evaluateEvidence(withEntry, currentCandidate.data, treeFreshness(this.store.root)).map((item) => [item.evidenceId, item.status]));
21217
21668
  const verified = withEntry.evidence.some((item) => findingVerificationPasses(finding.data.candidateId, {
21218
21669
  kind: item.data.kind,
21219
21670
  candidateId: item.data.candidateId,
@@ -21228,6 +21679,71 @@ class WorkitCore {
21228
21679
  return changed;
21229
21680
  return success(changed.data.revision, null, changed.data.evidence.at(-1));
21230
21681
  }
21682
+ observeCheck(request) {
21683
+ return this.retryOmittedRevisions(request, () => this.observeCheckOnce(request));
21684
+ }
21685
+ observeCheckOnce(request) {
21686
+ const root = this.contextRootError();
21687
+ if (!root.ok)
21688
+ return root;
21689
+ if (this.context.caller.host !== "workit_cli" || (this.context.workerId ?? null) !== null)
21690
+ return failure2("permission_denied", "only the workit CLI records observed checks");
21691
+ const observation = checkObservationSchema.safeParse(request?.observation);
21692
+ if (!observation.success)
21693
+ return failure2("invalid_input", "check observation is invalid", {
21694
+ fields: observation.error.issues.map((issue) => ({
21695
+ path: issue.path.join("."),
21696
+ reason: issue.message
21697
+ }))
21698
+ });
21699
+ const task = this.store.readTask(request.taskId);
21700
+ if (!task.ok)
21701
+ return task;
21702
+ if (task.data.status === "closed")
21703
+ return failure2("invalid_transition", "closed task cannot record evidence");
21704
+ const observed = observation.data;
21705
+ let candidate = null;
21706
+ if (observed.tree === null) {
21707
+ const captured = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
21708
+ if (!captured.ok)
21709
+ return captured;
21710
+ candidate = captured.data;
21711
+ }
21712
+ const label = observed.name ?? observed.argv.join(" ");
21713
+ const data = {
21714
+ kind: "check",
21715
+ claim: `workit check ${label}`.slice(0, 500),
21716
+ requirementIds: [],
21717
+ ...candidate ? { beforeCandidateId: candidate.id, candidateId: candidate.id } : {},
21718
+ result: observed.exitCode === 0 ? "passed" : "failed",
21719
+ summary: `${observed.configured ? "configured" : "ad-hoc"} check exited ${observed.exitCode}${observed.timedOut ? " (timed out)" : ""} in ${observed.durationMs} ms`,
21720
+ refs: [],
21721
+ exitCode: observed.exitCode,
21722
+ reviewContext: null,
21723
+ observation: observed
21724
+ };
21725
+ const changed = this.store.mutateTask(task.data.id, request.expectedRevision ?? task.data.revision, (current, mutation) => {
21726
+ const candidates = candidate === null || current.candidates.some((item) => item.id === candidate.id) ? current.candidates : [...current.candidates, candidate];
21727
+ const critical = [...new Set([...current.critical ?? [], CHECK_OBSERVATION_PATH])];
21728
+ return success(mutation.revision, null, {
21729
+ ...current,
21730
+ critical,
21731
+ candidates,
21732
+ evidence: [
21733
+ ...current.evidence,
21734
+ {
21735
+ id: newId(),
21736
+ recordedAt: mutation.now,
21737
+ provenance: provenance(this.context, "host_observed"),
21738
+ data
21739
+ }
21740
+ ]
21741
+ });
21742
+ }, trustedNow(this.context));
21743
+ if (!changed.ok)
21744
+ return changed;
21745
+ return success(changed.data.revision, null, changed.data.evidence.at(-1));
21746
+ }
21231
21747
  decision(request) {
21232
21748
  return this.recordDecision(request);
21233
21749
  }
@@ -21463,7 +21979,7 @@ class WorkitCore {
21463
21979
  const current = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
21464
21980
  if (!current.ok)
21465
21981
  return current;
21466
- const evaluations = evaluateEvidence(task.data, current.data);
21982
+ const evaluations = evaluateEvidence(task.data, current.data, treeFreshness(this.store.root));
21467
21983
  const verified = evidence.some((entry) => {
21468
21984
  const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
21469
21985
  return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
@@ -22167,7 +22683,8 @@ class WorkitCore {
22167
22683
  if (!current.ok)
22168
22684
  return current;
22169
22685
  const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
22170
- const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
22686
+ const tree = treeFreshness(this.store.root);
22687
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller, tree);
22171
22688
  return success(task.revision, workspace.data.revision, {
22172
22689
  id: task.id,
22173
22690
  revision: task.revision,
@@ -22184,7 +22701,7 @@ class WorkitCore {
22184
22701
  writer: evaluationWorkspace.writer
22185
22702
  });
22186
22703
  }
22187
- compactContext(taskId) {
22704
+ compactContext(taskId, freshness) {
22188
22705
  const root = this.contextRootError();
22189
22706
  if (!root.ok)
22190
22707
  return root;
@@ -22194,12 +22711,12 @@ class WorkitCore {
22194
22711
  const helper = this.helperTaskGuard(task.data, true);
22195
22712
  if (!helper.ok)
22196
22713
  return helper;
22197
- const view = this.view(task.data, false);
22714
+ const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
22198
22715
  if (!view.ok)
22199
22716
  return view;
22200
22717
  return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
22201
22718
  }
22202
- view(task, capture = true) {
22719
+ view(task, capture = true, freshness = treeFreshness(this.store.root)) {
22203
22720
  const workspace = this.store.readWorkspace();
22204
22721
  if (!workspace.ok)
22205
22722
  return workspace;
@@ -22210,13 +22727,14 @@ class WorkitCore {
22210
22727
  if (!current.ok)
22211
22728
  return current;
22212
22729
  const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
22213
- const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
22730
+ const tree = freshness;
22731
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller, tree);
22214
22732
  return success(task.revision, workspace.data.revision, {
22215
22733
  task,
22216
22734
  workspace: evaluationWorkspace,
22217
22735
  capabilities: this.context.capabilities,
22218
22736
  requirements,
22219
- evidence: evaluateEvidence(task, current.data)
22737
+ evidence: evaluateEvidence(task, current.data, tree)
22220
22738
  });
22221
22739
  }
22222
22740
  reconcileResume(view, observations = []) {
@@ -22443,13 +22961,14 @@ class WorkitCore {
22443
22961
  return current;
22444
22962
  const withCandidate = current.data;
22445
22963
  const taskForView = withCandidate.id === task.data.candidates.at(-1)?.id ? task.data : { ...task.data, candidates: [...task.data.candidates, withCandidate] };
22446
- const requirements = evaluateRequirements(taskForView, workspace.data, this.context.capabilities, withCandidate, this.store.root, this.context.caller);
22964
+ const tree = treeFreshness(this.store.root);
22965
+ const requirements = evaluateRequirements(taskForView, workspace.data, this.context.capabilities, withCandidate, this.store.root, this.context.caller, tree);
22447
22966
  const view = {
22448
22967
  task: taskForView,
22449
22968
  workspace: workspace.data,
22450
22969
  capabilities: this.context.capabilities,
22451
22970
  requirements,
22452
- evidence: evaluateEvidence(taskForView, withCandidate)
22971
+ evidence: evaluateEvidence(taskForView, withCandidate, tree)
22453
22972
  };
22454
22973
  const closure = evaluateClosure(input.outcome, view);
22455
22974
  if (!closure.ok)
@@ -22506,22 +23025,22 @@ function resolveGitRevision(root, value) {
22506
23025
  }
22507
23026
  function resolveInside(root, candidate) {
22508
23027
  const base = realpathSync7(root);
22509
- const target = path11.resolve(base, candidate);
22510
- if (target !== base && !target.startsWith(base + path11.sep)) {
23028
+ const target = path13.resolve(base, candidate);
23029
+ if (target !== base && !target.startsWith(base + path13.sep)) {
22511
23030
  throw new Error("path must stay inside repository root");
22512
23031
  }
22513
23032
  let ancestor = target;
22514
23033
  while (!existsSync5(ancestor))
22515
- ancestor = path11.dirname(ancestor);
23034
+ ancestor = path13.dirname(ancestor);
22516
23035
  const canonicalAncestor = realpathSync7(ancestor);
22517
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path11.sep)) {
23036
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path13.sep)) {
22518
23037
  throw new Error("path must stay inside repository root");
22519
23038
  }
22520
23039
  return target;
22521
23040
  }
22522
23041
  function run2(root, executable, args, env = {}) {
22523
23042
  const cwd = realpathSync7(root);
22524
- const result = spawnSync4(executable, args, {
23043
+ const result = spawnSync6(executable, args, {
22525
23044
  cwd,
22526
23045
  encoding: "utf8",
22527
23046
  env: { ...process.env, ...env }
@@ -22546,7 +23065,7 @@ var SUPPORT_MATRIX = {
22546
23065
 
22547
23066
  // packages/workit-mcp/src/server.ts
22548
23067
  import { existsSync as existsSync7, readFileSync as readFileSync8, realpathSync as realpathSync8 } from "node:fs";
22549
- import path15 from "node:path";
23068
+ import path17 from "node:path";
22550
23069
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
22551
23070
  function isZ4Schema(s) {
22552
23071
  const schema = s;
@@ -24925,7 +25444,7 @@ class StdioServerTransport {
24925
25444
  import { fileURLToPath as fileURLToPath2 } from "node:url";
24926
25445
 
24927
25446
  // packages/workit-core/src/core/logger.ts
24928
- import os2 from "node:os";
25447
+ import os3 from "node:os";
24929
25448
  var REDACTED = "[REDACTED]";
24930
25449
  var SECRET_VALUE = /\b(?:Bearer|Basic|Digest|Token)\s+\S+/gi;
24931
25450
  var KEY_EQ_VALUE = /\b([A-Za-z0-9_-]*(?:token|secret|password|passwd|apikey|api[_-]?key|authorization|credential|bearer)[A-Za-z0-9_-]*)([:=]\s*).+/gi;
@@ -24968,7 +25487,7 @@ var SENSITIVE_WORDS = new Set([
24968
25487
  ]);
24969
25488
  var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
24970
25489
  var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24971
- var homePattern = new RegExp(`${escapeRegExp(os2.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
25490
+ var homePattern = new RegExp(`${escapeRegExp(os3.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
24972
25491
  var applyPatterns = (value) => {
24973
25492
  let out = value;
24974
25493
  out = out.replace(SECRET_VALUE, REDACTED);
@@ -24982,9 +25501,9 @@ var applyPatterns = (value) => {
24982
25501
  var redactSecrets = (value) => applyPatterns(value);
24983
25502
 
24984
25503
  // packages/workit-core/src/core/pr-create.ts
24985
- import fs9 from "node:fs";
24986
- import path12 from "node:path";
24987
- import { spawnSync as spawnSync5 } from "node:child_process";
25504
+ import fs11 from "node:fs";
25505
+ import path14 from "node:path";
25506
+ import { spawnSync as spawnSync7 } from "node:child_process";
24988
25507
  // packages/workit-core/src/forge/pr-body.ts
24989
25508
  function parseGhIssue(value) {
24990
25509
  const m = /issues\/(\d+)/.exec(value);
@@ -24994,13 +25513,13 @@ function parseGhIssue(value) {
24994
25513
  // packages/workit-core/src/core/pr-create.ts
24995
25514
  function whichOnPath(tool) {
24996
25515
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
24997
- for (const dir of (process.env.PATH ?? "").split(path12.delimiter)) {
25516
+ for (const dir of (process.env.PATH ?? "").split(path14.delimiter)) {
24998
25517
  if (!dir)
24999
25518
  continue;
25000
25519
  for (const name of names) {
25001
- const candidate = path12.join(dir, name);
25520
+ const candidate = path14.join(dir, name);
25002
25521
  try {
25003
- fs9.accessSync(candidate, fs9.constants.X_OK);
25522
+ fs11.accessSync(candidate, fs11.constants.X_OK);
25004
25523
  return candidate;
25005
25524
  } catch {}
25006
25525
  }
@@ -25009,7 +25528,7 @@ function whichOnPath(tool) {
25009
25528
  }
25010
25529
  var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
25011
25530
  var pushRemote = (cwd) => {
25012
- const result = spawnSync5("git", ["remote", "get-url", "--push", "--all", "origin"], {
25531
+ const result = spawnSync7("git", ["remote", "get-url", "--push", "--all", "origin"], {
25013
25532
  cwd,
25014
25533
  encoding: "utf8"
25015
25534
  });
@@ -25020,9 +25539,9 @@ var pushRemote = (cwd) => {
25020
25539
  };
25021
25540
 
25022
25541
  // packages/workit-core/src/core/repo-context.ts
25023
- import { spawnSync as spawnSync6 } from "node:child_process";
25542
+ import { spawnSync as spawnSync8 } from "node:child_process";
25024
25543
  import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "node:fs";
25025
- import path13 from "node:path";
25544
+ import path15 from "node:path";
25026
25545
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
25027
25546
  try {
25028
25547
  gitRevisionParts(value);
@@ -25049,7 +25568,7 @@ var invalidRange = (cwd) => ({
25049
25568
  cwd
25050
25569
  });
25051
25570
  var runGit = (cwd, args) => {
25052
- const result = spawnSync6("git", args, { cwd, encoding: "utf8" });
25571
+ const result = spawnSync8("git", args, { cwd, encoding: "utf8" });
25053
25572
  return {
25054
25573
  stdout: (result.stdout ?? "").trimEnd(),
25055
25574
  stderr: (result.stderr ?? "").trimEnd(),
@@ -25156,7 +25675,7 @@ var findPrTemplate = (cwd) => {
25156
25675
  ]) {
25157
25676
  const parts = rel.split("/");
25158
25677
  const base = parts[parts.length - 1];
25159
- const dir = parts.length > 1 ? path13.join(cwd, ...parts.slice(0, -1)) : cwd;
25678
+ const dir = parts.length > 1 ? path15.join(cwd, ...parts.slice(0, -1)) : cwd;
25160
25679
  const actual = probeCaseInsensitive(dir, base);
25161
25680
  if (actual)
25162
25681
  return [...parts.slice(0, -1), actual].join("/");
@@ -25187,7 +25706,7 @@ var diffStatForRange = (cwd, range) => {
25187
25706
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
25188
25707
  };
25189
25708
  var packageScripts = (cwd, keys) => {
25190
- const pkgPath = path13.join(cwd, "package.json");
25709
+ const pkgPath = path15.join(cwd, "package.json");
25191
25710
  if (!existsSync6(pkgPath))
25192
25711
  return [];
25193
25712
  try {
@@ -25213,9 +25732,9 @@ var documentationFiles = (cwd) => {
25213
25732
  if (entry.isDirectory()) {
25214
25733
  if (excluded.has(entry.name))
25215
25734
  continue;
25216
- walk(path13.join(dir, entry.name), depth + 1);
25735
+ walk(path15.join(dir, entry.name), depth + 1);
25217
25736
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
25218
- matches.push("./" + path13.relative(cwd, path13.join(dir, entry.name)).replaceAll("\\", "/"));
25737
+ matches.push("./" + path15.relative(cwd, path15.join(dir, entry.name)).replaceAll("\\", "/"));
25219
25738
  }
25220
25739
  }
25221
25740
  };
@@ -25231,7 +25750,7 @@ var readTrimmed2 = (file, maxLines) => {
25231
25750
  `);
25232
25751
  };
25233
25752
  function prReadyContext(root, range) {
25234
- const cwd = path13.resolve(repoRoot(root));
25753
+ const cwd = path15.resolve(repoRoot(root));
25235
25754
  if (range !== undefined && !isResolvableContextRange(cwd, range))
25236
25755
  return invalidRange(cwd);
25237
25756
  let stdout = "";
@@ -25289,7 +25808,7 @@ ${status.stderr}` : "");
25289
25808
  stdout += `template_path: ${template}
25290
25809
 
25291
25810
  `;
25292
- stdout += readTrimmed2(path13.join(cwd, template), 220);
25811
+ stdout += readTrimmed2(path15.join(cwd, template), 220);
25293
25812
  } else {
25294
25813
  stdout += `template_path: none
25295
25814
 
@@ -25299,7 +25818,7 @@ ${status.stderr}` : "");
25299
25818
  stdout += `
25300
25819
  `;
25301
25820
  stdout += printSection("Recent Validation Signals");
25302
- if (existsSync6(path13.join(cwd, "package.json"))) {
25821
+ if (existsSync6(path15.join(cwd, "package.json"))) {
25303
25822
  stdout += `package.json detected. Common scripts:
25304
25823
  `;
25305
25824
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -25308,7 +25827,7 @@ ${status.stderr}` : "");
25308
25827
  `) + `
25309
25828
  `;
25310
25829
  }
25311
- if (existsSync6(path13.join(cwd, "Cargo.toml")) || existsSync6(path13.join(cwd, "src-tauri/Cargo.toml"))) {
25830
+ if (existsSync6(path15.join(cwd, "Cargo.toml")) || existsSync6(path15.join(cwd, "src-tauri/Cargo.toml"))) {
25312
25831
  stdout += `Rust project detected.
25313
25832
  `;
25314
25833
  }
@@ -25341,7 +25860,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
25341
25860
  - Apply with the host-native editor, preserving the repository changelog conventions.
25342
25861
  - If Unreleased already has duplicate category headings, normalize_only first.`;
25343
25862
  function changelogContext(root, range) {
25344
- const cwd = path13.resolve(repoRoot(root));
25863
+ const cwd = path15.resolve(repoRoot(root));
25345
25864
  if (range !== undefined && !isResolvableContextRange(cwd, range))
25346
25865
  return invalidRange(cwd);
25347
25866
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -25359,7 +25878,7 @@ function changelogContext(root, range) {
25359
25878
  stdout += CHANGELOG_RULES + `
25360
25879
  `;
25361
25880
  stdout += printSection("Existing CHANGELOG.md");
25362
- const changelogPath = path13.join(cwd, "CHANGELOG.md");
25881
+ const changelogPath = path15.join(cwd, "CHANGELOG.md");
25363
25882
  if (existsSync6(changelogPath)) {
25364
25883
  stdout += readTrimmed2(changelogPath, 260) + `
25365
25884
  `;
@@ -25379,7 +25898,7 @@ function changelogContext(root, range) {
25379
25898
  return { stdout, stderr: "", exitCode: 0, cwd };
25380
25899
  }
25381
25900
  function docsRefreshContext(root, range) {
25382
- const cwd = path13.resolve(repoRoot(root));
25901
+ const cwd = path15.resolve(repoRoot(root));
25383
25902
  if (range !== undefined && !isResolvableContextRange(cwd, range))
25384
25903
  return invalidRange(cwd);
25385
25904
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -25401,7 +25920,7 @@ function docsRefreshContext(root, range) {
25401
25920
  `) + `
25402
25921
  `;
25403
25922
  stdout += printSection("README Preview");
25404
- const readme = path13.join(cwd, "README.md");
25923
+ const readme = path15.join(cwd, "README.md");
25405
25924
  if (existsSync6(readme)) {
25406
25925
  stdout += readTrimmed2(readme, 220) + `
25407
25926
  `;
@@ -25410,7 +25929,7 @@ function docsRefreshContext(root, range) {
25410
25929
  `;
25411
25930
  }
25412
25931
  stdout += printSection("Package Scripts");
25413
- const pkgPath = path13.join(cwd, "package.json");
25932
+ const pkgPath = path15.join(cwd, "package.json");
25414
25933
  if (existsSync6(pkgPath)) {
25415
25934
  try {
25416
25935
  const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
@@ -25424,7 +25943,7 @@ function docsRefreshContext(root, range) {
25424
25943
  return { stdout, stderr: "", exitCode: 0, cwd };
25425
25944
  }
25426
25945
  function releaseNotesContext(root, rangeOrTag) {
25427
- const cwd = path13.resolve(repoRoot(root));
25946
+ const cwd = path15.resolve(repoRoot(root));
25428
25947
  if (!rangeOrTag) {
25429
25948
  return { stdout: "", stderr: `ERROR: release tag or range required
25430
25949
  `, exitCode: 1, cwd };
@@ -25462,7 +25981,7 @@ function releaseNotesContext(root, rangeOrTag) {
25462
25981
  `;
25463
25982
  stdout += printSection("Existing Release Files");
25464
25983
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
25465
- if (existsSync6(path13.join(cwd, rel)))
25984
+ if (existsSync6(path15.join(cwd, rel)))
25466
25985
  stdout += rel + `
25467
25986
  `;
25468
25987
  }
@@ -25490,7 +26009,7 @@ function releaseNotesContext(root, rangeOrTag) {
25490
26009
  }
25491
26010
 
25492
26011
  // packages/workit-core/src/core/tracker-issues.ts
25493
- import { spawnSync as spawnSync7 } from "node:child_process";
26012
+ import { spawnSync as spawnSync9 } from "node:child_process";
25494
26013
  var repoPathFromRemote = (remote) => {
25495
26014
  let rest = (remote || "").trim().replace(/\/+$/, "");
25496
26015
  if (!rest)
@@ -25511,7 +26030,7 @@ var defaultCli = (bin, available) => {
25511
26030
  if (!available)
25512
26031
  return null;
25513
26032
  return (args, root) => {
25514
- const out = spawnSync7(bin, args, {
26033
+ const out = spawnSync9(bin, args, {
25515
26034
  cwd: root,
25516
26035
  encoding: "utf8",
25517
26036
  env: { ...process.env, PATH: process.env.PATH ?? "" }
@@ -25589,8 +26108,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
25589
26108
  };
25590
26109
 
25591
26110
  // packages/workit-core/src/core/youtrack.ts
25592
- import fs10 from "node:fs";
25593
- import path14 from "node:path";
26111
+ import fs12 from "node:fs";
26112
+ import path16 from "node:path";
25594
26113
 
25595
26114
  // packages/workit-core/src/core/templates.ts
25596
26115
  var repoRoot2 = assetRoot();
@@ -25603,22 +26122,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
25603
26122
  // packages/workit-core/src/core/youtrack.ts
25604
26123
  var ISSUE_RE = /^[A-Z]+-\d+$/;
25605
26124
  var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
25606
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path14.join(configDir(), "youtrack.json");
25607
- var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path14.join(resolveConfigDir(), "youtrack.json");
26125
+ var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path16.join(configDir(), "youtrack.json");
26126
+ var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path16.join(resolveConfigDir(), "youtrack.json");
25608
26127
  var youTrackTokenModeOk = (p) => {
25609
26128
  if (process.platform === "win32")
25610
26129
  return true;
25611
- const mode = fs10.statSync(p).mode & 511;
26130
+ const mode = fs12.statSync(p).mode & 511;
25612
26131
  return mode === 384;
25613
26132
  };
25614
26133
  function readYouTrackConfig(required) {
25615
26134
  const cfgPath = youTrackConfigPath();
25616
- if (!fs10.existsSync(cfgPath)) {
26135
+ if (!fs12.existsSync(cfgPath)) {
25617
26136
  return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
25618
26137
  }
25619
26138
  let parsed;
25620
26139
  try {
25621
- parsed = JSON.parse(fs10.readFileSync(cfgPath, "utf8"));
26140
+ parsed = JSON.parse(fs12.readFileSync(cfgPath, "utf8"));
25622
26141
  } catch {
25623
26142
  return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
25624
26143
  }
@@ -25629,11 +26148,11 @@ function readYouTrackConfig(required) {
25629
26148
  }
25630
26149
  var readYouTrackContextConfig = () => {
25631
26150
  const cfgPath = youTrackReadOnlyConfigPath();
25632
- if (!fs10.existsSync(cfgPath))
26151
+ if (!fs12.existsSync(cfgPath))
25633
26152
  return { data: {} };
25634
26153
  let parsed;
25635
26154
  try {
25636
- parsed = JSON.parse(fs10.readFileSync(cfgPath, "utf8"));
26155
+ parsed = JSON.parse(fs12.readFileSync(cfgPath, "utf8"));
25637
26156
  } catch {
25638
26157
  return { error: "YouTrack configuration is unavailable" };
25639
26158
  }
@@ -25672,12 +26191,12 @@ var youTrackToken = () => {
25672
26191
  if ("error" in loaded)
25673
26192
  return loaded;
25674
26193
  const tokenFile = String(loaded.config.tokenFile ?? "");
25675
- const tokenPath = tokenFile ? path14.isAbsolute(tokenFile) ? path14.resolve(tokenFile) : path14.resolve(process.cwd(), tokenFile) : "";
25676
- if (!tokenPath || !fs10.existsSync(tokenPath))
26194
+ const tokenPath = tokenFile ? path16.isAbsolute(tokenFile) ? path16.resolve(tokenFile) : path16.resolve(process.cwd(), tokenFile) : "";
26195
+ if (!tokenPath || !fs12.existsSync(tokenPath))
25677
26196
  return { error: "missing youtrack.token" };
25678
26197
  if (!youTrackTokenModeOk(tokenPath))
25679
26198
  return { error: "youtrack.token mode must be 0600" };
25680
- const token = fs10.readFileSync(tokenPath, "utf8").trim();
26199
+ const token = fs12.readFileSync(tokenPath, "utf8").trim();
25681
26200
  if (!token)
25682
26201
  return { error: "empty token file" };
25683
26202
  if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
@@ -25742,9 +26261,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
25742
26261
  } catch {
25743
26262
  continue;
25744
26263
  }
25745
- if (!fs10.existsSync(full))
26264
+ if (!fs12.existsSync(full))
25746
26265
  continue;
25747
- const text = fs10.readFileSync(full, "utf8");
26266
+ const text = fs12.readFileSync(full, "utf8");
25748
26267
  const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
25749
26268
  if (m)
25750
26269
  return m[1];
@@ -25897,10 +26416,6 @@ var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\
25897
26416
  var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
25898
26417
  var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
25899
26418
 
25900
- // packages/workit-core/src/git/rev.ts
25901
- var MAX_BUFFER = 256 * 1024 * 1024;
25902
- var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
25903
-
25904
26419
  // packages/workit-core/src/ledger.ts
25905
26420
  var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
25906
26421
  var FAILING = new Set(["failed", "blocked"]);
@@ -26042,13 +26557,13 @@ var rootVariants = (workspaceRoot) => {
26042
26557
  if (!workspaceRoot)
26043
26558
  return [];
26044
26559
  const roots = new Set;
26045
- for (const candidate of [workspaceRoot, path15.resolve(workspaceRoot)]) {
26046
- if (path15.isAbsolute(candidate) && candidate !== path15.parse(candidate).root)
26560
+ for (const candidate of [workspaceRoot, path17.resolve(workspaceRoot)]) {
26561
+ if (path17.isAbsolute(candidate) && candidate !== path17.parse(candidate).root)
26047
26562
  roots.add(candidate);
26048
26563
  }
26049
26564
  try {
26050
26565
  const canonical = realpathSync8(workspaceRoot);
26051
- if (canonical !== path15.parse(canonical).root)
26566
+ if (canonical !== path17.parse(canonical).root)
26052
26567
  roots.add(canonical);
26053
26568
  } catch {}
26054
26569
  return [...roots].toSorted((left, right) => right.length - left.length);
@@ -26149,11 +26664,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
26149
26664
  };
26150
26665
  function createMcpServer(host, contextProvider) {
26151
26666
  assertMcpHost(host);
26152
- const here = path15.dirname(fileURLToPath2(import.meta.url));
26667
+ const here = path17.dirname(fileURLToPath2(import.meta.url));
26153
26668
  const sourceMarker = markSourcesLoaded([
26154
- path15.join(here, "server.ts"),
26155
- path15.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
26156
- path15.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
26669
+ path17.join(here, "server.ts"),
26670
+ path17.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
26671
+ path17.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
26157
26672
  ].filter((file) => existsSync7(file)));
26158
26673
  let staleWarned = false;
26159
26674
  const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
@@ -26343,6 +26858,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
26343
26858
  };
26344
26859
  });
26345
26860
  // packages/workit-core/src/hooks/context.ts
26861
+ var signals = new Map;
26346
26862
  var cache = new Map;
26347
26863
  // packages/workit-core/src/hooks/handle.ts
26348
26864
  var offered = new Set;
@@ -26490,8 +27006,8 @@ var codexQualification = (host) => host === "codex_cli" ? { surface: host, cli:
26490
27006
  var resolveCodexWorkspaceRoot = (pluginRoot, env) => {
26491
27007
  const explicit = env.WORKFLOW_WORKSPACE_ROOT;
26492
27008
  const inherited = env.PWD;
26493
- const candidate = explicit ?? (inherited && path16.resolve(inherited) !== path16.resolve(pluginRoot) ? inherited : undefined);
26494
- if (!candidate || !path16.isAbsolute(candidate) || !existsSync8(candidate))
27009
+ const candidate = explicit ?? (inherited && path18.resolve(inherited) !== path18.resolve(pluginRoot) ? inherited : undefined);
27010
+ if (!candidate || !path18.isAbsolute(candidate) || !existsSync8(candidate))
26495
27011
  return null;
26496
27012
  try {
26497
27013
  if (!statSync3(candidate).isDirectory())