@brainervirus/workit-claude-code 7.3.0 → 7.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/workit.js CHANGED
@@ -65,7 +65,7 @@ var package_default;
65
65
  var init_package = __esm(() => {
66
66
  package_default = {
67
67
  name: "@brainervirus/workit-cli",
68
- version: "7.3.0",
68
+ version: "7.4.0",
69
69
  private: false,
70
70
  description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
71
71
  keywords: [
@@ -103,9 +103,9 @@ var init_package = __esm(() => {
103
103
  },
104
104
  dependencies: {
105
105
  "@babel/parser": "7.29.9",
106
- "@brainervirus/workit-core": "^7.3.0",
107
- "@brainervirus/workit-cursor": "^7.3.0",
108
- "@brainervirus/workit-opencode": "^7.3.0",
106
+ "@brainervirus/workit-core": "^7.4.0",
107
+ "@brainervirus/workit-cursor": "^7.4.0",
108
+ "@brainervirus/workit-opencode": "^7.4.0",
109
109
  "@inkjs/ui": "2.0.0",
110
110
  ink: "7.1.1",
111
111
  react: "19.2.8"
@@ -63144,7 +63144,8 @@ var init_skill_manifests = __esm(() => {
63144
63144
  "workit-test-audit",
63145
63145
  "workit-deslop",
63146
63146
  "workit-fanout",
63147
- "workit-verify-app"
63147
+ "workit-verify-app",
63148
+ "workit-retro"
63148
63149
  ];
63149
63150
  if (false) {}
63150
63151
  });
@@ -69916,6 +69917,270 @@ var init_launch = __esm(() => {
69916
69917
  init_upgrade();
69917
69918
  });
69918
69919
 
69920
+ // packages/workit-core/src/knowledge.ts
69921
+ import { spawnSync as spawnSync14 } from "node:child_process";
69922
+ import fs17 from "node:fs";
69923
+ import path36 from "node:path";
69924
+ function lintKnowledge(root) {
69925
+ const seen = new Set;
69926
+ const unreadable = [];
69927
+ const load = (file) => {
69928
+ const doc = read(root, file);
69929
+ if (doc === "unreadable")
69930
+ unreadable.push(file);
69931
+ if (doc === null || doc === "unreadable" || seen.has(doc.real))
69932
+ return null;
69933
+ seen.add(doc.real);
69934
+ return doc;
69935
+ };
69936
+ const steering = STEERING_FILES.map(load).filter((doc) => doc !== null);
69937
+ const needBased = NEED_BASED_FILES.map(load).filter((doc) => doc !== null);
69938
+ const findings = [];
69939
+ for (const doc of steering) {
69940
+ const bytes = Buffer.byteLength(doc.text);
69941
+ if (bytes > AGENTS_BYTE_BUDGET)
69942
+ findings.push({
69943
+ rule: "agents-budget",
69944
+ file: doc.file,
69945
+ line: null,
69946
+ message: `${bytes} bytes, over the ${AGENTS_BYTE_BUDGET}-byte budget; move detail behind a pointer or into a check, and name what each addition removes`
69947
+ });
69948
+ }
69949
+ for (const doc of needBased)
69950
+ if (scaffoldOnly(doc))
69951
+ findings.push({
69952
+ rule: "scaffold-file",
69953
+ file: doc.file,
69954
+ line: null,
69955
+ message: "holds no entry (headings, comments or placeholders only); delete it, or create it in the same edit as its first real entry"
69956
+ });
69957
+ const tracked = trackedTopLevel(root);
69958
+ for (const doc of [...steering, ...needBased])
69959
+ findings.push(...brokenLinks(root, doc, tracked));
69960
+ const standards = needBased.find((doc) => doc.file === STANDARDS_FILE);
69961
+ if (standards)
69962
+ findings.push(...duplicateRules(steering, standards));
69963
+ return {
69964
+ root,
69965
+ budget: AGENTS_BYTE_BUDGET,
69966
+ files: [...steering, ...needBased].map((doc) => ({
69967
+ file: doc.file,
69968
+ bytes: Buffer.byteLength(doc.text)
69969
+ })),
69970
+ findings,
69971
+ unreadable
69972
+ };
69973
+ }
69974
+ var AGENTS_BYTE_BUDGET, STEERING_FILES, NEED_BASED_FILES, STANDARDS_FILE = "CODING_STANDARDS.md", MIN_RULE_WORDS = 6, FENCE, HEADING, TABLE_SEPARATOR, LIST_ITEM, PLACEHOLDER2, INDENTED_CODE, blank = (line) => (line ?? "").trim() === "", proseLines = (lines) => {
69975
+ const out = [];
69976
+ let fence = null;
69977
+ let comment = false;
69978
+ let indentedCode = false;
69979
+ let inList = false;
69980
+ lines.forEach((raw, index) => {
69981
+ if (fence) {
69982
+ const close = FENCE.exec(raw);
69983
+ if (close && close[1][0] === fence[0] && close[1].length >= fence.length && !close[2].trim())
69984
+ fence = null;
69985
+ return;
69986
+ }
69987
+ if (!comment) {
69988
+ const open = FENCE.exec(raw);
69989
+ if (open && !(open[1][0] === "`" && open[2].includes("`"))) {
69990
+ fence = open[1];
69991
+ indentedCode = false;
69992
+ return;
69993
+ }
69994
+ if (INDENTED_CODE.test(raw) && !blank(raw)) {
69995
+ if (indentedCode || !inList && blank(lines[index - 1])) {
69996
+ indentedCode = true;
69997
+ return;
69998
+ }
69999
+ } else if (!blank(raw)) {
70000
+ indentedCode = false;
70001
+ inList = LIST_ITEM.test(raw) || inList && /^\s/.test(raw);
70002
+ }
70003
+ }
70004
+ let text = raw;
70005
+ if (comment) {
70006
+ const end = text.indexOf("-->");
70007
+ if (end === -1)
70008
+ return;
70009
+ comment = false;
70010
+ text = text.slice(end + 3);
70011
+ }
70012
+ text = text.replace(/<!--[\s\S]*?-->/g, "");
70013
+ const open = text.indexOf("<!--");
70014
+ if (open !== -1) {
70015
+ comment = true;
70016
+ text = text.slice(0, open);
70017
+ }
70018
+ out.push({ index, text });
70019
+ });
70020
+ return out;
70021
+ }, withoutFrontMatter = (lines) => {
70022
+ if (lines[0]?.trim() !== "---")
70023
+ return lines;
70024
+ const end = lines.indexOf("---", 1);
70025
+ return end === -1 ? lines : [...lines.slice(0, end + 1).map(() => ""), ...lines.slice(end + 1)];
70026
+ }, read = (root, file) => {
70027
+ const full = path36.join(root, file);
70028
+ try {
70029
+ fs17.lstatSync(full);
70030
+ } catch {
70031
+ return null;
70032
+ }
70033
+ try {
70034
+ if (!fs17.statSync(full).isFile())
70035
+ return null;
70036
+ const text = fs17.readFileSync(full, "utf8");
70037
+ return { file, text, lines: text.split(/\r?\n/), real: fs17.realpathSync(full) };
70038
+ } catch {
70039
+ return "unreadable";
70040
+ }
70041
+ }, trackedTopLevel = (root) => {
70042
+ const run = spawnSync14("git", ["ls-files", "-z", "--cached"], {
70043
+ cwd: root,
70044
+ encoding: "utf8",
70045
+ maxBuffer: 256 * 1024 * 1024,
70046
+ timeout: 20000,
70047
+ windowsHide: true
70048
+ });
70049
+ if (run.status !== 0)
70050
+ return null;
70051
+ return new Set(run.stdout.split("\x00").filter(Boolean).map((file) => file.split("/")[0]));
70052
+ }, exists = (target) => {
70053
+ try {
70054
+ fs17.statSync(target);
70055
+ return true;
70056
+ } catch {
70057
+ return false;
70058
+ }
70059
+ }, LINK, REFERENCE_DEFINITION, CODE_SPAN, PATH_LIKE, isExternal = (target) => /^[a-z][a-z0-9+.-]*:/i.test(target) || target.startsWith("#") || target.startsWith("//"), localTarget = (target) => {
70060
+ const bare = target.replace(/[#?].*$/, "");
70061
+ try {
70062
+ return decodeURIComponent(bare);
70063
+ } catch {
70064
+ return bare;
70065
+ }
70066
+ }, brokenLinks = (root, doc, tracked) => {
70067
+ const findings = [];
70068
+ const base = path36.dirname(path36.join(root, doc.file));
70069
+ const missing = (target, line, how) => findings.push({
70070
+ rule: "broken-link",
70071
+ file: doc.file,
70072
+ line,
70073
+ message: `${how} \`${target}\` points at a missing file`
70074
+ });
70075
+ for (const { index, text } of proseLines(doc.lines)) {
70076
+ const prose = text.replace(CODE_SPAN, (span) => " ".repeat(span.length));
70077
+ const targets = [...prose.matchAll(LINK)].map((match) => match[1] ?? match[2]);
70078
+ const definition = REFERENCE_DEFINITION.exec(prose)?.[1];
70079
+ if (definition)
70080
+ targets.push(definition);
70081
+ for (const target of targets) {
70082
+ if (isExternal(target))
70083
+ continue;
70084
+ const local = localTarget(target);
70085
+ if (!local)
70086
+ continue;
70087
+ const resolved = local.startsWith("/") ? path36.join(root, local) : path36.resolve(base, local);
70088
+ if (!exists(resolved))
70089
+ missing(target, index + 1, "link");
70090
+ }
70091
+ for (const [, span] of text.matchAll(CODE_SPAN)) {
70092
+ const candidate = span.replace(/:\d+(?::\d+)?$/, "");
70093
+ if (!PATH_LIKE.test(candidate))
70094
+ continue;
70095
+ const relative = /^\.\.?\//.test(candidate);
70096
+ const first = candidate.split("/")[0];
70097
+ if (!relative && !(tracked ? tracked.has(first) : exists(path36.join(root, first))))
70098
+ continue;
70099
+ const resolved = relative ? path36.resolve(base, candidate) : path36.join(root, candidate);
70100
+ if (!exists(resolved))
70101
+ missing(span, index + 1, "pointer");
70102
+ }
70103
+ }
70104
+ return findings;
70105
+ }, isPlaceholder2 = (text) => PLACEHOLDER2.test(text.replace(LIST_ITEM, "").replace(/[*_`]/g, "").replace(/^\[\s?\]\s*/, "").trim()), scaffoldOnly = (doc) => {
70106
+ const lines = proseLines(withoutFrontMatter(doc.lines));
70107
+ return lines.every(({ text }, position) => {
70108
+ const trimmed = text.trim();
70109
+ if (trimmed === "" || HEADING.test(text) || /^([-*_])(\s*\1){2,}$/.test(trimmed))
70110
+ return true;
70111
+ if (TABLE_SEPARATOR.test(text))
70112
+ return true;
70113
+ const next = lines[position + 1]?.text ?? "";
70114
+ if (trimmed.startsWith("|") && TABLE_SEPARATOR.test(next))
70115
+ return true;
70116
+ if (trimmed.startsWith("|"))
70117
+ return trimmed.split("|").map((cell) => cell.trim()).every((cell) => cell === "" || isPlaceholder2(cell));
70118
+ return isPlaceholder2(trimmed);
70119
+ });
70120
+ }, ruleUnits = (doc) => {
70121
+ const units = [];
70122
+ let current = null;
70123
+ const flush = () => {
70124
+ if (current)
70125
+ units.push(current);
70126
+ current = null;
70127
+ };
70128
+ for (const { index, text } of proseLines(doc.lines)) {
70129
+ const trimmed = text.trim();
70130
+ if (trimmed === "" || HEADING.test(text) || trimmed.startsWith("|")) {
70131
+ flush();
70132
+ continue;
70133
+ }
70134
+ if (LIST_ITEM.test(text) || !current) {
70135
+ flush();
70136
+ current = { line: index + 1, text: trimmed.replace(LIST_ITEM, "") };
70137
+ } else
70138
+ current.text += ` ${trimmed}`;
70139
+ }
70140
+ flush();
70141
+ return units;
70142
+ }, normalize = (sentence) => sentence.replace(/\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/[*_`]/g, "").toLowerCase().replace(/\s+/g, " ").replace(/[\s.,;:!?]+$/, "").trim(), sentences = (doc) => {
70143
+ const out = new Map;
70144
+ for (const unit of ruleUnits(doc))
70145
+ for (const sentence of unit.text.split(/(?<=[.!?])\s+/)) {
70146
+ const key = normalize(sentence);
70147
+ if (key.split(" ").length >= MIN_RULE_WORDS && !out.has(key))
70148
+ out.set(key, unit.line);
70149
+ }
70150
+ return out;
70151
+ }, duplicateRules = (steering, standards) => {
70152
+ const findings = [];
70153
+ const rules = sentences(standards);
70154
+ for (const doc of steering)
70155
+ for (const [key, line] of sentences(doc)) {
70156
+ const standardsLine = rules.get(key);
70157
+ if (standardsLine === undefined)
70158
+ continue;
70159
+ findings.push({
70160
+ rule: "duplicate-rule",
70161
+ file: doc.file,
70162
+ line,
70163
+ message: `repeats ${standards.file}:${standardsLine} ("${key.slice(0, 60)}${key.length > 60 ? "…" : ""}"); keep the rule in one file`
70164
+ });
70165
+ }
70166
+ return findings;
70167
+ };
70168
+ var init_knowledge = __esm(() => {
70169
+ AGENTS_BYTE_BUDGET = 8 * 1024;
70170
+ STEERING_FILES = ["AGENTS.md", "CLAUDE.md"];
70171
+ NEED_BASED_FILES = ["CODING_STANDARDS.md", "GLOSSARY.md"];
70172
+ FENCE = /^ {0,3}(`{3,}|~{3,})(.*)$/;
70173
+ HEADING = /^\s{0,3}#{1,6}(\s|$)/;
70174
+ TABLE_SEPARATOR = /^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?\s*$/;
70175
+ LIST_ITEM = /^\s*(?:[-*+]|\d+[.)])\s+/;
70176
+ PLACEHOLDER2 = /^(?:(?:tbd|todo|tba|fixme)\b.*|n\/a|none|none yet|coming soon|placeholder|\.\.\.|…|-)?[.:]?$/i;
70177
+ INDENTED_CODE = /^(?: {4}|\t)/;
70178
+ LINK = /!?\[[^\]]*\]\(\s*(?:<([^>\n]+)>|((?:[^()\s<>]|\([^()\s]*\))+))(?:\s+["'(][^)]*)?\)/g;
70179
+ REFERENCE_DEFINITION = /^\s{0,3}\[[^\]]+\]:\s*<?(\S+?)>?(?:\s|$)/;
70180
+ CODE_SPAN = /`([^`\n]+)`/g;
70181
+ PATH_LIKE = /^(?:\.{1,2}\/)?[\w@.-]+(?:\/[\w@.-]+)+\/?$/;
70182
+ });
70183
+
69919
70184
  // packages/workit-cli/src/prompt.ts
69920
70185
  import { createInterface } from "node:readline/promises";
69921
70186
  async function askLine(question, output = process.stdout) {
@@ -69999,8 +70264,23 @@ async function run5(argv, io) {
69999
70264
  fixLock = clearStaleMetadataLock(root, { force });
70000
70265
  }
70001
70266
  const report = runDoctor({ host: "cli", cwd: io.cwd, workspaceRoot: root });
70267
+ let knowledge = null;
70268
+ try {
70269
+ knowledge = lintKnowledge(root);
70270
+ if (knowledge.unreadable.length > 0)
70271
+ knowledge = null;
70272
+ } catch {
70273
+ knowledge = null;
70274
+ }
70275
+ const agents = knowledge?.files.find((file) => file.file === "AGENTS.md");
70276
+ const knowledgeLine = knowledge ? `info knowledge — ${agents ? `AGENTS.md ${agents.bytes} of ${knowledge.budget} bytes` : "no AGENTS.md"}; ${knowledge.findings.length} finding(s) (workit knowledge lint)` : "info knowledge — unavailable";
70002
70277
  if (argv.includes("--json")) {
70003
- io.stdout(`${JSON.stringify(fixLock ? { ...report, fixLock } : report, null, 2)}
70278
+ const summary = knowledge ? {
70279
+ agentsBytes: agents?.bytes ?? null,
70280
+ budget: knowledge.budget,
70281
+ findings: knowledge.findings.length
70282
+ } : { unavailable: true };
70283
+ io.stdout(`${JSON.stringify({ ...report, ...fixLock ? { fixLock } : {}, knowledge: summary }, null, 2)}
70004
70284
  `);
70005
70285
  return report.exitCode;
70006
70286
  }
@@ -70019,12 +70299,15 @@ async function run5(argv, io) {
70019
70299
  io.stdout(` fix: ${check.fix}
70020
70300
  `);
70021
70301
  }
70302
+ io.stdout(`${knowledgeLine}
70303
+ `);
70022
70304
  io.stdout(`passed ${report.summary.passed} / warned ${report.summary.warned} / failed ${report.summary.failed}
70023
70305
  `);
70024
70306
  return report.exitCode;
70025
70307
  }
70026
70308
  var init_doctor2 = __esm(() => {
70027
70309
  init_doctor();
70310
+ init_knowledge();
70028
70311
  init_store_lock();
70029
70312
  init_output();
70030
70313
  init_prompt();
@@ -70135,11 +70418,11 @@ var init_redact = __esm(() => {
70135
70418
  });
70136
70419
 
70137
70420
  // packages/workit-core/src/checks.ts
70138
- import { spawn, spawnSync as spawnSync14 } from "node:child_process";
70421
+ import { spawn, spawnSync as spawnSync15 } from "node:child_process";
70139
70422
  import { createHash as createHash14 } from "node:crypto";
70140
- import fs17 from "node:fs";
70423
+ import fs18 from "node:fs";
70141
70424
  import os13 from "node:os";
70142
- import path36 from "node:path";
70425
+ import path37 from "node:path";
70143
70426
  import { StringDecoder } from "node:string_decoder";
70144
70427
  function tailLines(text, lines = TAIL_LINES) {
70145
70428
  const all = text.replace(/\r\n?/gu, `
@@ -70259,10 +70542,10 @@ function planSpawn(argv, options) {
70259
70542
  return { file: argv[0], args: [], verbatim: false, shell: true };
70260
70543
  if (options.platform !== "win32")
70261
70544
  return { file: argv[0], args: argv.slice(1), verbatim: false, shell: false };
70262
- const win = path36.win32;
70545
+ const win = path37.win32;
70263
70546
  const isFile = options.isFile ?? ((file) => {
70264
70547
  try {
70265
- return fs17.statSync(file).isFile();
70548
+ return fs18.statSync(file).isFile();
70266
70549
  } catch {
70267
70550
  return false;
70268
70551
  }
@@ -70314,7 +70597,7 @@ function runCheckCommand(argv, options) {
70314
70597
  if (!child?.pid)
70315
70598
  return;
70316
70599
  if (windows)
70317
- spawnSync14("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
70600
+ spawnSync15("taskkill", ["/pid", String(child.pid), "/T", "/F"], {
70318
70601
  stdio: "ignore",
70319
70602
  windowsHide: true
70320
70603
  });
@@ -70416,13 +70699,13 @@ function runCheckCommand(argv, options) {
70416
70699
  function storeLog(storeRoot, log) {
70417
70700
  const digest = logDigest(log);
70418
70701
  const ref = `blobs/logs/${digest.slice("sha256:".length)}.log`;
70419
- const file = path36.join(storeRoot, ref);
70702
+ const file = path37.join(storeRoot, ref);
70420
70703
  try {
70421
- if (!fs17.existsSync(file)) {
70422
- fs17.mkdirSync(path36.dirname(file), { recursive: true });
70704
+ if (!fs18.existsSync(file)) {
70705
+ fs18.mkdirSync(path37.dirname(file), { recursive: true });
70423
70706
  const temp = `${file}.${process.pid}.${Date.now()}.tmp`;
70424
- fs17.writeFileSync(temp, log, { mode: 384 });
70425
- fs17.renameSync(temp, file);
70707
+ fs18.writeFileSync(temp, log, { mode: 384 });
70708
+ fs18.renameSync(temp, file);
70426
70709
  }
70427
70710
  return { digest, ref };
70428
70711
  } catch {
@@ -70432,17 +70715,17 @@ function storeLog(storeRoot, log) {
70432
70715
  function pruneCheckLogs(storeRoot, options = {}) {
70433
70716
  const limits = { ...LOG_RETENTION, ...options.retention };
70434
70717
  const now = options.now ?? Date.now();
70435
- const dir = path36.join(storeRoot, "blobs", "logs");
70718
+ const dir = path37.join(storeRoot, "blobs", "logs");
70436
70719
  const report = { removed: 0, removedBytes: 0, kept: 0 };
70437
70720
  let names;
70438
70721
  try {
70439
- names = fs17.readdirSync(dir);
70722
+ names = fs18.readdirSync(dir);
70440
70723
  } catch {
70441
70724
  return report;
70442
70725
  }
70443
70726
  const files = names.flatMap((name) => {
70444
70727
  try {
70445
- const stat = fs17.statSync(path36.join(dir, name));
70728
+ const stat = fs18.statSync(path37.join(dir, name));
70446
70729
  return stat.isFile() ? [{ name, size: stat.size, mtimeMs: stat.mtimeMs }] : [];
70447
70730
  } catch {
70448
70731
  return [];
@@ -70461,7 +70744,7 @@ function pruneCheckLogs(storeRoot, options = {}) {
70461
70744
  continue;
70462
70745
  if (!options.dryRun)
70463
70746
  try {
70464
- fs17.rmSync(path36.join(dir, file.name), { force: true });
70747
+ fs18.rmSync(path37.join(dir, file.name), { force: true });
70465
70748
  } catch {
70466
70749
  continue;
70467
70750
  }
@@ -70712,7 +70995,7 @@ var exports_check = {};
70712
70995
  __export(exports_check, {
70713
70996
  run: () => run9
70714
70997
  });
70715
- import path37 from "node:path";
70998
+ import path38 from "node:path";
70716
70999
  function parse5(argv) {
70717
71000
  const split = argv.indexOf("--");
70718
71001
  const head = split >= 0 ? argv.slice(0, split) : [...argv];
@@ -70895,7 +71178,7 @@ async function run9(argv, io) {
70895
71178
  if (options.shell && command.length !== 1)
70896
71179
  return emit(io, fail("invalid_input", '--shell takes the command as one string: workit check --shell -- "<cmd>"'));
70897
71180
  }
70898
- const relCwd = posix(path37.relative(top, runCwd));
71181
+ const relCwd = posix(path38.relative(top, runCwd));
70899
71182
  const configured = !options.shell && matchesNamedCheck(config, name, command, relCwd);
70900
71183
  const actor = actorFromEnv(io.env);
70901
71184
  const branchKey = codeKey(io.cwd, { base: options.base });
@@ -71019,7 +71302,7 @@ async function run9(argv, io) {
71019
71302
  return recordError.code === "busy" ? 4 : recordError.code === "invalid_input" ? 2 : 5;
71020
71303
  return 0;
71021
71304
  }
71022
- var USAGE3 = "workit check <name> | workit check [--name <n>] [--shell] [--timeout <s>] [--task <id>] [--json] -- <cmd…>", RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(path37.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS;
71305
+ var USAGE3 = "workit check <name> | workit check [--name <n>] [--shell] [--timeout <s>] [--task <id>] [--json] -- <cmd…>", RECORD_TAIL_LINES = 20, RECORD_TAIL_CHARS = 200, posix = (value) => value.split(path38.sep).join("/") || ".", ENGINE_CODES2, FINGERPRINT_VARS;
71023
71306
  var init_check = __esm(() => {
71024
71307
  init_check_config();
71025
71308
  init_checks3();
@@ -71040,7 +71323,7 @@ var init_check = __esm(() => {
71040
71323
  var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
71041
71324
 
71042
71325
  // packages/workit-core/src/git/ops.ts
71043
- import { spawnSync as spawnSync15 } from "node:child_process";
71326
+ import { spawnSync as spawnSync16 } from "node:child_process";
71044
71327
  function dirtEntries(cwd) {
71045
71328
  const run = git5(cwd, ["status", "--porcelain=v1", "-z", "--untracked-files=all"]);
71046
71329
  if (!run.ok)
@@ -71384,7 +71667,7 @@ ${run.stdout}`;
71384
71667
  return /stale info|cannot lock ref|fetch first|non-fast-forward/iu.test(text) ? { ok: false, lease: true, error: "the remote branch tip changed before deletion" } : { ok: false, lease: false, error: gitError(run) };
71385
71668
  }
71386
71669
  var COMMIT_TIMEOUT_MS, PUSH_TIMEOUT_MS = 120000, git5 = (cwd, args, options = {}) => {
71387
- const result = spawnSync15("git", args, {
71670
+ const result = spawnSync16("git", args, {
71388
71671
  cwd,
71389
71672
  encoding: "utf8",
71390
71673
  env: { ...options.env ?? process.env, GIT_TERMINAL_PROMPT: "0" },
@@ -71420,11 +71703,11 @@ var init_ops = __esm(() => {
71420
71703
  });
71421
71704
 
71422
71705
  // packages/workit-core/src/forge/reruns.ts
71423
- import fs18 from "node:fs";
71424
- import path38 from "node:path";
71706
+ import fs19 from "node:fs";
71707
+ import path39 from "node:path";
71425
71708
  function rerunLogPath(cwd) {
71426
71709
  const common = gitCommonDir(cwd);
71427
- return common ? path38.join(common, "workit", "ci-reruns.jsonl") : null;
71710
+ return common ? path39.join(common, "workit", "ci-reruns.jsonl") : null;
71428
71711
  }
71429
71712
  function rerunCounts(cwd, key) {
71430
71713
  const counts = new Map;
@@ -71433,7 +71716,7 @@ function rerunCounts(cwd, key) {
71433
71716
  return counts;
71434
71717
  let text;
71435
71718
  try {
71436
- text = fs18.readFileSync(file, "utf8");
71719
+ text = fs19.readFileSync(file, "utf8");
71437
71720
  } catch {
71438
71721
  return counts;
71439
71722
  }
@@ -71459,8 +71742,8 @@ function recordReruns(cwd, rows) {
71459
71742
  if (!file)
71460
71743
  return false;
71461
71744
  try {
71462
- fs18.mkdirSync(path38.dirname(file), { recursive: true });
71463
- fs18.appendFileSync(file, rows.map((row) => `${JSON.stringify(row)}
71745
+ fs19.mkdirSync(path39.dirname(file), { recursive: true });
71746
+ fs19.appendFileSync(file, rows.map((row) => `${JSON.stringify(row)}
71464
71747
  `).join(""));
71465
71748
  return true;
71466
71749
  } catch {
@@ -71472,18 +71755,18 @@ function withRerunLock(cwd, fn) {
71472
71755
  if (!file)
71473
71756
  return fn();
71474
71757
  const lock = `${file.slice(0, -".jsonl".length)}.lock`;
71475
- fs18.mkdirSync(path38.dirname(lock), { recursive: true });
71758
+ fs19.mkdirSync(path39.dirname(lock), { recursive: true });
71476
71759
  const until = Date.now() + LOCK_WAIT_MS2;
71477
71760
  for (;; ) {
71478
71761
  try {
71479
- fs18.closeSync(fs18.openSync(lock, "wx"));
71762
+ fs19.closeSync(fs19.openSync(lock, "wx"));
71480
71763
  break;
71481
71764
  } catch (error) {
71482
71765
  if (error.code !== "EEXIST")
71483
71766
  throw error;
71484
71767
  try {
71485
- if (Date.now() - fs18.statSync(lock).mtimeMs > LOCK_STALE_MS2) {
71486
- fs18.rmSync(lock, { force: true });
71768
+ if (Date.now() - fs19.statSync(lock).mtimeMs > LOCK_STALE_MS2) {
71769
+ fs19.rmSync(lock, { force: true });
71487
71770
  continue;
71488
71771
  }
71489
71772
  } catch {
@@ -71497,7 +71780,7 @@ function withRerunLock(cwd, fn) {
71497
71780
  try {
71498
71781
  return fn();
71499
71782
  } finally {
71500
- fs18.rmSync(lock, { force: true });
71783
+ fs19.rmSync(lock, { force: true });
71501
71784
  }
71502
71785
  }
71503
71786
  var isRow = (value) => {
@@ -71865,7 +72148,7 @@ var init_report = __esm(() => {
71865
72148
  });
71866
72149
 
71867
72150
  // packages/workit-core/src/forge/pr-ops.ts
71868
- import { spawnSync as spawnSync16 } from "node:child_process";
72151
+ import { spawnSync as spawnSync17 } from "node:child_process";
71869
72152
  function fillFromCommits(cwd, resolved, base) {
71870
72153
  const tracking = `refs/remotes/${resolved.baseRemote}/${base}`;
71871
72154
  if (!base.startsWith("-"))
@@ -71873,7 +72156,7 @@ function fillFromCommits(cwd, resolved, base) {
71873
72156
  const from = resolveRef(cwd, tracking) ?? resolveRef(cwd, `refs/heads/${base}`);
71874
72157
  if (!from)
71875
72158
  return failure3("not_found", `base ${base} is not available locally`, `git fetch ${resolved.baseRemote} ${base}`);
71876
- const log = spawnSync16("git", ["log", "--reverse", "--no-merges", "--format=%s%x1f%b%x1e", `${from}..HEAD`], {
72159
+ const log = spawnSync17("git", ["log", "--reverse", "--no-merges", "--format=%s%x1f%b%x1e", `${from}..HEAD`], {
71877
72160
  cwd,
71878
72161
  encoding: "utf8",
71879
72162
  timeout: GIT_TIMEOUTS.local,
@@ -72148,17 +72431,17 @@ var init_pr_ops = __esm(() => {
72148
72431
  });
72149
72432
 
72150
72433
  // packages/workit-core/src/forge/exec.ts
72151
- import { spawnSync as spawnSync17 } from "node:child_process";
72152
- import fs19 from "node:fs";
72153
- import path39 from "node:path";
72434
+ import { spawnSync as spawnSync18 } from "node:child_process";
72435
+ import fs20 from "node:fs";
72436
+ import path40 from "node:path";
72154
72437
  function findExecutable(bin, env) {
72155
- const dirs = (env.PATH ?? env.Path ?? "").split(path39.delimiter).filter(Boolean);
72438
+ const dirs = (env.PATH ?? env.Path ?? "").split(path40.delimiter).filter(Boolean);
72156
72439
  const exts = process.platform === "win32" ? [".exe"] : [""];
72157
72440
  for (const dir of dirs)
72158
72441
  for (const ext of exts) {
72159
- const candidate = path39.join(dir, `${bin}${ext}`);
72442
+ const candidate = path40.join(dir, `${bin}${ext}`);
72160
72443
  try {
72161
- const stat = fs19.statSync(candidate);
72444
+ const stat = fs20.statSync(candidate);
72162
72445
  if (stat.isFile())
72163
72446
  return candidate;
72164
72447
  } catch {}
@@ -72183,7 +72466,7 @@ function systemRunner(kind, apiHost, env = process.env) {
72183
72466
  if (!exe)
72184
72467
  return { status: null, stdout: "", stderr: "", timedOut: false, missing: true };
72185
72468
  const token = options.token ? kind === "github" ? { GH_TOKEN: options.token, GH_ENTERPRISE_TOKEN: options.token } : { GITLAB_TOKEN: options.token } : {};
72186
- const result = spawnSync17(exe, [...args], {
72469
+ const result = spawnSync18(exe, [...args], {
72187
72470
  encoding: "utf8",
72188
72471
  env: { ...childEnv, ...token },
72189
72472
  maxBuffer: MAX_BUFFER2,
@@ -72853,7 +73136,7 @@ var init_gitlab = __esm(() => {
72853
73136
  });
72854
73137
 
72855
73138
  // packages/workit-core/src/forge/resolve.ts
72856
- import fs20 from "node:fs";
73139
+ import fs21 from "node:fs";
72857
73140
  function resolveForge(cwd, options = {}) {
72858
73141
  let workspace;
72859
73142
  try {
@@ -72882,7 +73165,7 @@ function resolveForge(cwd, options = {}) {
72882
73165
  if (derived.via === "host_name" && !explicit)
72883
73166
  return failure3("blocked", `token_host_unverified: push host ${derived.host} only looks like ${kind}; workspace ${workspace?.name ?? "?"} vcs.tokenFile is not sent to it`, `if ${apiHost} is your ${kind} server, set ${kind}.host in ~/.config/workit/vcs.json (or vcs.host for the workspace); otherwise fix the push remote`);
72884
73167
  try {
72885
- token = fs20.readFileSync(tokenFile.trim(), "utf8").trim();
73168
+ token = fs21.readFileSync(tokenFile.trim(), "utf8").trim();
72886
73169
  } catch {
72887
73170
  return failure3("blocked", `workspace ${workspace?.name ?? "?"} vcs.tokenFile cannot be read`, "fix vcs.tokenFile in ~/.config/workit/workspaces.json");
72888
73171
  }
@@ -73114,7 +73397,7 @@ __export(exports_pr, {
73114
73397
  run: () => run10
73115
73398
  });
73116
73399
  import { readFileSync as readFileSync18 } from "node:fs";
73117
- import path40 from "node:path";
73400
+ import path41 from "node:path";
73118
73401
  async function status(argv, io) {
73119
73402
  const flags = parseFlags(argv, { pr: "value", branch: "value", "log-lines": "value" });
73120
73403
  if (typeof flags === "string")
@@ -73185,7 +73468,7 @@ async function create4(argv, io) {
73185
73468
  let body = flags.values.body ?? null;
73186
73469
  if (flags.values["body-file"] !== undefined) {
73187
73470
  try {
73188
- body = readFileSync18(path40.resolve(io.cwd, flags.values["body-file"]), "utf8");
73471
+ body = readFileSync18(path41.resolve(io.cwd, flags.values["body-file"]), "utf8");
73189
73472
  } catch {
73190
73473
  return usage3(io, `cannot read --body-file ${flags.values["body-file"]}`, CREATE_USAGE);
73191
73474
  }
@@ -73677,7 +73960,7 @@ var init_git2 = __esm(() => {
73677
73960
  });
73678
73961
 
73679
73962
  // packages/workit-core/src/forge/verify.ts
73680
- import { spawnSync as spawnSync18 } from "node:child_process";
73963
+ import { spawnSync as spawnSync19 } from "node:child_process";
73681
73964
  function finish(cwd, report, actor) {
73682
73965
  const delivered = report.observations.length > 0 && report.observations.every((entry) => entry.ok);
73683
73966
  const out = { ...report, delivered };
@@ -73890,7 +74173,7 @@ var NPM_NAME, SEMVER, SAFE_ARG, systemNpm = (args) => {
73890
74173
  };
73891
74174
  const found = process.platform === "win32" ? findExecutable("npm.cmd", process.env) : null;
73892
74175
  const npmCmd = found && !/["%!^]/u.test(found) ? `"${found}"` : "npm.cmd";
73893
- const result = process.platform === "win32" ? spawnSync18(`${npmCmd} ${args.join(" ")}`, { ...options, shell: true }) : spawnSync18("npm", [...args], options);
74176
+ const result = process.platform === "win32" ? spawnSync19(`${npmCmd} ${args.join(" ")}`, { ...options, shell: true }) : spawnSync19("npm", [...args], options);
73894
74177
  return { status: result.status, stdout: result.stdout ?? "", stderr: result.stderr ?? "" };
73895
74178
  }, short3 = (sha) => sha ? sha.slice(0, 12) : "(absent)", npmView = (stdout) => {
73896
74179
  try {
@@ -73997,10 +74280,10 @@ var init_verify_delivery = __esm(() => {
73997
74280
  });
73998
74281
 
73999
74282
  // packages/workit-core/src/stack.ts
74000
- import { spawnSync as spawnSync19 } from "node:child_process";
74283
+ import { spawnSync as spawnSync20 } from "node:child_process";
74001
74284
  import { createHash as createHash15, randomBytes as randomBytes3 } from "node:crypto";
74002
- import fs21 from "node:fs";
74003
- import path41 from "node:path";
74285
+ import fs22 from "node:fs";
74286
+ import path42 from "node:path";
74004
74287
  function worktreeOf(cwd, branch) {
74005
74288
  const list = gitRun3(cwd, ["worktree", "list", "--porcelain"]);
74006
74289
  if (!list.ok)
@@ -74023,7 +74306,7 @@ function rebasingBranch2(dir) {
74023
74306
  if (!found.ok)
74024
74307
  continue;
74025
74308
  try {
74026
- return fs21.readFileSync(found.stdout.trim(), "utf8").trim();
74309
+ return fs22.readFileSync(found.stdout.trim(), "utf8").trim();
74027
74310
  } catch {}
74028
74311
  }
74029
74312
  return null;
@@ -74031,7 +74314,7 @@ function rebasingBranch2(dir) {
74031
74314
  function rebaseInProgress(dir) {
74032
74315
  for (const name of ["rebase-merge", "rebase-apply"]) {
74033
74316
  const found = gitRun3(dir, ["rev-parse", "--path-format=absolute", "--git-path", name]);
74034
- if (found.ok && fs21.existsSync(found.stdout.trim()))
74317
+ if (found.ok && fs22.existsSync(found.stdout.trim()))
74035
74318
  return true;
74036
74319
  }
74037
74320
  return false;
@@ -74043,8 +74326,8 @@ function gitRestack(cwd, input) {
74043
74326
  const scratch = scratchDir(cwd);
74044
74327
  if (!scratch)
74045
74328
  return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
74046
- fs21.mkdirSync(scratch, { recursive: true });
74047
- dir = path41.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes3(4).toString("hex")}`);
74329
+ fs22.mkdirSync(scratch, { recursive: true });
74330
+ dir = path42.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes3(4).toString("hex")}`);
74048
74331
  const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
74049
74332
  if (!added.ok)
74050
74333
  return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
@@ -74082,10 +74365,10 @@ function gitRestack(cwd, input) {
74082
74365
  }
74083
74366
  function pruneScratch(cwd) {
74084
74367
  const scratch = scratchDir(cwd);
74085
- if (!scratch || !fs21.existsSync(scratch))
74368
+ if (!scratch || !fs22.existsSync(scratch))
74086
74369
  return;
74087
- for (const name of fs21.readdirSync(scratch)) {
74088
- const dir = path41.join(scratch, name);
74370
+ for (const name of fs22.readdirSync(scratch)) {
74371
+ const dir = path42.join(scratch, name);
74089
74372
  if (!rebaseInProgress(dir) && !trackedDirt(dir))
74090
74373
  gitRun3(cwd, ["worktree", "remove", "--force", dir]);
74091
74374
  }
@@ -74095,7 +74378,7 @@ function stacksDir(cwd) {
74095
74378
  const root = storeRoot(cwd);
74096
74379
  if (!root.ok)
74097
74380
  return stackFail(root.code, root.error, root.unblock);
74098
- return { ok: true, data: path41.join(root.value.root, "stacks") };
74381
+ return { ok: true, data: path42.join(root.value.root, "stacks") };
74099
74382
  }
74100
74383
  function parseStack(raw) {
74101
74384
  if (!isRecord7(raw) || !Array.isArray(raw.branches))
@@ -74147,10 +74430,10 @@ function readStack(cwd, name) {
74147
74430
  const dir = stacksDir(cwd);
74148
74431
  if (!dir.ok)
74149
74432
  return dir;
74150
- const file = path41.join(dir.data, fileFor(name));
74433
+ const file = path42.join(dir.data, fileFor(name));
74151
74434
  let text;
74152
74435
  try {
74153
- text = fs21.readFileSync(file, "utf8");
74436
+ text = fs22.readFileSync(file, "utf8");
74154
74437
  } catch (error) {
74155
74438
  if (error.code === "ENOENT")
74156
74439
  return { ok: true, data: null };
@@ -74168,17 +74451,17 @@ function writeStack(cwd, stack, now = new Date) {
74168
74451
  const dir = stacksDir(cwd);
74169
74452
  if (!dir.ok)
74170
74453
  return dir;
74171
- const file = path41.join(dir.data, fileFor(stack.name));
74454
+ const file = path42.join(dir.data, fileFor(stack.name));
74172
74455
  const temp = `${file}.${process.pid}.tmp`;
74173
74456
  try {
74174
- fs21.mkdirSync(dir.data, { recursive: true });
74457
+ fs22.mkdirSync(dir.data, { recursive: true });
74175
74458
  stack.updatedAt = now.toISOString();
74176
- fs21.writeFileSync(temp, `${JSON.stringify(stack, null, 2)}
74459
+ fs22.writeFileSync(temp, `${JSON.stringify(stack, null, 2)}
74177
74460
  `);
74178
- fs21.renameSync(temp, file);
74461
+ fs22.renameSync(temp, file);
74179
74462
  return { ok: true, data: undefined };
74180
74463
  } catch (error) {
74181
- fs21.rmSync(temp, { force: true });
74464
+ fs22.rmSync(temp, { force: true });
74182
74465
  return stackFail("unavailable", `cannot write ${file}: ${error.message}`);
74183
74466
  }
74184
74467
  }
@@ -74188,7 +74471,7 @@ function listStacks(cwd) {
74188
74471
  return dir;
74189
74472
  let names = [];
74190
74473
  try {
74191
- names = fs21.readdirSync(dir.data).filter((name) => name.endsWith(".json"));
74474
+ names = fs22.readdirSync(dir.data).filter((name) => name.endsWith(".json"));
74192
74475
  } catch {
74193
74476
  return { ok: true, data: [] };
74194
74477
  }
@@ -74196,7 +74479,7 @@ function listStacks(cwd) {
74196
74479
  for (const name of names) {
74197
74480
  let parsed = null;
74198
74481
  try {
74199
- parsed = parseStack(JSON.parse(fs21.readFileSync(path41.join(dir.data, name), "utf8")));
74482
+ parsed = parseStack(JSON.parse(fs22.readFileSync(path42.join(dir.data, name), "utf8")));
74200
74483
  } catch {
74201
74484
  parsed = null;
74202
74485
  }
@@ -74243,7 +74526,7 @@ function stackLockStale(raw, age) {
74243
74526
  }
74244
74527
  function takeLock(lock) {
74245
74528
  try {
74246
- fs21.mkdirSync(lock);
74529
+ fs22.mkdirSync(lock);
74247
74530
  } catch (error) {
74248
74531
  if (error.code === "EEXIST")
74249
74532
  return null;
@@ -74255,24 +74538,24 @@ function takeLock(lock) {
74255
74538
  host: localLockHost(),
74256
74539
  nonce: randomBytes3(8).toString("hex")
74257
74540
  };
74258
- fs21.writeFileSync(path41.join(lock, OWNER), JSON.stringify(owner));
74541
+ fs22.writeFileSync(path42.join(lock, OWNER), JSON.stringify(owner));
74259
74542
  return owner;
74260
74543
  }
74261
74544
  function reapLock(lock, observed) {
74262
74545
  const tomb = `${lock}.reap-${randomBytes3(6).toString("hex")}`;
74263
74546
  try {
74264
- fs21.renameSync(lock, tomb);
74547
+ fs22.renameSync(lock, tomb);
74265
74548
  } catch {
74266
74549
  return false;
74267
74550
  }
74268
74551
  if (readOwner(tomb).raw === observed) {
74269
- fs21.rmSync(tomb, { recursive: true, force: true });
74552
+ fs22.rmSync(tomb, { recursive: true, force: true });
74270
74553
  return true;
74271
74554
  }
74272
74555
  try {
74273
- fs21.renameSync(tomb, lock);
74556
+ fs22.renameSync(tomb, lock);
74274
74557
  } catch {
74275
- fs21.rmSync(tomb, { recursive: true, force: true });
74558
+ fs22.rmSync(tomb, { recursive: true, force: true });
74276
74559
  }
74277
74560
  return false;
74278
74561
  }
@@ -74282,8 +74565,8 @@ function releaseLock(lock, owner) {
74282
74565
  return;
74283
74566
  const tomb = `${lock}.release-${randomBytes3(6).toString("hex")}`;
74284
74567
  try {
74285
- fs21.renameSync(lock, tomb);
74286
- fs21.rmSync(tomb, { recursive: true, force: true });
74568
+ fs22.renameSync(lock, tomb);
74569
+ fs22.rmSync(tomb, { recursive: true, force: true });
74287
74570
  } catch {}
74288
74571
  }
74289
74572
  async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
@@ -74292,9 +74575,9 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
74292
74575
  return dir;
74293
74576
  if (!validName(name))
74294
74577
  return stackFail("invalid_input", "a stack name must be 1-200 printable characters");
74295
- const lock = path41.join(dir.data, `${fileFor(name)}.lock`);
74578
+ const lock = path42.join(dir.data, `${fileFor(name)}.lock`);
74296
74579
  try {
74297
- fs21.mkdirSync(dir.data, { recursive: true });
74580
+ fs22.mkdirSync(dir.data, { recursive: true });
74298
74581
  } catch (error) {
74299
74582
  return stackFail("unavailable", `cannot create ${dir.data}: ${error.message}`);
74300
74583
  }
@@ -74322,7 +74605,7 @@ async function withStackLock(cwd, name, fn, waitMs = LOCK_WAIT_MS3) {
74322
74605
  const touch = () => {
74323
74606
  try {
74324
74607
  const now = new Date;
74325
- fs21.utimesSync(path41.join(lock, OWNER), now, now);
74608
+ fs22.utimesSync(path42.join(lock, OWNER), now, now);
74326
74609
  } catch {}
74327
74610
  };
74328
74611
  const beat = setInterval(touch, HEARTBEAT_MS);
@@ -74510,7 +74793,7 @@ function commitKeys(cwd, range) {
74510
74793
  const patches = new Map;
74511
74794
  const diff = gitRun3(cwd, ["log", "--no-merges", "-p", "--format=commit %H", range]);
74512
74795
  if (diff.ok && diff.stdout.trim()) {
74513
- const ids = spawnSync19("git", ["patch-id", "--stable"], {
74796
+ const ids = spawnSync20("git", ["patch-id", "--stable"], {
74514
74797
  cwd,
74515
74798
  input: diff.stdout,
74516
74799
  encoding: "utf8",
@@ -75118,7 +75401,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
75118
75401
  ...unblock ? { unblock } : {},
75119
75402
  ...data ? { data } : {}
75120
75403
  }), fromForge = (result) => stackFail(result.code, result.error, result.unblock), stackDeps, gitRun3 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
75121
- const run = spawnSync19("git", args, {
75404
+ const run = spawnSync20("git", args, {
75122
75405
  cwd,
75123
75406
  encoding: "utf8",
75124
75407
  timeout: timeoutMs,
@@ -75146,7 +75429,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
75146
75429
  return !status.ok || status.stdout.trim() !== "";
75147
75430
  }, scratchDir = (cwd) => {
75148
75431
  const dir = stacksDir(cwd);
75149
- return dir.ok ? path41.join(dir.data, "worktrees") : null;
75432
+ return dir.ok ? path42.join(dir.data, "worktrees") : null;
75150
75433
  }, firstLine = (run) => (`${run.stderr}
75151
75434
  ${run.stdout}`.split(`
75152
75435
  `).find((line) => line.trim()) ?? "git failed").trim().slice(0, 300), stackFileName = (name) => `${name.replace(/[^A-Za-z0-9._-]+/gu, "_").replace(/^[._]+/u, "").slice(0, 48).toLowerCase()}-${createHash15("sha256").update(name).digest("hex").slice(0, 12)}.json`, fileFor, validName = (name) => {
@@ -75161,14 +75444,14 @@ ${run.stdout}`.split(`
75161
75444
  }, isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), str3 = (value) => typeof value === "string" && value ? value : null, LOCK_WAIT_MS3 = 2000, HEARTBEAT_MS = 20000, HEARTBEAT_STALE_MS, OWNERLESS_STALE_MS = 30000, OWNER = "owner", readOwner = (lock) => {
75162
75445
  let raw = null;
75163
75446
  try {
75164
- raw = fs21.readFileSync(path41.join(lock, OWNER), "utf8");
75447
+ raw = fs22.readFileSync(path42.join(lock, OWNER), "utf8");
75165
75448
  } catch {
75166
75449
  raw = null;
75167
75450
  }
75168
75451
  let age = null;
75169
- for (const target of [path41.join(lock, OWNER), lock])
75452
+ for (const target of [path42.join(lock, OWNER), lock])
75170
75453
  try {
75171
- age = Date.now() - fs21.statSync(target).mtimeMs;
75454
+ age = Date.now() - fs22.statSync(target).mtimeMs;
75172
75455
  break;
75173
75456
  } catch {}
75174
75457
  return { raw, age };
@@ -75408,9 +75691,9 @@ var init_stack2 = __esm(() => {
75408
75691
  });
75409
75692
 
75410
75693
  // packages/workit-core/src/fanout.ts
75411
- import { spawnSync as spawnSync20 } from "node:child_process";
75412
- import fs22 from "node:fs";
75413
- import path42 from "node:path";
75694
+ import { spawnSync as spawnSync21 } from "node:child_process";
75695
+ import fs23 from "node:fs";
75696
+ import path43 from "node:path";
75414
75697
  function scopeSamples(pattern) {
75415
75698
  if (!hasWildcard(pattern))
75416
75699
  return [unescape2(pattern)];
@@ -75607,7 +75890,7 @@ function parsePlan(raw, defaults, name) {
75607
75890
  id,
75608
75891
  branch,
75609
75892
  base: optional("base") ?? "",
75610
- worktree: optional("worktree") ?? path42.join(defaults.worktreeRoot, id),
75893
+ worktree: optional("worktree") ?? path43.join(defaults.worktreeRoot, id),
75611
75894
  tier,
75612
75895
  dependsOn: [...new Set(dependsOn ?? [])],
75613
75896
  scope,
@@ -75807,14 +76090,14 @@ function defaultTrunk(cwd) {
75807
76090
  }
75808
76091
  function worktreeRoot(cwd) {
75809
76092
  const common = gitCommonDir(cwd);
75810
- const main = common && path42.basename(common) === ".git" ? path42.dirname(common) : cwd;
75811
- return path42.join(path42.dirname(main), `${path42.basename(main)}-wt`);
76093
+ const main = common && path43.basename(common) === ".git" ? path43.dirname(common) : cwd;
76094
+ return path43.join(path43.dirname(main), `${path43.basename(main)}-wt`);
75812
76095
  }
75813
76096
  function fanoutsDir(cwd) {
75814
76097
  const root = storeRoot(cwd);
75815
76098
  if (!root.ok)
75816
76099
  return fanoutFail(root.code, root.error, root.unblock);
75817
- return { ok: true, data: path42.join(root.value.root, "fanouts") };
76100
+ return { ok: true, data: path43.join(root.value.root, "fanouts") };
75818
76101
  }
75819
76102
  function parseStored(raw) {
75820
76103
  if (!isRecord8(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
@@ -75871,10 +76154,10 @@ function readFanout(cwd, name) {
75871
76154
  const dir = fanoutsDir(cwd);
75872
76155
  if (!dir.ok)
75873
76156
  return dir;
75874
- const file = path42.join(dir.data, stackFileName(name));
76157
+ const file = path43.join(dir.data, stackFileName(name));
75875
76158
  let parsed = null;
75876
76159
  try {
75877
- parsed = parseStored(JSON.parse(fs22.readFileSync(file, "utf8")));
76160
+ parsed = parseStored(JSON.parse(fs23.readFileSync(file, "utf8")));
75878
76161
  } catch (error) {
75879
76162
  if (error.code === "ENOENT")
75880
76163
  return { ok: true, data: null };
@@ -75888,7 +76171,7 @@ function listFanouts(cwd) {
75888
76171
  return dir;
75889
76172
  let names = [];
75890
76173
  try {
75891
- names = fs22.readdirSync(dir.data).filter((entry) => entry.endsWith(".json"));
76174
+ names = fs23.readdirSync(dir.data).filter((entry) => entry.endsWith(".json"));
75892
76175
  } catch {
75893
76176
  return { ok: true, data: [] };
75894
76177
  }
@@ -75896,7 +76179,7 @@ function listFanouts(cwd) {
75896
76179
  for (const entry of names) {
75897
76180
  let parsed = null;
75898
76181
  try {
75899
- parsed = parseStored(JSON.parse(fs22.readFileSync(path42.join(dir.data, entry), "utf8")));
76182
+ parsed = parseStored(JSON.parse(fs23.readFileSync(path43.join(dir.data, entry), "utf8")));
75900
76183
  } catch {
75901
76184
  parsed = null;
75902
76185
  }
@@ -75926,16 +76209,16 @@ function writeFanout(cwd, plan) {
75926
76209
  const dir = fanoutsDir(cwd);
75927
76210
  if (!dir.ok)
75928
76211
  return dir;
75929
- const file = path42.join(dir.data, stackFileName(plan.name));
76212
+ const file = path43.join(dir.data, stackFileName(plan.name));
75930
76213
  const temp = `${file}.${process.pid}.tmp`;
75931
76214
  try {
75932
- fs22.mkdirSync(dir.data, { recursive: true });
75933
- fs22.writeFileSync(temp, `${JSON.stringify(plan, null, 2)}
76215
+ fs23.mkdirSync(dir.data, { recursive: true });
76216
+ fs23.writeFileSync(temp, `${JSON.stringify(plan, null, 2)}
75934
76217
  `);
75935
- fs22.renameSync(temp, file);
76218
+ fs23.renameSync(temp, file);
75936
76219
  return { ok: true, data: file };
75937
76220
  } catch (error) {
75938
- fs22.rmSync(temp, { force: true });
76221
+ fs23.rmSync(temp, { force: true });
75939
76222
  return fanoutFail("unavailable", `cannot write ${file}: ${error.message}`);
75940
76223
  }
75941
76224
  }
@@ -76014,7 +76297,7 @@ var FANOUT_VERSION = 1, TIERS, fanoutFail = (code, error, unblock, data) => ({
76014
76297
  error,
76015
76298
  ...unblock ? { unblock } : {},
76016
76299
  ...data ? { data } : {}
76017
- }), DEFAULT_SHARED, escapeRegExp4 = (text) => text.replace(/[.+^$()|[\]\\{}*?]/gu, "\\$&"), unescape2 = (pattern) => pattern.replace(/\\(.)/gu, "$1"), hasWildcard = (pattern) => /[*?{]/u.test(pattern.replace(/\\./gu, "")), compiled, matchesAny = (patterns, file) => patterns.some((pattern) => matchesScope(pattern, file)), isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), PLACEHOLDER2, filled = (text) => text.trim() !== "" && !PLACEHOLDER2.test(text.trim()), textList = (value) => {
76300
+ }), DEFAULT_SHARED, escapeRegExp4 = (text) => text.replace(/[.+^$()|[\]\\{}*?]/gu, "\\$&"), unescape2 = (pattern) => pattern.replace(/\\(.)/gu, "$1"), hasWildcard = (pattern) => /[*?{]/u.test(pattern.replace(/\\./gu, "")), compiled, matchesAny = (patterns, file) => patterns.some((pattern) => matchesScope(pattern, file)), isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), PLACEHOLDER3, filled = (text) => text.trim() !== "" && !PLACEHOLDER3.test(text.trim()), textList = (value) => {
76018
76301
  if (typeof value === "string")
76019
76302
  return [value];
76020
76303
  if (Array.isArray(value) && value.every((item) => typeof item === "string"))
@@ -76033,7 +76316,7 @@ var FANOUT_VERSION = 1, TIERS, fanoutFail = (code, error, unblock, data) => ({
76033
76316
  text: edges.map((edge) => `add "${edge.dependsOn}" to "dependsOn" of slice ${edge.slice}`).join("; ")
76034
76317
  };
76035
76318
  }, gitRun4 = (cwd, args) => {
76036
- const run = spawnSync20("git", args, {
76319
+ const run = spawnSync21("git", args, {
76037
76320
  cwd,
76038
76321
  encoding: "utf8",
76039
76322
  timeout: GIT_TIMEOUTS.local,
@@ -76065,7 +76348,7 @@ var init_fanout = __esm(() => {
76065
76348
  "CHANGELOG.md"
76066
76349
  ];
76067
76350
  compiled = new Map;
76068
- PLACEHOLDER2 = /^(?:<[^>]*>|tbd|todo|fixme|\?+|\.\.\.|…)$/iu;
76351
+ PLACEHOLDER3 = /^(?:<[^>]*>|tbd|todo|fixme|\?+|\.\.\.|…)$/iu;
76069
76352
  ID = /^[a-z0-9][a-z0-9._-]{0,63}$/u;
76070
76353
  });
76071
76354
 
@@ -76255,8 +76538,8 @@ var exports_fanout = {};
76255
76538
  __export(exports_fanout, {
76256
76539
  run: () => run15
76257
76540
  });
76258
- import fs23 from "node:fs";
76259
- import path43 from "node:path";
76541
+ import fs24 from "node:fs";
76542
+ import path44 from "node:path";
76260
76543
  function renderBlockedPlan(io, result) {
76261
76544
  if (io.json || result.code !== "blocked" && result.code !== "invalid_input")
76262
76545
  return;
@@ -76301,10 +76584,10 @@ function plan2(argv, io) {
76301
76584
  return usage3(io, "pass --trunk or --track, not both (--track picks the trunk)", PLAN_USAGE2);
76302
76585
  if (flags.positionals.length !== 1)
76303
76586
  return usage3(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <plan.json>", PLAN_USAGE2);
76304
- const file = path43.resolve(io.cwd, flags.positionals[0]);
76587
+ const file = path44.resolve(io.cwd, flags.positionals[0]);
76305
76588
  let raw;
76306
76589
  try {
76307
- raw = JSON.parse(fs23.readFileSync(file, "utf8"));
76590
+ raw = JSON.parse(fs24.readFileSync(file, "utf8"));
76308
76591
  } catch (error) {
76309
76592
  return usage3(io, `cannot read ${file} as JSON: ${error.message}`, PLAN_USAGE2);
76310
76593
  }
@@ -91532,7 +91815,7 @@ var init_ast = __esm(() => {
91532
91815
 
91533
91816
  // packages/workit-cli/src/test-audit/rules.ts
91534
91817
  import { builtinModules } from "node:module";
91535
- import path44 from "node:path";
91818
+ import path45 from "node:path";
91536
91819
  function testCallee(call) {
91537
91820
  let callee = call.callee;
91538
91821
  if (callee.type === "CallExpression")
@@ -92025,12 +92308,12 @@ function checkConstantsOnly(ctx, emit) {
92025
92308
  }
92026
92309
  function checkOverMocking(ctx, emit) {
92027
92310
  const { program } = ctx.parsed;
92028
- const unit = path44.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
92311
+ const unit = path45.basename(ctx.file).replace(/\.(?:[cm]?[jt]sx?)$/, "").replace(/\.(?:test|spec)$/, "");
92029
92312
  const mocked = find2(program, (node) => node.type === "CallExpression" && MODULE_MOCK.test(calleeName(node.callee) ?? "") && node.arguments[0]?.type === "StringLiteral");
92030
92313
  const internal = mocked.filter((call) => /^(?:\.|@\/|~\/)/.test(call.arguments[0].value));
92031
92314
  for (const call of mocked) {
92032
92315
  const spec = call.arguments[0].value;
92033
- const base = path44.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
92316
+ const base = path45.basename(spec.replace(/\/index(?:\.[cm]?[jt]sx?)?$/, "")).replace(/\.(?:[cm]?[jt]sx?)$/, "");
92034
92317
  if (base === unit)
92035
92318
  emit({
92036
92319
  rule: "over-mocking",
@@ -92418,11 +92701,11 @@ var init_rules = __esm(() => {
92418
92701
  });
92419
92702
 
92420
92703
  // packages/workit-cli/src/test-audit/audit.ts
92421
- import { spawnSync as spawnSync21 } from "node:child_process";
92704
+ import { spawnSync as spawnSync22 } from "node:child_process";
92422
92705
  import { existsSync as existsSync18, readdirSync as readdirSync8, readFileSync as readFileSync19, statSync as statSync7 } from "node:fs";
92423
- import path45 from "node:path";
92706
+ import path46 from "node:path";
92424
92707
  function git6(cwd, args) {
92425
- const run = spawnSync21("git", ["-c", "core.quotepath=off", ...args], {
92708
+ const run = spawnSync22("git", ["-c", "core.quotepath=off", ...args], {
92426
92709
  cwd,
92427
92710
  encoding: "utf8",
92428
92711
  maxBuffer: 64 * 1024 * 1024
@@ -92433,7 +92716,7 @@ function walkTests(dir, out) {
92433
92716
  for (const entry of readdirSync8(dir, { withFileTypes: true })) {
92434
92717
  if (entry.name.startsWith(".") && entry.name !== "." && entry.isDirectory())
92435
92718
  continue;
92436
- const full = path45.join(dir, entry.name);
92719
+ const full = path46.join(dir, entry.name);
92437
92720
  if (entry.isDirectory()) {
92438
92721
  if (!SKIP_DIRS.has(entry.name))
92439
92722
  walkTests(full, out);
@@ -92445,7 +92728,7 @@ function expandPaths(cwd, inputs) {
92445
92728
  const files = [];
92446
92729
  const missing = [];
92447
92730
  for (const input of inputs.length > 0 ? inputs : ["."]) {
92448
- const full = path45.resolve(cwd, input);
92731
+ const full = path46.resolve(cwd, input);
92449
92732
  if (!existsSync18(full))
92450
92733
  missing.push(input);
92451
92734
  else if (statSync7(full).isDirectory())
@@ -92482,11 +92765,11 @@ function changedLines(cwd, base) {
92482
92765
  for (let offset = 0;offset < count; offset += 1)
92483
92766
  lines.add(start + offset);
92484
92767
  }
92485
- changed.set(path45.join(root, file), lines);
92768
+ changed.set(path46.join(root, file), lines);
92486
92769
  }
92487
92770
  const untracked = git6(root, ["ls-files", "--others", "--exclude-standard", "-z"]);
92488
92771
  for (const file of untracked.stdout.split("\x00").filter(Boolean))
92489
- changed.set(path45.join(root, file), "all");
92772
+ changed.set(path46.join(root, file), "all");
92490
92773
  return changed;
92491
92774
  }
92492
92775
  function auditFiles(files, display) {
@@ -92548,7 +92831,7 @@ function auditFiles(files, display) {
92548
92831
  durationMs: Math.round(performance.now() - started)
92549
92832
  };
92550
92833
  }
92551
- var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path45.sep).join("/"), SEVERITY_ORDER, ruleIds;
92834
+ var TEST_FILE, CODE_FILE, SKIP_DIRS, toPosix = (file) => file.split(path46.sep).join("/"), SEVERITY_ORDER, ruleIds;
92552
92835
  var init_audit = __esm(() => {
92553
92836
  init_ast();
92554
92837
  init_rules();
@@ -92578,7 +92861,7 @@ import {
92578
92861
  copyFileSync as copyFileSync6
92579
92862
  } from "node:fs";
92580
92863
  import os14 from "node:os";
92581
- import path46 from "node:path";
92864
+ import path47 from "node:path";
92582
92865
  function mutantsFor(source, file, lines) {
92583
92866
  const parsed = parseSource(source, file);
92584
92867
  const out = [];
@@ -92651,7 +92934,7 @@ function relatedTests(source, testFiles) {
92651
92934
  for (const match of text.matchAll(SPECIFIER)) {
92652
92935
  const spec = match[1];
92653
92936
  if (spec.startsWith(".")) {
92654
- if (stripExt(path46.posix.join(path46.posix.dirname(file), spec)) === target)
92937
+ if (stripExt(path47.posix.join(path47.posix.dirname(file), spec)) === target)
92655
92938
  return true;
92656
92939
  } else if (spec.includes("/") && stripExt(spec).split("/").slice(-2).join("/") === tail)
92657
92940
  return true;
@@ -92671,12 +92954,12 @@ function removeStaleCopies(tmp = os14.tmpdir()) {
92671
92954
  for (const name of names) {
92672
92955
  if (!name.startsWith(TEMP_PREFIX))
92673
92956
  continue;
92674
- const dir = path46.join(tmp, name);
92957
+ const dir = path47.join(tmp, name);
92675
92958
  try {
92676
92959
  const stat = lstatSync7(dir);
92677
92960
  if (!stat.isDirectory() || uid !== undefined && stat.uid !== uid)
92678
92961
  continue;
92679
- const pid = Number(readFileSync20(path46.join(dir, PID_FILE), "utf8"));
92962
+ const pid = Number(readFileSync20(path47.join(dir, PID_FILE), "utf8"));
92680
92963
  if (Number.isInteger(pid) && pid > 0 && alive(pid))
92681
92964
  continue;
92682
92965
  } catch (error) {
@@ -92693,8 +92976,8 @@ function removeStaleCopies(tmp = os14.tmpdir()) {
92693
92976
  function linkNodeModules(origin, target, repo, copy) {
92694
92977
  mkdirSync9(target, { recursive: true });
92695
92978
  for (const entry of readdirSync9(origin, { withFileTypes: true })) {
92696
- const from = path46.join(origin, entry.name);
92697
- const to = path46.join(target, entry.name);
92979
+ const from = path47.join(origin, entry.name);
92980
+ const to = path47.join(target, entry.name);
92698
92981
  if (entry.name.startsWith("@") && entry.isDirectory()) {
92699
92982
  linkNodeModules(from, to, repo, copy);
92700
92983
  continue;
@@ -92710,9 +92993,9 @@ function linkNodeModules(origin, target, repo, copy) {
92710
92993
  } catch {
92711
92994
  continue;
92712
92995
  }
92713
- const relative = path46.relative(repo, real);
92714
- if (!relative.startsWith("..") && !path46.isAbsolute(relative) && !relative.split(path46.sep).includes("node_modules")) {
92715
- symlinkSync(path46.join(copy, relative), to, "junction");
92996
+ const relative = path47.relative(repo, real);
92997
+ if (!relative.startsWith("..") && !path47.isAbsolute(relative) && !relative.split(path47.sep).includes("node_modules")) {
92998
+ symlinkSync(path47.join(copy, relative), to, "junction");
92716
92999
  continue;
92717
93000
  }
92718
93001
  }
@@ -92728,7 +93011,7 @@ function copySymlink(repo, tree, from, to) {
92728
93011
  }
92729
93012
  const realRepo = realpathSync9(repo);
92730
93013
  if (real && inside2(realRepo, real)) {
92731
- symlinkSync(path46.relative(path46.dirname(to), path46.join(tree, path46.relative(realRepo, real))), to);
93014
+ symlinkSync(path47.relative(path47.dirname(to), path47.join(tree, path47.relative(realRepo, real))), to);
92732
93015
  return;
92733
93016
  }
92734
93017
  if (real && statSync8(real).isFile())
@@ -92737,10 +93020,10 @@ function copySymlink(repo, tree, from, to) {
92737
93020
  symlinkSync(readlinkSync3(from), to);
92738
93021
  }
92739
93022
  function writeInside(copy, file, content) {
92740
- const target = path46.join(copy, file);
93023
+ const target = path47.join(copy, file);
92741
93024
  try {
92742
93025
  const stat = lstatSync7(target);
92743
- if (!stat.isFile() || !inside2(realpathSync9(copy), realpathSync9(path46.dirname(target))))
93026
+ if (!stat.isFile() || !inside2(realpathSync9(copy), realpathSync9(path47.dirname(target))))
92744
93027
  return false;
92745
93028
  } catch {
92746
93029
  return false;
@@ -92751,9 +93034,9 @@ function writeInside(copy, file, content) {
92751
93034
  return true;
92752
93035
  }
92753
93036
  function copyWorkingTree(repo) {
92754
- const dir = mkdtempSync4(path46.join(os14.tmpdir(), TEMP_PREFIX));
92755
- writeFileSync9(path46.join(dir, PID_FILE), String(process.pid));
92756
- const tree = path46.join(dir, "tree");
93037
+ const dir = mkdtempSync4(path47.join(os14.tmpdir(), TEMP_PREFIX));
93038
+ writeFileSync9(path47.join(dir, PID_FILE), String(process.pid));
93039
+ const tree = path47.join(dir, "tree");
92757
93040
  const head = git6(repo, ["rev-parse", "--verify", "--quiet", "HEAD"]).stdout.trim();
92758
93041
  if (head && git6(dir, ["clone", "-q", "--shared", "--no-checkout", repo, tree]).ok) {
92759
93042
  const branch = git6(repo, ["symbolic-ref", "--quiet", "--short", "HEAD"]).stdout.trim();
@@ -92770,29 +93053,29 @@ function copyWorkingTree(repo) {
92770
93053
  for (const relative of listed) {
92771
93054
  if (!relative)
92772
93055
  continue;
92773
- const from = path46.join(repo, relative);
93056
+ const from = path47.join(repo, relative);
92774
93057
  let stat;
92775
93058
  try {
92776
93059
  stat = lstatSync7(from);
92777
93060
  } catch {
92778
93061
  continue;
92779
93062
  }
92780
- const to = path46.join(tree, relative);
92781
- mkdirSync9(path46.dirname(to), { recursive: true });
93063
+ const to = path47.join(tree, relative);
93064
+ mkdirSync9(path47.dirname(to), { recursive: true });
92782
93065
  if (stat.isSymbolicLink())
92783
93066
  copySymlink(repo, tree, from, to);
92784
93067
  else if (stat.isFile())
92785
93068
  copyFileSync6(from, to);
92786
- let parent = path46.dirname(relative);
93069
+ let parent = path47.dirname(relative);
92787
93070
  while (parent !== "." && !dirs.has(parent)) {
92788
93071
  dirs.add(parent);
92789
- parent = path46.dirname(parent);
93072
+ parent = path47.dirname(parent);
92790
93073
  }
92791
93074
  }
92792
93075
  for (const parent of dirs) {
92793
- const modules = path46.join(repo, parent, "node_modules");
92794
- if (existsSync19(modules) && !existsSync19(path46.join(tree, parent, "node_modules")))
92795
- linkNodeModules(modules, path46.join(tree, parent, "node_modules"), repo, tree);
93076
+ const modules = path47.join(repo, parent, "node_modules");
93077
+ if (existsSync19(modules) && !existsSync19(path47.join(tree, parent, "node_modules")))
93078
+ linkNodeModules(modules, path47.join(tree, parent, "node_modules"), repo, tree);
92796
93079
  }
92797
93080
  return { dir, tree };
92798
93081
  }
@@ -92831,14 +93114,14 @@ async function runMutation(options) {
92831
93114
  const tracked = git6(repo, ["ls-files", "-co", "--exclude-standard", "-z"]).stdout.split("\x00").filter(Boolean);
92832
93115
  const testFiles = tracked.filter((file) => TEST_FILE.test(file)).map((file) => {
92833
93116
  try {
92834
- return { file, text: readFileSync20(path46.join(repo, file), "utf8") };
93117
+ return { file, text: readFileSync20(path47.join(repo, file), "utf8") };
92835
93118
  } catch {
92836
93119
  return { file, text: "" };
92837
93120
  }
92838
93121
  });
92839
93122
  const candidates = [];
92840
93123
  for (const [absolute, lines] of [...options.changed].toSorted(([a], [b]) => a.localeCompare(b))) {
92841
- const relative = toPosix(path46.relative(repo, absolute));
93124
+ const relative = toPosix(path47.relative(repo, absolute));
92842
93125
  if (!CODE_FILE.test(relative) || TEST_FILE.test(relative) || relative.endsWith(".d.ts"))
92843
93126
  continue;
92844
93127
  if (NOT_SOURCE.test(relative) || !existsSync19(absolute))
@@ -92852,9 +93135,9 @@ async function runMutation(options) {
92852
93135
  for (const mutant of mutants)
92853
93136
  if (!related.has(mutant.file))
92854
93137
  related.set(mutant.file, relatedTests(mutant.file, testFiles));
92855
- const cwdRelative = path46.relative(repo, options.cwd);
93138
+ const cwdRelative = path47.relative(repo, options.cwd);
92856
93139
  const asArg = (file) => {
92857
- const relative = toPosix(path46.relative(cwdRelative, file));
93140
+ const relative = toPosix(path47.relative(cwdRelative, file));
92858
93141
  return shellQuote(relative.startsWith(".") ? relative : `./${relative}`);
92859
93142
  };
92860
93143
  const commandFor = (tests) => options.command.replaceAll("{files}", () => tests.map(asArg).join(" "));
@@ -92889,7 +93172,7 @@ async function runMutation(options) {
92889
93172
  return finish2(options, 0, results, counts, started);
92890
93173
  removeStaleCopies();
92891
93174
  const { dir: copyDir, tree: copy } = copyWorkingTree(repo);
92892
- const copyCwd = path46.join(copy, cwdRelative);
93175
+ const copyCwd = path47.join(copy, cwdRelative);
92893
93176
  let current = null;
92894
93177
  const restore = () => {
92895
93178
  if (current)
@@ -92924,7 +93207,7 @@ async function runMutation(options) {
92924
93207
  record({ ...mutant, status: "skipped", durationMs: 0, tests, reason: "budget" });
92925
93208
  continue;
92926
93209
  }
92927
- const original = readFileSync20(path46.join(copy, mutant.file), "utf8");
93210
+ const original = readFileSync20(path47.join(copy, mutant.file), "utf8");
92928
93211
  const mutated = original.slice(0, mutant.start) + mutant.replacement + original.slice(mutant.end);
92929
93212
  current = { file: mutant.file, original };
92930
93213
  if (!writeInside(copy, mutant.file, mutated)) {
@@ -92981,8 +93264,8 @@ var NOT_SOURCE, FLIPS, isStringy = (node) => node.type === "StringLiteral" || no
92981
93264
  return error.code === "EPERM";
92982
93265
  }
92983
93266
  }, inside2 = (root, file) => {
92984
- const relative = path46.relative(root, file);
92985
- return relative === "" || !relative.startsWith("..") && !path46.isAbsolute(relative);
93267
+ const relative = path47.relative(root, file);
93268
+ return relative === "" || !relative.startsWith("..") && !path47.isAbsolute(relative);
92986
93269
  }, active, killTree = (child) => {
92987
93270
  try {
92988
93271
  if (child.pid && process.platform !== "win32")
@@ -93031,7 +93314,7 @@ __export(exports_test_audit, {
93031
93314
  run: () => run17
93032
93315
  });
93033
93316
  import { existsSync as existsSync20 } from "node:fs";
93034
- import path47 from "node:path";
93317
+ import path48 from "node:path";
93035
93318
  function parse7(argv) {
93036
93319
  const options = {
93037
93320
  paths: [],
@@ -93131,8 +93414,8 @@ async function run17(argv, io) {
93131
93414
  return emit(io, fail("invalid_input", `${options}
93132
93415
  usage: ${USAGE10}`));
93133
93416
  const top = git6(io.cwd, ["rev-parse", "--show-toplevel"]);
93134
- const repo = top.ok ? path47.resolve(top.stdout.trim()) : null;
93135
- const display = (file) => toPosix(path47.relative(io.cwd, file) || file);
93417
+ const repo = top.ok ? path48.resolve(top.stdout.trim()) : null;
93418
+ const display = (file) => toPosix(path48.relative(io.cwd, file) || file);
93136
93419
  let base = null;
93137
93420
  let changed = null;
93138
93421
  if (options.diff || options.mutate) {
@@ -93233,6 +93516,38 @@ var init_test_audit = __esm(() => {
93233
93516
  ]);
93234
93517
  });
93235
93518
 
93519
+ // packages/workit-cli/src/verbs/knowledge.ts
93520
+ var exports_knowledge = {};
93521
+ __export(exports_knowledge, {
93522
+ run: () => run18
93523
+ });
93524
+ async function run18(argv, io) {
93525
+ const [sub, ...rest] = argv.filter((arg) => arg !== "--json");
93526
+ if (sub !== "lint" || rest.length > 0)
93527
+ return emit(io, fail("invalid_input", `usage: ${USAGE11}`));
93528
+ const report = lintKnowledge(checkRoot(io.cwd));
93529
+ if (report.unreadable.length > 0)
93530
+ return emit(io, fail("unavailable", `knowledge lint: cannot read ${report.unreadable.join(", ")}`, {
93531
+ data: report,
93532
+ unblock: `make ${report.unreadable.join(", ")} readable, then re-run workit knowledge lint`
93533
+ }));
93534
+ const summary = `knowledge lint: ${report.findings.length} finding${report.findings.length === 1 ? "" : "s"} in ${report.files.length} file${report.files.length === 1 ? "" : "s"} (${report.files.map((file) => `${file.file} ${file.bytes} B`).join(", ") || "no knowledge files"}; AGENTS.md budget ${report.budget} B)`;
93535
+ if (io.json)
93536
+ return emit(io, report.findings.length === 0 ? ok(report) : fail("failed", summary, { data: report }));
93537
+ for (const finding of report.findings)
93538
+ io.stdout(`${finding.file}${finding.line === null ? "" : `:${finding.line}`} [${finding.rule}] ${finding.message}
93539
+ `);
93540
+ io.stdout(`${summary}
93541
+ `);
93542
+ return report.findings.length === 0 ? 0 : 1;
93543
+ }
93544
+ var USAGE11 = "workit knowledge lint [--json]";
93545
+ var init_knowledge2 = __esm(() => {
93546
+ init_check_config();
93547
+ init_knowledge();
93548
+ init_output();
93549
+ });
93550
+
93236
93551
  // packages/workit-core/src/core/youtrack-tools.ts
93237
93552
  async function logTimeUpdate(input, operation = defaultOperations) {
93238
93553
  try {
@@ -93340,14 +93655,14 @@ var init_youtrack_writes = __esm(() => {
93340
93655
  // packages/workit-cli/src/verbs/youtrack.ts
93341
93656
  var exports_youtrack = {};
93342
93657
  __export(exports_youtrack, {
93343
- run: () => run18
93658
+ run: () => run19
93344
93659
  });
93345
93660
  import { readFileSync as readFileSync21 } from "node:fs";
93346
- import path48 from "node:path";
93347
- async function run18(argv, io) {
93661
+ import path49 from "node:path";
93662
+ async function run19(argv, io) {
93348
93663
  const [sub, ...rest] = argv;
93349
93664
  if (sub !== "note" && sub !== "time" && sub !== "meeting")
93350
- return usage3(io, `unknown subcommand "${sub ?? ""}"`, USAGE11);
93665
+ return usage3(io, `unknown subcommand "${sub ?? ""}"`, USAGE12);
93351
93666
  const flags = parseFlags(rest, {
93352
93667
  markdown: "value",
93353
93668
  file: "value",
@@ -93356,18 +93671,18 @@ async function run18(argv, io) {
93356
93671
  date: "value"
93357
93672
  });
93358
93673
  if (typeof flags === "string")
93359
- return usage3(io, flags, USAGE11);
93674
+ return usage3(io, flags, USAGE12);
93360
93675
  const [issueId, extra] = flags.positionals;
93361
93676
  if (!issueId || !ISSUE_RE2.test(issueId))
93362
- return usage3(io, "an issue id like ABC-123 is required", USAGE11);
93677
+ return usage3(io, "an issue id like ABC-123 is required", USAGE12);
93363
93678
  if (extra !== undefined)
93364
- return usage3(io, `unexpected argument ${extra}`, USAGE11);
93679
+ return usage3(io, `unexpected argument ${extra}`, USAGE12);
93365
93680
  const minutes = positiveInt(flags.values.minutes, "--minutes");
93366
93681
  if (typeof minutes === "string")
93367
- return usage3(io, minutes, USAGE11);
93682
+ return usage3(io, minutes, USAGE12);
93368
93683
  const date = youTrackWorkDateMs(flags.values.date ?? "auto");
93369
93684
  if ("error" in date)
93370
- return usage3(io, date.error, USAGE11);
93685
+ return usage3(io, date.error, USAGE12);
93371
93686
  const dateMs = date.data.dateMs;
93372
93687
  const markedDay = flags.values.date !== undefined && flags.values.date !== "auto" ? { dateMs } : {};
93373
93688
  const root = io.cwd;
@@ -93385,24 +93700,24 @@ async function run18(argv, io) {
93385
93700
  };
93386
93701
  if (sub === "note") {
93387
93702
  if (Boolean(flags.values.markdown) === Boolean(flags.values.file))
93388
- return usage3(io, "pass exactly one of --markdown or --file", USAGE11);
93703
+ return usage3(io, "pass exactly one of --markdown or --file", USAGE12);
93389
93704
  let markdown = flags.values.markdown ?? "";
93390
93705
  if (flags.values.file)
93391
93706
  try {
93392
- markdown = readFileSync21(path48.resolve(io.cwd, flags.values.file), "utf8");
93707
+ markdown = readFileSync21(path49.resolve(io.cwd, flags.values.file), "utf8");
93393
93708
  } catch (error) {
93394
- return usage3(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE11);
93709
+ return usage3(io, `cannot read ${flags.values.file}: ${String(error)}`, USAGE12);
93395
93710
  }
93396
93711
  if (!markdown.trim())
93397
- return usage3(io, "the note is empty", USAGE11);
93712
+ return usage3(io, "the note is empty", USAGE12);
93398
93713
  const missing = step("comment", "comments", { operation: "note", issueId, markdown }, markdown, async (text) => commentResult(await postComment(issueId, text, root))) ?? (minutes ? step("time", "workItems", { operation: "note.time", issueId, markdown, minutes, ...markedDay }, "workit update", (text) => logTimeUpdate({ issueId, minutes, text, dateMs, workspace_root: root })) : null);
93399
93714
  if (missing)
93400
93715
  return emit(io, fail("unavailable", missing));
93401
93716
  } else {
93402
93717
  if (!minutes)
93403
- return usage3(io, `${sub} needs --minutes <n>`, USAGE11);
93718
+ return usage3(io, `${sub} needs --minutes <n>`, USAGE12);
93404
93719
  if (sub === "meeting" && !flags.values.text)
93405
- return usage3(io, "meeting needs --text <what the meeting was>", USAGE11);
93720
+ return usage3(io, "meeting needs --text <what the meeting was>", USAGE12);
93406
93721
  const text = flags.values.text ?? "";
93407
93722
  const missing = step(sub, "workItems", { operation: sub, issueId, minutes, text, ...markedDay }, text, (marked) => logTimeUpdate({ issueId, minutes, text: marked, dateMs, workspace_root: root }));
93408
93723
  if (missing)
@@ -93431,7 +93746,7 @@ async function run18(argv, io) {
93431
93746
  ...data.steps.flatMap((entry) => entry.note ? [` note (${entry.step}): ${entry.note}`] : [])
93432
93747
  ]);
93433
93748
  }
93434
- var USAGE11 = "workit youtrack note <ISSUE> (--markdown <t> | --file <p>) [--minutes <n>] [--date auto|YYYY-MM-DD] | youtrack time <ISSUE> --minutes <n> [--text <t>] [--date …] | youtrack meeting <ISSUE> --minutes <n> --text <t> [--date …]", commentResult = (value) => {
93749
+ var USAGE12 = "workit youtrack note <ISSUE> (--markdown <t> | --file <p>) [--minutes <n>] [--date auto|YYYY-MM-DD] | youtrack time <ISSUE> --minutes <n> [--text <t>] [--date …] | youtrack meeting <ISSUE> --minutes <n> --text <t> [--date …]", commentResult = (value) => {
93435
93750
  try {
93436
93751
  return { ok: true, data: unwrap2(value) };
93437
93752
  } catch (error) {
@@ -93455,14 +93770,14 @@ var init_youtrack2 = __esm(() => {
93455
93770
  // packages/workit-cli/src/verbs/changelog.ts
93456
93771
  var exports_changelog = {};
93457
93772
  __export(exports_changelog, {
93458
- run: () => run19
93773
+ run: () => run20
93459
93774
  });
93460
93775
  import { readFileSync as readFileSync22 } from "node:fs";
93461
- import path49 from "node:path";
93462
- async function run19(argv, io) {
93776
+ import path50 from "node:path";
93777
+ async function run20(argv, io) {
93463
93778
  const [sub, ...rest] = argv;
93464
93779
  if (sub !== "apply")
93465
- return usage3(io, `unknown subcommand "${sub ?? ""}"`, USAGE12);
93780
+ return usage3(io, `unknown subcommand "${sub ?? ""}"`, USAGE13);
93466
93781
  const flags = parseFlags(rest, {
93467
93782
  entries: "value",
93468
93783
  path: "value",
@@ -93470,18 +93785,18 @@ async function run19(argv, io) {
93470
93785
  preview: "boolean"
93471
93786
  });
93472
93787
  if (typeof flags === "string")
93473
- return usage3(io, flags, USAGE12);
93788
+ return usage3(io, flags, USAGE13);
93474
93789
  if (flags.positionals.length)
93475
- return usage3(io, `unexpected argument ${flags.positionals[0]}`, USAGE12);
93790
+ return usage3(io, `unexpected argument ${flags.positionals[0]}`, USAGE13);
93476
93791
  const normalizeOnly = flags.booleans.has("normalize-only");
93477
93792
  if (!normalizeOnly && !flags.values.entries)
93478
- return usage3(io, "pass --entries <JSON> or --normalize-only", USAGE12);
93793
+ return usage3(io, "pass --entries <JSON> or --normalize-only", USAGE13);
93479
93794
  let entries;
93480
93795
  if (flags.values.entries)
93481
93796
  try {
93482
93797
  entries = readEntries(flags.values.entries, io.cwd);
93483
93798
  } catch (error) {
93484
- return usage3(io, `--entries is not valid JSON: ${String(error)}`, USAGE12);
93799
+ return usage3(io, `--entries is not valid JSON: ${String(error)}`, USAGE13);
93485
93800
  }
93486
93801
  const input = {
93487
93802
  entries,
@@ -93503,8 +93818,8 @@ async function run19(argv, io) {
93503
93818
  `changelog updated: ${String(applied.path ?? "CHANGELOG.md")}`
93504
93819
  ]);
93505
93820
  }
93506
- var USAGE12 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
93507
- const text = raw === "-" ? readFileSync22(0, "utf8") : raw.startsWith("@") ? readFileSync22(path49.resolve(cwd, raw.slice(1)), "utf8") : raw;
93821
+ var USAGE13 = "workit changelog apply (--entries <JSON|@file|-> | --normalize-only) [--path CHANGELOG.md] [--preview]", readEntries = (raw, cwd) => {
93822
+ const text = raw === "-" ? readFileSync22(0, "utf8") : raw.startsWith("@") ? readFileSync22(path50.resolve(cwd, raw.slice(1)), "utf8") : raw;
93508
93823
  return JSON.parse(text);
93509
93824
  };
93510
93825
  var init_changelog2 = __esm(() => {
@@ -93516,7 +93831,7 @@ var init_changelog2 = __esm(() => {
93516
93831
  // packages/workit-cli/src/verbs/handoff.ts
93517
93832
  var exports_handoff = {};
93518
93833
  __export(exports_handoff, {
93519
- run: () => run20
93834
+ run: () => run21
93520
93835
  });
93521
93836
  import { parseArgs as parseArgs2 } from "node:util";
93522
93837
  function human2(brief) {
@@ -93564,11 +93879,11 @@ async function resumeBrief(argv, io) {
93564
93879
  strict: true
93565
93880
  }));
93566
93881
  } catch (error) {
93567
- return emit(io, fail("invalid_input", error.message, { unblock: USAGE13 }));
93882
+ return emit(io, fail("invalid_input", error.message, { unblock: USAGE14 }));
93568
93883
  }
93569
93884
  const last = values.last === undefined ? 10 : Number(values.last);
93570
93885
  if (!Number.isInteger(last) || last < 1)
93571
- return emit(io, fail("invalid_input", "--last must be a positive integer", { unblock: USAGE13 }));
93886
+ return emit(io, fail("invalid_input", "--last must be a positive integer", { unblock: USAGE14 }));
93572
93887
  const built = buildHandoff(io.cwd, {
93573
93888
  last,
93574
93889
  note: values.note ?? null,
@@ -93586,13 +93901,13 @@ async function resumeBrief(argv, io) {
93586
93901
  }
93587
93902
  return emit(io, ok({ ...data, recorded }), human2);
93588
93903
  }
93589
- async function run20(argv, io) {
93904
+ async function run21(argv, io) {
93590
93905
  if (!argv.includes("--task"))
93591
93906
  return resumeBrief(argv, io);
93592
93907
  await Promise.resolve().then(() => init_task());
93593
93908
  return runTaskCommand(["handoff", ...argv]);
93594
93909
  }
93595
- var USAGE13 = "workit handoff [--note <text>] [--next <text>] [--record] [--last <n>] [--json]", short5 = (sha) => sha ? sha.slice(0, 12) : "no commit";
93910
+ var USAGE14 = "workit handoff [--note <text>] [--next <text>] [--record] [--last <n>] [--json]", short5 = (sha) => sha ? sha.slice(0, 12) : "no commit";
93596
93911
  var init_handoff = __esm(() => {
93597
93912
  init_ledger();
93598
93913
  init_output();
@@ -93601,7 +93916,7 @@ var init_handoff = __esm(() => {
93601
93916
  // packages/workit-cli/src/main.ts
93602
93917
  init_package();
93603
93918
  init_output();
93604
- import path50 from "node:path";
93919
+ import path51 from "node:path";
93605
93920
 
93606
93921
  // packages/workit-cli/src/verbs/registry.ts
93607
93922
  var TASK_FAMILY_NAMES = [
@@ -93734,6 +94049,13 @@ var VERBS = [
93734
94049
  summary: "Flag tautological and low-value tests with a suggested independent oracle; --mutate checks changed lines with diff-scoped mutation",
93735
94050
  load: () => Promise.resolve().then(() => (init_test_audit(), exports_test_audit))
93736
94051
  },
94052
+ {
94053
+ name: "knowledge",
94054
+ group: "delivery",
94055
+ usage: "workit knowledge lint [--json]",
94056
+ summary: "Lint agent knowledge files: AGENTS.md byte budget, broken local links, scaffold-only files, duplicated rules (exit 1 on findings)",
94057
+ load: () => Promise.resolve().then(() => (init_knowledge2(), exports_knowledge))
94058
+ },
93737
94059
  {
93738
94060
  name: "youtrack",
93739
94061
  group: "delivery",
@@ -93838,7 +94160,7 @@ var defaultIo = (json) => ({
93838
94160
  });
93839
94161
  async function main(argv, overrides = {}, options = {}) {
93840
94162
  const parsed = parseGlobals(argv);
93841
- const cwd = path50.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
94163
+ const cwd = path51.resolve(overrides.cwd ?? process.cwd(), parsed.cwd ?? ".");
93842
94164
  const io = { ...defaultIo(parsed.json), ...overrides, json: parsed.json, cwd };
93843
94165
  if (parsed.error)
93844
94166
  return emit(io, fail("invalid_input", parsed.error));