finitemonkey 0.1.0-next.29 → 0.1.0-next.30

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.
@@ -95,16 +95,16 @@ var nodeExec = (request) => {
95
95
  };
96
96
  const resolved = process.platform === "win32" && request.shell !== true ? resolveWindowsCommand(request.command, options.env, existsSync) : void 0;
97
97
  const result = request.shell === true ? spawnSync([request.command, ...request.args].join(" "), { ...options, shell: true }) : resolved?.shell === true ? spawnSync(windowsCommandLine(resolved, request.args), { ...options, shell: true }) : spawnSync(resolved?.command ?? request.command, [...request.args], options);
98
- const code = errnoCode(result.error);
98
+ const code2 = errnoCode(result.error);
99
99
  const exitCode = result.status;
100
100
  const shellMissing = request.shell === true && exitCode !== null && (SHELL_NOT_FOUND.has(exitCode) || exitCode === 1 && CMD_NOT_FOUND.test(text(result.stderr)));
101
- const notFound3 = code === "ENOENT" || shellMissing;
101
+ const notFound3 = code2 === "ENOENT" || shellMissing;
102
102
  return {
103
103
  found: !notFound3,
104
104
  exitCode,
105
105
  stdout: text(result.stdout),
106
106
  stderr: text(result.stderr),
107
- timedOut: code === "ETIMEDOUT",
107
+ timedOut: code2 === "ETIMEDOUT",
108
108
  durationMs: Date.now() - started
109
109
  };
110
110
  };
@@ -318,8 +318,8 @@ var nodeGitLines = (args2, cwd, onLine) => new Promise((resolve2) => {
318
318
  });
319
319
  const lines3 = createInterface({ input: child2.stdout, crlfDelay: Infinity });
320
320
  lines3.on("line", onLine);
321
- child2.on("close", (code) => {
322
- resolve2({ found, ok: found && code === 0, stdout: "" });
321
+ child2.on("close", (code2) => {
322
+ resolve2({ found, ok: found && code2 === 0, stdout: "" });
323
323
  });
324
324
  });
325
325
  var textOf = (value) => typeof value === "string" ? value : "";
@@ -359,13 +359,13 @@ var nodeProbe = (command, args2, timeoutMs) => {
359
359
  return { found: false, ok: false, stdout: "", timedOut: false };
360
360
  }
361
361
  const result = spawnProbe(command, args2, timeoutMs);
362
- const code = errnoCode2(result.error);
362
+ const code2 = errnoCode2(result.error);
363
363
  const stdout = result.stdout;
364
364
  return {
365
- found: code !== "ENOENT" && result.status !== WINDOWS_NOT_FOUND,
365
+ found: code2 !== "ENOENT" && result.status !== WINDOWS_NOT_FOUND,
366
366
  ok: result.error === void 0 && result.status === 0,
367
367
  stdout: typeof stdout === "string" ? stdout.trim() : "",
368
- timedOut: code === "ETIMEDOUT"
368
+ timedOut: code2 === "ETIMEDOUT"
369
369
  };
370
370
  };
371
371
  var nodeReadText = (path) => {
@@ -461,8 +461,8 @@ var startApp = (command, cwd, port) => {
461
461
  output = `${output}${error.message}`;
462
462
  exit = { code: null };
463
463
  });
464
- child2.once("exit", (code) => {
465
- exit = { code };
464
+ child2.once("exit", (code2) => {
465
+ exit = { code: code2 };
466
466
  });
467
467
  return { child: child2, output: () => output, exited: () => exit };
468
468
  };
@@ -586,8 +586,8 @@ var exitCodeDocs = {
586
586
  };
587
587
 
588
588
  // src/core/problem.ts
589
- var usageProblem = (code, messageKey, fix, params2 = {}) => ({
590
- code,
589
+ var usageProblem = (code2, messageKey, fix, params2 = {}) => ({
590
+ code: code2,
591
591
  messageKey,
592
592
  params: params2,
593
593
  fix,
@@ -667,6 +667,11 @@ var stringOption = (values, name) => {
667
667
  const value = values[name];
668
668
  return typeof value === "string" ? value : void 0;
669
669
  };
670
+ var listOption = (values, name) => {
671
+ const value = values[name];
672
+ const raw = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
673
+ return raw.filter((item2) => typeof item2 === "string" && item2.trim() !== "");
674
+ };
670
675
 
671
676
  // src/core/result.ts
672
677
  var SCHEMA_VERSION = 1;
@@ -905,12 +910,12 @@ var DEFAULT_PROTECTED = ["main", "master", "release/*", "prod", "production", "r
905
910
  var openRepo = (runtime, dir) => {
906
911
  const result = runtime.git(["rev-parse", "--show-toplevel", "--absolute-git-dir", "--show-prefix"], dir);
907
912
  if (!result.ok) return void 0;
908
- const [root, gitDir, prefix] = result.stdout.split("\n");
913
+ const [root, gitDir, prefix2] = result.stdout.split("\n");
909
914
  if (root === void 0 || root === "" || gitDir === void 0 || gitDir === "") return void 0;
910
915
  return {
911
916
  root: toAbsolutePath(root),
912
917
  gitDir: toAbsolutePath(gitDir),
913
- prefix: (prefix ?? "").replace(/\/$/, "")
918
+ prefix: (prefix2 ?? "").replace(/\/$/, "")
914
919
  };
915
920
  };
916
921
  var globRegex = (pattern) => new RegExp(`^${pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`);
@@ -1147,9 +1152,9 @@ var readLedger = (runtime, root) => {
1147
1152
  });
1148
1153
  };
1149
1154
  var GROUP_ORDER = ["must", "should", "nice", "already-there", "not-reproduced"];
1150
- var resultText = (row) => {
1151
- if (row === void 0) return "-";
1152
- return row.exitCode === null ? "did not run" : `exit ${String(row.exitCode)}`;
1155
+ var resultText = (row2) => {
1156
+ if (row2 === void 0) return "-";
1157
+ return row2.exitCode === null ? "did not run" : `exit ${String(row2.exitCode)}`;
1153
1158
  };
1154
1159
  var findingLines = (t, settled) => GROUP_ORDER.flatMap((group) => {
1155
1160
  const items = settled.filter((item2) => item2.group === group);
@@ -1171,7 +1176,7 @@ var findingLines = (t, settled) => GROUP_ORDER.flatMap((group) => {
1171
1176
  var tableLines = (t, checked, ledger) => {
1172
1177
  const rows = checked.filter((item2) => item2.finding.group === "must");
1173
1178
  if (rows.length === 0) return [];
1174
- const last = (id, side) => ledger.filter((row) => row.finding === id && row.side === side).at(-1);
1179
+ const last = (id, side) => ledger.filter((row2) => row2.finding === id && row2.side === side).at(-1);
1175
1180
  return [
1176
1181
  "",
1177
1182
  `| # | ${t("critique.table.command")} | base | head | ${t("critique.table.outcome")} |`,
@@ -1204,7 +1209,7 @@ var notRepository = (dir) => ({
1204
1209
  fix: "git init",
1205
1210
  exitCode: ExitCode.NotConfigured
1206
1211
  });
1207
- var gitProblem = (code, messageKey, detail, fix, exitCode = ExitCode.CheckFailed) => ({ code, messageKey, params: { detail }, fix, exitCode });
1212
+ var gitProblem = (code2, messageKey, detail, fix, exitCode = ExitCode.CheckFailed) => ({ code: code2, messageKey, params: { detail }, fix, exitCode });
1208
1213
  var repoOf = (ctx) => openRepo(ctx.runtime, ctx.cwd);
1209
1214
 
1210
1215
  // src/commands/critique.ts
@@ -1343,7 +1348,7 @@ var isOwnHandler = (handler) => {
1343
1348
 
1344
1349
  // src/install/statusline.ts
1345
1350
  var OWN = /finitemonkey(\.mjs|\.cmd|\.exe)?"?\s+statusline\s*$/;
1346
- var projectFile = (relative11) => relative11;
1351
+ var projectFile = (relative12) => relative12;
1347
1352
  var commandFor = (runner) => {
1348
1353
  if (runner.kind === "local") return `node ${projectFile(LOCAL_BIN)} statusline`;
1349
1354
  if (runner.kind === "vendored") return `node ${projectFile(VENDOR_BIN)} statusline`;
@@ -2421,11 +2426,11 @@ var needsWrite = (plan) => hasChanges(plan) || plan.previous === void 0;
2421
2426
 
2422
2427
  // src/install/apply.ts
2423
2428
  var stamp = (iso) => iso.replace(/[:.]/g, "-");
2424
- var ensureParent = (project, relative11) => project.runtime.mkdir(project.abs(dirname3(relative11)));
2425
- var writeText = (project, relative11, text9) => {
2426
- const parent = ensureParent(project, relative11);
2427
- const written = parent.ok ? project.runtime.writeAtomic(project.abs(relative11), text9) : parent;
2428
- return written.ok ? accept(void 0) : reject({ path: relative11, detail: written.error });
2429
+ var ensureParent = (project, relative12) => project.runtime.mkdir(project.abs(dirname3(relative12)));
2430
+ var writeText = (project, relative12, text9) => {
2431
+ const parent = ensureParent(project, relative12);
2432
+ const written = parent.ok ? project.runtime.writeAtomic(project.abs(relative12), text9) : parent;
2433
+ return written.ok ? accept(void 0) : reject({ path: relative12, detail: written.error });
2429
2434
  };
2430
2435
  var restore = (project, items) => {
2431
2436
  for (const item2 of items) {
@@ -2567,17 +2572,17 @@ var detectLang = (flag2, env, settings = {}) => {
2567
2572
  // src/install/project.ts
2568
2573
  import { dirname as dirname4, join as join7 } from "node:path";
2569
2574
  var projectAt = (root, runtime) => {
2570
- const abs = (relative11) => toAbsolutePath(join7(root, ...relative11.split("/")));
2575
+ const abs = (relative12) => toAbsolutePath(join7(root, ...relative12.split("/")));
2571
2576
  return {
2572
2577
  root,
2573
2578
  runtime,
2574
2579
  abs,
2575
- read: (relative11) => {
2576
- const text9 = runtime.readText(abs(relative11));
2580
+ read: (relative12) => {
2581
+ const text9 = runtime.readText(abs(relative12));
2577
2582
  return text9.ok ? text9.value : void 0;
2578
2583
  },
2579
- exists: (relative11) => runtime.stat(abs(relative11)) !== void 0,
2580
- isDir: (relative11) => runtime.stat(abs(relative11))?.kind === "dir"
2584
+ exists: (relative12) => runtime.stat(abs(relative12)) !== void 0,
2585
+ isDir: (relative12) => runtime.stat(abs(relative12))?.kind === "dir"
2581
2586
  };
2582
2587
  };
2583
2588
  var findGitRoot = (start, runtime) => {
@@ -2595,8 +2600,8 @@ var openProject = (ctx) => {
2595
2600
  const root = findGitRoot(ctx.cwd, ctx.runtime);
2596
2601
  return { project: projectAt(root ?? ctx.cwd, ctx.runtime), gitRoot: root !== void 0 };
2597
2602
  };
2598
- var problem = (code, messageKey, fix, exitCode, params2) => ({
2599
- code,
2603
+ var problem = (code2, messageKey, fix, exitCode, params2) => ({
2604
+ code: code2,
2600
2605
  messageKey,
2601
2606
  params: params2,
2602
2607
  fix,
@@ -3651,7 +3656,7 @@ var checkAuthVariables = ({ env, platform }) => {
3651
3656
  };
3652
3657
 
3653
3658
  // src/doctor/hooks.ts
3654
- import { join as join36 } from "node:path";
3659
+ import { join as join37 } from "node:path";
3655
3660
 
3656
3661
  // src/hooks/events.ts
3657
3662
  var HOOK_EVENTS = [
@@ -4737,20 +4742,20 @@ var POPULAR = { npm: NPM, pypi: PYPI };
4737
4742
  var distance = (left, right) => {
4738
4743
  const rows = Array.from(
4739
4744
  { length: left.length + 1 },
4740
- (_, row) => Array.from({ length: right.length + 1 }, (_2, column) => row === 0 ? column : column === 0 ? row : 0)
4745
+ (_, row2) => Array.from({ length: right.length + 1 }, (_2, column) => row2 === 0 ? column : column === 0 ? row2 : 0)
4741
4746
  );
4742
- const cell = (row, column) => rows[row]?.[column] ?? Number.POSITIVE_INFINITY;
4743
- for (let row = 1; row <= left.length; row += 1) {
4744
- const current2 = rows[row] ?? [];
4747
+ const cell2 = (row2, column) => rows[row2]?.[column] ?? Number.POSITIVE_INFINITY;
4748
+ for (let row2 = 1; row2 <= left.length; row2 += 1) {
4749
+ const current2 = rows[row2] ?? [];
4745
4750
  for (let column = 1; column <= right.length; column += 1) {
4746
- const cost = left[row - 1] === right[column - 1] ? 0 : 1;
4747
- let best = Math.min(cell(row - 1, column) + 1, cell(row, column - 1) + 1, cell(row - 1, column - 1) + cost);
4748
- const swapped = left[row - 1] === right[column - 2] && left[row - 2] === right[column - 1];
4749
- if (row > 1 && column > 1 && swapped) best = Math.min(best, cell(row - 2, column - 2) + 1);
4751
+ const cost = left[row2 - 1] === right[column - 1] ? 0 : 1;
4752
+ let best = Math.min(cell2(row2 - 1, column) + 1, cell2(row2, column - 1) + 1, cell2(row2 - 1, column - 1) + cost);
4753
+ const swapped = left[row2 - 1] === right[column - 2] && left[row2 - 2] === right[column - 1];
4754
+ if (row2 > 1 && column > 1 && swapped) best = Math.min(best, cell2(row2 - 2, column - 2) + 1);
4750
4755
  current2[column] = best;
4751
4756
  }
4752
4757
  }
4753
- return cell(left.length, right.length);
4758
+ return cell2(left.length, right.length);
4754
4759
  };
4755
4760
  var squash2 = (name) => name.replace(/^@[^/]+\//, "").replace(/[-_.]/g, "");
4756
4761
  var closePopular = (ecosystem, name) => {
@@ -5002,8 +5007,8 @@ var npmDeclared = (runtime, root) => {
5002
5007
  const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
5003
5008
  if (parsed?.ok !== true) return /* @__PURE__ */ new Set();
5004
5009
  return new Set(
5005
- NPM_SECTIONS.flatMap((section2) => {
5006
- const value = parsed.value[section2];
5010
+ NPM_SECTIONS.flatMap((section3) => {
5011
+ const value = parsed.value[section3];
5007
5012
  return isRecord(value) ? Object.keys(value) : [];
5008
5013
  })
5009
5014
  );
@@ -5620,9 +5625,9 @@ var hostsInText = (text9) => {
5620
5625
  return [...urls, ...keyed, ...commands].filter((host) => host !== "" && !host.includes("$"));
5621
5626
  };
5622
5627
  var workflowFiles = (runtime, root) => {
5623
- const listed4 = runtime.listDir(toAbsolutePath(join14(root, ".github", "workflows")));
5624
- if (!listed4.ok) return [];
5625
- return listed4.value.filter((name) => /deploy/i.test(name) && /\.ya?ml$/i.test(name)).map((name) => `.github/workflows/${name}`);
5628
+ const listed5 = runtime.listDir(toAbsolutePath(join14(root, ".github", "workflows")));
5629
+ if (!listed5.ok) return [];
5630
+ return listed5.value.filter((name) => /deploy/i.test(name) && /\.ya?ml$/i.test(name)).map((name) => `.github/workflows/${name}`);
5626
5631
  };
5627
5632
  var detectedHosts = (runtime, root, files) => files.flatMap((file) => {
5628
5633
  const text9 = runtime.readText(toAbsolutePath(join14(root, file)));
@@ -6080,10 +6085,10 @@ var RULES2 = {
6080
6085
  }
6081
6086
  };
6082
6087
  var countTests = (language, source) => {
6083
- const code = language === "py" ? stripHash(source) : stripJs(source);
6088
+ const code2 = language === "py" ? stripHash(source) : stripJs(source);
6084
6089
  const rules = RULES2[language];
6085
- const skips = count(code, rules.skips);
6086
- return { tests: count(code, rules.tests), skips, assertions: count(code, rules.asserts) };
6090
+ const skips = count(code2, rules.skips);
6091
+ return { tests: count(code2, rules.tests), skips, assertions: count(code2, rules.asserts) };
6087
6092
  };
6088
6093
 
6089
6094
  // src/guards/tests/analyze.ts
@@ -6715,7 +6720,8 @@ var CHECK_KINDS = [
6715
6720
  "merge",
6716
6721
  "prod",
6717
6722
  "prose",
6718
- "review"
6723
+ "review",
6724
+ "inventory"
6719
6725
  ];
6720
6726
  var GIT_REQUIREMENTS = ["committed", "pushed", "pr_open"];
6721
6727
  var SIZES = ["S", "M", "L"];
@@ -6982,8 +6988,8 @@ var parseNameStatus = (text9) => {
6982
6988
  const fields = nulFields(text9);
6983
6989
  const changes = [];
6984
6990
  for (let index = 0; index < fields.length; index += 1) {
6985
- const code = fields[index] ?? "";
6986
- const letter = code.slice(0, 1);
6991
+ const code2 = fields[index] ?? "";
6992
+ const letter = code2.slice(0, 1);
6987
6993
  if (letter === "R" || letter === "C") {
6988
6994
  const oldPath = fields[index + 1] ?? "";
6989
6995
  const path2 = fields[index + 2] ?? "";
@@ -7032,9 +7038,9 @@ var parseAddedLines = (diff) => {
7032
7038
  };
7033
7039
  var addedLines = (git2, base) => parseAddedLines(git2.out(["diff", "-U0", "--no-color", "--no-ext-diff", base, "--", ".", EXCLUDE_FM]) ?? "");
7034
7040
  var numstat = (git2, base) => {
7035
- const rows = (git2.out(["diff", "--numstat", "--no-color", base, "--", ".", EXCLUDE_FM]) ?? "").split("\n").filter((row) => row.trim() !== "");
7036
- const lines3 = rows.reduce((sum, row) => {
7037
- const [plus = "0", minus = "0"] = row.split(" ");
7041
+ const rows = (git2.out(["diff", "--numstat", "--no-color", base, "--", ".", EXCLUDE_FM]) ?? "").split("\n").filter((row2) => row2.trim() !== "");
7042
+ const lines3 = rows.reduce((sum, row2) => {
7043
+ const [plus = "0", minus = "0"] = row2.split(" ");
7038
7044
  return sum + (Number(plus) || 0) + (Number(minus) || 0);
7039
7045
  }, 0);
7040
7046
  return { files: rows.length, lines: lines3 };
@@ -7252,21 +7258,21 @@ var applySize = (contract, verdict) => {
7252
7258
 
7253
7259
  // src/gate/prepare.ts
7254
7260
  var diskFiles = (runtime, root) => ({
7255
- read: (relative11) => {
7256
- const text9 = runtime.readText(toAbsolutePath(join19(root, relative11)));
7261
+ read: (relative12) => {
7262
+ const text9 = runtime.readText(toAbsolutePath(join19(root, relative12)));
7257
7263
  return text9.ok ? text9.value : void 0;
7258
7264
  },
7259
- exists: (relative11) => runtime.stat(toAbsolutePath(join19(root, relative11))) !== void 0
7265
+ exists: (relative12) => runtime.stat(toAbsolutePath(join19(root, relative12))) !== void 0
7260
7266
  });
7261
7267
  var baseFiles = (git2, base, disk) => ({
7262
- read: (relative11) => base === EMPTY_TREE ? disk.read(relative11) : fileAt(git2, base, relative11),
7268
+ read: (relative12) => base === EMPTY_TREE ? disk.read(relative12) : fileAt(git2, base, relative12),
7263
7269
  exists: disk.exists
7264
7270
  });
7265
7271
  var loadContract = (options, paths, git2, base) => {
7266
7272
  const disk = diskFiles(options.runtime, paths.root);
7267
7273
  const id = activeTaskId(options.runtime, paths, options.taskId);
7268
7274
  if (id === void 0) return accept(loadImplicit(options.runtime, paths, baseFiles(git2, base, disk)));
7269
- return loadTask(options.runtime, paths, { disk: disk.read, base: (relative11) => fileAt(git2, base, relative11) }, id);
7275
+ return loadTask(options.runtime, paths, { disk: disk.read, base: (relative12) => fileAt(git2, base, relative12) }, id);
7270
7276
  };
7271
7277
  var memo2 = (make) => {
7272
7278
  let value;
@@ -8285,7 +8291,50 @@ var en_default = {
8285
8291
  "deps.finding.young": "{name} has little history: first published {days, plural, one {# day} other {# days}} ago, {count, plural, one {# version} other {# versions}}.",
8286
8292
  "deps.finding.fresh": "{name}@{version} was published {hours, plural, one {# hour} other {# hours}} ago; prefer a version at least {days, plural, one {# day} other {# days}} old.",
8287
8293
  "deps.finding.script": "{name}@{version} adds an install script that the previous version did not have; read it before installing.",
8288
- "deps.finding.unchecked": "{name} was not checked on {registry} (offline or no answer in time), so only the spelling was checked."
8294
+ "deps.finding.unchecked": "{name} was not checked on {registry} (offline or no answer in time), so only the spelling was checked.",
8295
+ "command.inventory.summary": "Write docs/plan/NNa-today-<topic>.md: what the code does today, tied to the commit read",
8296
+ "inventory.written": "Wrote {file} ({count, plural, one {# repository read} other {# repositories read}}).",
8297
+ "inventory.error.ref": "{ref} is not a commit in {repo}",
8298
+ "inventory.error.topic": "name the topic: finitemonkey inventory <topic>",
8299
+ "plan.error.write": "could not write the plan file: {detail}",
8300
+ "inventory.title": "Today: {topic}",
8301
+ "inventory.read_at": "Read `{path}` at {hash} on {date} (ref {ref}).",
8302
+ "inventory.risks": "Active risks",
8303
+ "inventory.col.risk": "Risk",
8304
+ "inventory.col.still": "Still there?",
8305
+ "inventory.col.evidence": "Evidence",
8306
+ "inventory.col.fix": "Minimal fix",
8307
+ "inventory.risk.credential": "Credential in a tracked file ({rule})",
8308
+ "inventory.risk.yes": "yes",
8309
+ "inventory.risk.fix_credential": "move it to the environment, rotate it, run finitemonkey secrets scan",
8310
+ "inventory.gap": "The plan says, the code does today",
8311
+ "inventory.col.plan": "The plan says",
8312
+ "inventory.col.today": "The code does today",
8313
+ "inventory.col.effect": "Effect",
8314
+ "inventory.modules": "Modules",
8315
+ "inventory.routes": "Routes",
8316
+ "inventory.tables": "Tables",
8317
+ "inventory.queues": "Queues",
8318
+ "inventory.jobs": "Jobs",
8319
+ "inventory.env": "Environment variables (names only)",
8320
+ "inventory.dependencies": "Dependencies",
8321
+ "inventory.tests": "Tests",
8322
+ "inventory.test_count": "{count, plural, one {# test file} other {# test files}}.",
8323
+ "inventory.credentials": "Credentials",
8324
+ "inventory.credentials_note": "Cited by file and line only. The value never goes into this report.",
8325
+ "inventory.deploy": "Deploy",
8326
+ "inventory.history": "Secrets in history",
8327
+ "inventory.worktrees": "Existing worktrees",
8328
+ "inventory.hotspots": "Hotspots",
8329
+ "inventory.delta": "Changed since {hash}",
8330
+ "inventory.none": "Nothing found.",
8331
+ "inventory.more": "and {count} more",
8332
+ "inventory.files": "{count, plural, one {# file} other {# files}}",
8333
+ "inventory.changes": "{count, plural, one {# change} other {# changes}} in the last 300 commits",
8334
+ "gate.inventory.missing": "no inventory in docs/plan; run finitemonkey inventory <topic>",
8335
+ "gate.inventory.expired": "{report} expired: {count, plural, one {# covered path changed} other {# covered paths changed}} since {hash} ({paths}); run finitemonkey inventory {topic} for the delta",
8336
+ "gate.inventory.unknown": "{report} cites {hash} in {repo}, a commit this clone cannot read",
8337
+ "gate.inventory.fresh": "{count, plural, one {# inventory matches} other {# inventories match}} the covered paths"
8289
8338
  };
8290
8339
 
8291
8340
  // src/i18n/es.json
@@ -8990,7 +9039,50 @@ var es_default = {
8990
9039
  "deps.finding.young": "{name} tiene poco historial: se publicó por primera vez hace {days, plural, one {# día} other {# días}}, {count, plural, one {# versión} other {# versiones}}.",
8991
9040
  "deps.finding.fresh": "{name}@{version} salió hace {hours, plural, one {# hora} other {# horas}}; mejor una versión con al menos {days, plural, one {# día} other {# días}}.",
8992
9041
  "deps.finding.script": "{name}@{version} trae un script de instalación que la versión anterior no tenía; léelo antes de instalar.",
8993
- "deps.finding.unchecked": "{name} no se comprobó en {registry} (sin red o sin respuesta a tiempo), así que solo se revisó la ortografía."
9042
+ "deps.finding.unchecked": "{name} no se comprobó en {registry} (sin red o sin respuesta a tiempo), así que solo se revisó la ortografía.",
9043
+ "command.inventory.summary": "Escribe docs/plan/NNa-today-<tema>.md: lo que hace el código hoy, ligado al commit leído",
9044
+ "inventory.written": "Escrito {file} ({count, plural, one {# repositorio leído} other {# repositorios leídos}}).",
9045
+ "inventory.error.ref": "{ref} no es un commit en {repo}",
9046
+ "inventory.error.topic": "indica el tema: finitemonkey inventory <tema>",
9047
+ "plan.error.write": "no se pudo escribir el archivo del plan: {detail}",
9048
+ "inventory.title": "Hoy: {topic}",
9049
+ "inventory.read_at": "Leído `{path}` en {hash} el {date} (ref {ref}).",
9050
+ "inventory.risks": "Riesgos activos",
9051
+ "inventory.col.risk": "Riesgo",
9052
+ "inventory.col.still": "¿Sigue ahí?",
9053
+ "inventory.col.evidence": "Evidencia",
9054
+ "inventory.col.fix": "Corrección mínima",
9055
+ "inventory.risk.credential": "Credencial en un archivo versionado ({rule})",
9056
+ "inventory.risk.yes": "sí",
9057
+ "inventory.risk.fix_credential": "moverla al entorno, rotarla, ejecutar finitemonkey secrets scan",
9058
+ "inventory.gap": "El plan dice, el código hace hoy",
9059
+ "inventory.col.plan": "El plan dice",
9060
+ "inventory.col.today": "El código hace hoy",
9061
+ "inventory.col.effect": "Efecto",
9062
+ "inventory.modules": "Módulos",
9063
+ "inventory.routes": "Rutas",
9064
+ "inventory.tables": "Tablas",
9065
+ "inventory.queues": "Colas",
9066
+ "inventory.jobs": "Jobs",
9067
+ "inventory.env": "Variables de entorno (solo los nombres)",
9068
+ "inventory.dependencies": "Dependencias",
9069
+ "inventory.tests": "Tests",
9070
+ "inventory.test_count": "{count, plural, one {# archivo de test} other {# archivos de test}}.",
9071
+ "inventory.credentials": "Credenciales",
9072
+ "inventory.credentials_note": "Citadas solo por archivo y línea. El valor nunca entra en este informe.",
9073
+ "inventory.deploy": "Despliegue",
9074
+ "inventory.history": "Secretos en el historial",
9075
+ "inventory.worktrees": "Worktrees existentes",
9076
+ "inventory.hotspots": "Puntos calientes",
9077
+ "inventory.delta": "Cambió desde {hash}",
9078
+ "inventory.none": "Nada encontrado.",
9079
+ "inventory.more": "y {count} más",
9080
+ "inventory.files": "{count, plural, one {# archivo} other {# archivos}}",
9081
+ "inventory.changes": "{count, plural, one {# cambio} other {# cambios}} en los últimos 300 commits",
9082
+ "gate.inventory.missing": "ningún inventario en docs/plan; ejecuta finitemonkey inventory <tema>",
9083
+ "gate.inventory.expired": "{report} venció: {count, plural, one {# ruta cubierta cambió} other {# rutas cubiertas cambiaron}} desde {hash} ({paths}); ejecuta finitemonkey inventory {topic} para el delta",
9084
+ "gate.inventory.unknown": "{report} cita {hash} en {repo}, un commit que este clon no puede leer",
9085
+ "gate.inventory.fresh": "{count, plural, one {# inventario coincide} other {# inventarios coinciden}} con las rutas cubiertas"
8994
9086
  };
8995
9087
 
8996
9088
  // src/i18n/pt-BR.json
@@ -9695,7 +9787,50 @@ var pt_BR_default = {
9695
9787
  "deps.finding.young": "{name} tem pouco histórico: publicado pela primeira vez há {days, plural, one {# dia} other {# dias}}, {count, plural, one {# versão} other {# versões}}.",
9696
9788
  "deps.finding.fresh": "{name}@{version} saiu há {hours, plural, one {# hora} other {# horas}}; prefira uma versão com pelo menos {days, plural, one {# dia} other {# dias}}.",
9697
9789
  "deps.finding.script": "{name}@{version} traz um script de instalação que a versão anterior não tinha; leia antes de instalar.",
9698
- "deps.finding.unchecked": "{name} não foi conferido no {registry} (sem rede ou sem resposta a tempo), então só a grafia foi checada."
9790
+ "deps.finding.unchecked": "{name} não foi conferido no {registry} (sem rede ou sem resposta a tempo), então só a grafia foi checada.",
9791
+ "command.inventory.summary": "Grava docs/plan/NNa-today-<tema>.md: o que o código faz hoje, ligado ao commit lido",
9792
+ "inventory.written": "Gravado {file} ({count, plural, one {# repositório lido} other {# repositórios lidos}}).",
9793
+ "inventory.error.ref": "{ref} não é um commit em {repo}",
9794
+ "inventory.error.topic": "diga o tema: finitemonkey inventory <tema>",
9795
+ "plan.error.write": "não deu para gravar o arquivo do plano: {detail}",
9796
+ "inventory.title": "Hoje: {topic}",
9797
+ "inventory.read_at": "Lido `{path}` em {hash} no dia {date} (ref {ref}).",
9798
+ "inventory.risks": "Riscos ativos",
9799
+ "inventory.col.risk": "Risco",
9800
+ "inventory.col.still": "Ainda está lá?",
9801
+ "inventory.col.evidence": "Evidência",
9802
+ "inventory.col.fix": "Correção mínima",
9803
+ "inventory.risk.credential": "Credencial em arquivo versionado ({rule})",
9804
+ "inventory.risk.yes": "sim",
9805
+ "inventory.risk.fix_credential": "mover para o ambiente, rotacionar, rodar finitemonkey secrets scan",
9806
+ "inventory.gap": "O plano diz, o código faz hoje",
9807
+ "inventory.col.plan": "O plano diz",
9808
+ "inventory.col.today": "O código faz hoje",
9809
+ "inventory.col.effect": "Efeito",
9810
+ "inventory.modules": "Módulos",
9811
+ "inventory.routes": "Rotas",
9812
+ "inventory.tables": "Tabelas",
9813
+ "inventory.queues": "Filas",
9814
+ "inventory.jobs": "Jobs",
9815
+ "inventory.env": "Variáveis de ambiente (só os nomes)",
9816
+ "inventory.dependencies": "Dependências",
9817
+ "inventory.tests": "Testes",
9818
+ "inventory.test_count": "{count, plural, one {# arquivo de teste} other {# arquivos de teste}}.",
9819
+ "inventory.credentials": "Credenciais",
9820
+ "inventory.credentials_note": "Citadas só por arquivo e linha. O valor nunca entra neste relatório.",
9821
+ "inventory.deploy": "Deploy",
9822
+ "inventory.history": "Segredos no histórico",
9823
+ "inventory.worktrees": "Worktrees existentes",
9824
+ "inventory.hotspots": "Pontos quentes",
9825
+ "inventory.delta": "Mudou desde {hash}",
9826
+ "inventory.none": "Nada encontrado.",
9827
+ "inventory.more": "e mais {count}",
9828
+ "inventory.files": "{count, plural, one {# arquivo} other {# arquivos}}",
9829
+ "inventory.changes": "{count, plural, one {# mudança} other {# mudanças}} nos últimos 300 commits",
9830
+ "gate.inventory.missing": "nenhum inventário em docs/plan; rode finitemonkey inventory <tema>",
9831
+ "gate.inventory.expired": "{report} venceu: {count, plural, one {# caminho coberto mudou} other {# caminhos cobertos mudaram}} desde {hash} ({paths}); rode finitemonkey inventory {topic} para o delta",
9832
+ "gate.inventory.unknown": "{report} cita {hash} em {repo}, um commit que este clone não lê",
9833
+ "gate.inventory.fresh": "{count, plural, one {# inventário confere} other {# inventários conferem}} com os caminhos cobertos"
9699
9834
  };
9700
9835
 
9701
9836
  // src/i18n/review.json
@@ -10401,7 +10536,50 @@ var review_default = {
10401
10536
  "deps.finding.young": "machine",
10402
10537
  "deps.finding.fresh": "machine",
10403
10538
  "deps.finding.script": "machine",
10404
- "deps.finding.unchecked": "machine"
10539
+ "deps.finding.unchecked": "machine",
10540
+ "command.inventory.summary": "machine",
10541
+ "inventory.written": "machine",
10542
+ "inventory.error.ref": "machine",
10543
+ "inventory.error.topic": "machine",
10544
+ "plan.error.write": "machine",
10545
+ "inventory.title": "machine",
10546
+ "inventory.read_at": "machine",
10547
+ "inventory.risks": "machine",
10548
+ "inventory.col.risk": "machine",
10549
+ "inventory.col.still": "machine",
10550
+ "inventory.col.evidence": "machine",
10551
+ "inventory.col.fix": "machine",
10552
+ "inventory.risk.credential": "machine",
10553
+ "inventory.risk.yes": "machine",
10554
+ "inventory.risk.fix_credential": "machine",
10555
+ "inventory.gap": "machine",
10556
+ "inventory.col.plan": "machine",
10557
+ "inventory.col.today": "machine",
10558
+ "inventory.col.effect": "machine",
10559
+ "inventory.modules": "machine",
10560
+ "inventory.routes": "machine",
10561
+ "inventory.tables": "machine",
10562
+ "inventory.queues": "machine",
10563
+ "inventory.jobs": "machine",
10564
+ "inventory.env": "machine",
10565
+ "inventory.dependencies": "machine",
10566
+ "inventory.tests": "machine",
10567
+ "inventory.test_count": "machine",
10568
+ "inventory.credentials": "machine",
10569
+ "inventory.credentials_note": "machine",
10570
+ "inventory.deploy": "machine",
10571
+ "inventory.history": "machine",
10572
+ "inventory.worktrees": "machine",
10573
+ "inventory.hotspots": "machine",
10574
+ "inventory.delta": "machine",
10575
+ "inventory.none": "machine",
10576
+ "inventory.more": "machine",
10577
+ "inventory.files": "machine",
10578
+ "inventory.changes": "machine",
10579
+ "gate.inventory.missing": "machine",
10580
+ "gate.inventory.expired": "machine",
10581
+ "gate.inventory.unknown": "machine",
10582
+ "gate.inventory.fresh": "machine"
10405
10583
  },
10406
10584
  es: {
10407
10585
  "command.init.summary": "machine",
@@ -11104,7 +11282,50 @@ var review_default = {
11104
11282
  "deps.finding.young": "machine",
11105
11283
  "deps.finding.fresh": "machine",
11106
11284
  "deps.finding.script": "machine",
11107
- "deps.finding.unchecked": "machine"
11285
+ "deps.finding.unchecked": "machine",
11286
+ "command.inventory.summary": "machine",
11287
+ "inventory.written": "machine",
11288
+ "inventory.error.ref": "machine",
11289
+ "inventory.error.topic": "machine",
11290
+ "plan.error.write": "machine",
11291
+ "inventory.title": "machine",
11292
+ "inventory.read_at": "machine",
11293
+ "inventory.risks": "machine",
11294
+ "inventory.col.risk": "machine",
11295
+ "inventory.col.still": "machine",
11296
+ "inventory.col.evidence": "machine",
11297
+ "inventory.col.fix": "machine",
11298
+ "inventory.risk.credential": "machine",
11299
+ "inventory.risk.yes": "machine",
11300
+ "inventory.risk.fix_credential": "machine",
11301
+ "inventory.gap": "machine",
11302
+ "inventory.col.plan": "machine",
11303
+ "inventory.col.today": "machine",
11304
+ "inventory.col.effect": "machine",
11305
+ "inventory.modules": "machine",
11306
+ "inventory.routes": "machine",
11307
+ "inventory.tables": "machine",
11308
+ "inventory.queues": "machine",
11309
+ "inventory.jobs": "machine",
11310
+ "inventory.env": "machine",
11311
+ "inventory.dependencies": "machine",
11312
+ "inventory.tests": "machine",
11313
+ "inventory.test_count": "machine",
11314
+ "inventory.credentials": "machine",
11315
+ "inventory.credentials_note": "machine",
11316
+ "inventory.deploy": "machine",
11317
+ "inventory.history": "machine",
11318
+ "inventory.worktrees": "machine",
11319
+ "inventory.hotspots": "machine",
11320
+ "inventory.delta": "machine",
11321
+ "inventory.none": "machine",
11322
+ "inventory.more": "machine",
11323
+ "inventory.files": "machine",
11324
+ "inventory.changes": "machine",
11325
+ "gate.inventory.missing": "machine",
11326
+ "gate.inventory.expired": "machine",
11327
+ "gate.inventory.unknown": "machine",
11328
+ "gate.inventory.fresh": "machine"
11108
11329
  }
11109
11330
  };
11110
11331
 
@@ -11569,8 +11790,8 @@ var removedSymbols = (runtime, git2, base, changes) => {
11569
11790
  }
11570
11791
  return [...found.values()].slice(0, MAX_SYMBOLS);
11571
11792
  };
11572
- var parseGrep = (output) => output.split("\n").filter((row) => row !== "").flatMap((row) => {
11573
- const [file = "", line2 = "", ...rest] = row.split("\0");
11793
+ var parseGrep = (output) => output.split("\n").filter((row2) => row2 !== "").flatMap((row2) => {
11794
+ const [file = "", line2 = "", ...rest] = row2.split("\0");
11574
11795
  const number2 = Number(line2);
11575
11796
  return file === "" || !Number.isInteger(number2) ? [] : [{ file: toPosixPath(file), line: number2, text: rest.join("\0") }];
11576
11797
  });
@@ -11811,11 +12032,224 @@ var gitCheck = (check2, ctx) => {
11811
12032
  return missing2.length > 0 ? failed(missing2) : passed([note("gate.git.ok", { require: wants.join(", ") })]);
11812
12033
  };
11813
12034
 
12035
+ // src/plan/folder.ts
12036
+ import { join as join21 } from "node:path";
12037
+ var PLAN_DIR = "docs/plan";
12038
+ var NUMBERED = /^(\d{2,})([a-z]?)-/;
12039
+ var planDir = (root) => toAbsolutePath(join21(root, ...PLAN_DIR.split("/")));
12040
+ var planPath = (root, name) => toAbsolutePath(join21(planDir(root), name));
12041
+ var planFiles = (runtime, root) => {
12042
+ const listed5 = runtime.listDir(planDir(root));
12043
+ return listed5.ok ? [...listed5.value].sort() : [];
12044
+ };
12045
+ var numberOf = (name) => {
12046
+ const match = NUMBERED.exec(name);
12047
+ return match?.[1] === void 0 ? void 0 : Number.parseInt(match[1], 10);
12048
+ };
12049
+ var nextNumber = (names) => {
12050
+ const highest = Math.max(0, ...names.map((name) => numberOf(name) ?? 0));
12051
+ return String(highest + 1).padStart(2, "0");
12052
+ };
12053
+ var slugOf = (topic) => {
12054
+ const slug2 = topic.toLowerCase().normalize("NFKD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48).replace(/-+$/, "");
12055
+ return slug2 === "" ? "topic" : slug2;
12056
+ };
12057
+ var findByKind = (names, kind, topic) => names.find((name) => NUMBERED.test(name) && name.endsWith(`-${kind}-${slugOf(topic)}.md`));
12058
+
12059
+ // src/core/yaml/write.ts
12060
+ var PLAIN_SAFE = /^[A-Za-z_./][A-Za-z0-9_./ ()-]*$/;
12061
+ var RESERVED = /^(true|false|null|~|-?\d.*)$/;
12062
+ var scalarText = (value) => {
12063
+ if (value === null) return "null";
12064
+ if (typeof value !== "string") return String(value);
12065
+ return PLAIN_SAFE.test(value) && !RESERVED.test(value) && !value.endsWith(" ") ? value : JSON.stringify(value);
12066
+ };
12067
+ var isScalar = (value) => value === null || typeof value !== "object";
12068
+ var inline2 = (value) => {
12069
+ if (isScalar(value)) return scalarText(value);
12070
+ if (Array.isArray(value)) {
12071
+ if (value.length === 0) return "[]";
12072
+ return value.every(isScalar) ? `[${value.map(scalarText).join(", ")}]` : void 0;
12073
+ }
12074
+ return Object.keys(value).length === 0 ? "{}" : void 0;
12075
+ };
12076
+ var lines = (value, indent) => {
12077
+ if (Array.isArray(value)) {
12078
+ return value.flatMap((item2) => {
12079
+ const flat = inline2(item2);
12080
+ if (flat !== void 0) return [`${indent}- ${flat}`];
12081
+ const [head = "", ...rest] = lines(item2, `${indent} `);
12082
+ return [`${indent}- ${head.trimStart()}`, ...rest];
12083
+ });
12084
+ }
12085
+ if (isScalar(value)) return [`${indent}${scalarText(value)}`];
12086
+ return Object.entries(value).flatMap(([key, item2]) => {
12087
+ const flat = inline2(item2);
12088
+ const name = scalarText(key);
12089
+ return flat === void 0 ? [`${indent}${name}:`, ...lines(item2, `${indent} `)] : [`${indent}${name}: ${flat}`];
12090
+ });
12091
+ };
12092
+ var stringifyYaml = (value) => `${lines(value, "").join("\n")}
12093
+ `;
12094
+
12095
+ // src/plan/front.ts
12096
+ var FENCE2 = "---";
12097
+ var readFront = (text9) => {
12098
+ const normalized = text9.replace(/\r\n/g, "\n");
12099
+ if (!normalized.startsWith(`${FENCE2}
12100
+ `)) return { data: void 0, body: normalized };
12101
+ const end = normalized.indexOf(`
12102
+ ${FENCE2}
12103
+ `, FENCE2.length);
12104
+ if (end === -1) return { data: void 0, body: normalized };
12105
+ const parsed = parseYaml(normalized.slice(FENCE2.length + 1, end + 1));
12106
+ const data = parsed.ok && isRecord(parsed.value) ? parsed.value : void 0;
12107
+ return { data, body: normalized.slice(end + FENCE2.length + 2).replace(/^\n/, "") };
12108
+ };
12109
+ var writeFront = (data, body2) => `${FENCE2}
12110
+ ${stringifyYaml(data)}${FENCE2}
12111
+
12112
+ ${body2}`;
12113
+ var textList = (record4, key) => {
12114
+ const value = record4[key];
12115
+ return Array.isArray(value) ? value.filter((item2) => typeof item2 === "string") : [];
12116
+ };
12117
+
12118
+ // src/inventory/patterns.ts
12119
+ var ROUTES = {
12120
+ pattern: `(^|[^A-Za-z0-9_])(app|router|server|api|route|r)\\.(get|post|put|patch|delete|all|route)\\(|@(Get|Post|Put|Patch|Delete|Request)(Mapping)?\\(|@(app|router|bp|blueprint)\\.(route|get|post|put|patch|delete)\\(|(^|[^A-Za-z0-9_])path\\(["']|HandleFunc\\(`
12121
+ };
12122
+ var TABLES = {
12123
+ pattern: `create table|(pg|sqlite|mysql)Table\\(|^model [A-Z][A-Za-z0-9_]* *\\{|__tablename__|@Entity\\(|createTable\\(|(^|[^A-Za-z0-9_])define\\(["'][A-Za-z_]+["'], *\\{`,
12124
+ ignoreCase: true
12125
+ };
12126
+ var QUEUES = {
12127
+ pattern: "new (Queue|Worker|QueueScheduler)\\(|createQueue\\(|@Processor\\(|\\.consume\\(|(^|[^A-Za-z0-9_])sqs([^A-Za-z0-9_]|$)|SendMessageCommand|(^|[^A-Za-z0-9_])kafka([^A-Za-z0-9_]|$)|(^|[^A-Za-z0-9_])amqp([^A-Za-z0-9_]|$)|(^|[^A-Za-z0-9_])pubsub([^A-Za-z0-9_]|$)|@shared_task|@celery",
12128
+ ignoreCase: true
12129
+ };
12130
+ var JOBS = {
12131
+ pattern: "cron\\.schedule\\(|new CronJob\\(|@Cron\\(|@Scheduled\\(|^[[:space:]]*-? *cron: |schedule\\.every\\(|setInterval\\("
12132
+ };
12133
+ var ENV = {
12134
+ pattern: `process\\.env(\\.[A-Z_][A-Z0-9_]*|\\[["'][A-Z_][A-Z0-9_]*)|import\\.meta\\.env\\.[A-Z_]|os\\.environ|os\\.getenv\\(|Getenv\\(|ENV\\[["'][A-Z_]|env\\("[A-Z_]`
12135
+ };
12136
+ var CREDENTIAL_HINTS = {
12137
+ pattern: "(key|secret|token|passw|pwd|bearer|auth|credential|private)|sk_live|ghp_|github_pat_|AKIA|xox[bp]-|-----BEGIN|://[^/ ]+:[^@ ]+@",
12138
+ ignoreCase: true
12139
+ };
12140
+ var ENV_NAME = /(?:process\.env\.|process\.env\[["']|import\.meta\.env\.|os\.environ\.get\(["']|os\.environ\[["']|os\.getenv\(["']|Getenv\(["']|ENV\[["']|env\(["'])([A-Z_][A-Z0-9_]*)/g;
12141
+ var envNamesIn = (text9) => [...text9.matchAll(ENV_NAME)].map((match) => match[1] ?? "").filter((name) => name !== "");
12142
+ var SKIPPED = [
12143
+ ":(exclude,glob)**/node_modules/**",
12144
+ ":(exclude,glob)**/dist/**",
12145
+ ":(exclude,glob)**/build/**",
12146
+ ":(exclude,glob)**/vendor/**",
12147
+ ":(exclude,glob)**/*.min.js",
12148
+ ":(exclude,glob)**/package-lock.json",
12149
+ ":(exclude,glob)**/yarn.lock",
12150
+ ":(exclude,glob)**/pnpm-lock.yaml",
12151
+ ":(exclude,glob)docs/plan/**",
12152
+ ":(exclude,glob).finitemonkey/**"
12153
+ ];
12154
+ var TEST_FILE = /(^|\/)(tests?|__tests__|spec)\/|\.(test|spec)\.[a-z]+$|_test\.(go|py)$|(^|\/)test_[^/]+\.py$/;
12155
+ var DEPLOY_FILE = /(^|\/)(Dockerfile[^/]*|docker-compose[^/]*\.ya?ml|compose\.ya?ml|fly\.toml|vercel\.json|netlify\.toml|Procfile|render\.yaml|app\.yaml|serverless\.ya?ml|wrangler\.toml|railway\.json)$|^\.github\/workflows\/|\.tf$|(^|\/)(k8s|kubernetes|helm|deploy)\//;
12156
+
12157
+ // src/inventory/grep.ts
12158
+ var GREP_LINE = /^[^:]+:(.+?):(\d+):(.*)$/;
12159
+ var pathspecOf = (covered) => [
12160
+ ...covered.length === 0 ? ["."] : covered,
12161
+ ...SKIPPED
12162
+ ];
12163
+ var linesOf = (text9) => (text9 ?? "").split("\n").map(withoutCarriageReturn).filter((line2) => line2 !== "");
12164
+ var grepAt = (git2, hash, grep2, covered) => {
12165
+ const flags = grep2.ignoreCase === true ? ["-i"] : [];
12166
+ const out = git2.out(["grep", "-n", "-I", "-E", ...flags, "-e", grep2.pattern, hash, "--", ...pathspecOf(covered)]);
12167
+ return linesOf(out).flatMap((line2) => {
12168
+ const match = GREP_LINE.exec(line2);
12169
+ if (match?.[1] === void 0 || match[2] === void 0) return [];
12170
+ return [{ file: match[1], line: Number.parseInt(match[2], 10), text: (match[3] ?? "").trim() }];
12171
+ });
12172
+ };
12173
+ var SKIPPED_FILE = /(^|\/)(node_modules|dist|build|vendor)\/|\.min\.js$|(^|\/)(package-lock\.json|yarn\.lock|pnpm-lock\.yaml)$|^docs\/plan\/|^\.finitemonkey\//;
12174
+ var insideAny = (file, covered) => covered.length === 0 || covered.some((dir) => {
12175
+ const prefix2 = dir.replace(/\/+$/, "");
12176
+ return prefix2 === "." || prefix2 === "" || file === prefix2 || file.startsWith(`${prefix2}/`);
12177
+ });
12178
+ var filesAt = (git2, hash, covered) => linesOf(git2.out(["ls-tree", "-r", "--name-only", hash])).filter(
12179
+ (file) => insideAny(file, covered) && !SKIPPED_FILE.test(file)
12180
+ );
12181
+ var showAt = (git2, hash, file) => git2.out(["show", `${hash}:${file}`]);
12182
+ var resolveCommit = (git2, ref) => {
12183
+ const hash = git2.out(["rev-parse", "--verify", "-q", `${ref}^{commit}`])?.trim();
12184
+ return hash === void 0 || hash === "" ? void 0 : hash;
12185
+ };
12186
+
12187
+ // src/inventory/report.ts
12188
+ var reposOf = (data) => {
12189
+ const value = data?.["repos"];
12190
+ if (!Array.isArray(value)) return [];
12191
+ return value.flatMap((item2) => {
12192
+ if (!isRecord(item2)) return [];
12193
+ const { path, hash } = item2;
12194
+ return typeof path === "string" && typeof hash === "string" ? [{ path, hash }] : [];
12195
+ });
12196
+ };
12197
+
12198
+ // src/inventory/types.ts
12199
+ var INVENTORY_SCHEMA = "finitemonkey/inventory@1";
12200
+
12201
+ // src/inventory/expiry.ts
12202
+ var TODAY = /^\d{2,}[a-z]?-today-.+\.md$/;
12203
+ var changedSince = (runtime, dir, hash, covered) => {
12204
+ const git2 = gitAt(runtime, dir);
12205
+ if (git2.out(["cat-file", "-e", `${hash}^{commit}`]) === void 0) return void 0;
12206
+ const spec2 = pathspecOf(covered);
12207
+ const tracked = linesOf(git2.out(["diff", "--name-only", hash, "--", ...spec2]));
12208
+ const untracked3 = linesOf(git2.out(["ls-files", "--others", "--exclude-standard", "--", ...spec2]));
12209
+ return [.../* @__PURE__ */ new Set([...tracked, ...untracked3])].sort();
12210
+ };
12211
+ var stateOf = (runtime, root, name) => {
12212
+ const text9 = runtime.readText(planPath(root, name));
12213
+ const front = text9.ok ? readFront(text9.value).data : void 0;
12214
+ if (front?.["schema"] !== INVENTORY_SCHEMA) return void 0;
12215
+ const report2 = `${PLAN_DIR}/${name}`;
12216
+ const topic = typeof front["topic"] === "string" ? front["topic"] : name;
12217
+ const covered = textList(front, "covered");
12218
+ for (const repo of reposOf(front)) {
12219
+ const changed = changedSince(runtime, toAbsolutePath(repo.path, root), repo.hash, covered);
12220
+ if (changed === void 0) return { kind: "unknown", report: report2, repo: repo.path, hash: repo.hash };
12221
+ if (changed.length > 0) return { kind: "expired", report: report2, topic, hash: repo.hash, changed };
12222
+ }
12223
+ return { kind: "fresh", report: report2 };
12224
+ };
12225
+ var inventoryStates = (runtime, root) => planFiles(runtime, root).filter((name) => TODAY.test(name)).flatMap((name) => stateOf(runtime, root, name) ?? []);
12226
+
12227
+ // src/gate/checks/inventory.ts
12228
+ var MAX_PATHS = 5;
12229
+ var noteOf = (state) => {
12230
+ if (state.kind === "fresh") return void 0;
12231
+ if (state.kind === "unknown")
12232
+ return note("gate.inventory.unknown", { report: state.report, repo: state.repo, hash: state.hash });
12233
+ return note("gate.inventory.expired", {
12234
+ report: state.report,
12235
+ count: state.changed.length,
12236
+ hash: state.hash.slice(0, 12),
12237
+ paths: state.changed.slice(0, MAX_PATHS).join(", "),
12238
+ topic: state.topic
12239
+ });
12240
+ };
12241
+ var inventoryOutcome = (states) => {
12242
+ if (states.length === 0) return failed([note("gate.inventory.missing")]);
12243
+ const notes2 = states.flatMap((state) => noteOf(state) ?? []);
12244
+ return notes2.length === 0 ? passed([note("gate.inventory.fresh", { count: states.length })]) : failed(notes2);
12245
+ };
12246
+ var inventoryCheck = (_check, ctx) => inventoryOutcome(inventoryStates(ctx.runtime, ctx.paths.root));
12247
+
11814
12248
  // src/gate/checks/look.ts
11815
- import { join as join23 } from "node:path";
12249
+ import { join as join24 } from "node:path";
11816
12250
 
11817
12251
  // src/look/config.ts
11818
- import { join as join21 } from "node:path";
12252
+ import { join as join22 } from "node:path";
11819
12253
 
11820
12254
  // src/look/types.ts
11821
12255
  var LOOK_RULES = [
@@ -11877,18 +12311,18 @@ var parseLookConfig = (source) => {
11877
12311
  };
11878
12312
  };
11879
12313
  var loadLookConfig = (runtime, root) => {
11880
- const source = runtime.readText(toAbsolutePath(join21(root, LOOK_CONFIG)));
12314
+ const source = runtime.readText(toAbsolutePath(join22(root, LOOK_CONFIG)));
11881
12315
  return source.ok ? parseLookConfig(source.value) : void 0;
11882
12316
  };
11883
12317
  var withMock = (route, mock) => `${route}${route.includes("?") ? "&" : "?"}mock=${encodeURIComponent(mock)}`;
11884
12318
  var lookRoutes = (config) => config.routes.flatMap((route) => [route, ...config.mocks.map((mock) => withMock(route, mock))]);
11885
12319
 
11886
12320
  // src/look/paths.ts
11887
- import { join as join22, relative as relative5 } from "node:path";
12321
+ import { join as join23, relative as relative5 } from "node:path";
11888
12322
  var LOOK_DIR = "look";
11889
12323
  var SUMMARY_FILE = "summary.json";
11890
- var lookDir = (paths, treeHash2) => toAbsolutePath(join22(paths.dir, LOOK_DIR, treeHash2));
11891
- var summaryPath = (paths, treeHash2) => toAbsolutePath(join22(lookDir(paths, treeHash2), SUMMARY_FILE));
12324
+ var lookDir = (paths, treeHash2) => toAbsolutePath(join23(paths.dir, LOOK_DIR, treeHash2));
12325
+ var summaryPath = (paths, treeHash2) => toAbsolutePath(join23(lookDir(paths, treeHash2), SUMMARY_FILE));
11892
12326
  var projectRelative = (paths, absolute) => toPosixPath(relative5(paths.root, absolute));
11893
12327
 
11894
12328
  // src/gate/checks/look.ts
@@ -11923,7 +12357,7 @@ var readSummary = (ctx) => {
11923
12357
  var lastScreenEdit = (ctx) => {
11924
12358
  const screens = loadLookConfig(ctx.runtime, ctx.paths.root)?.screens ?? DEFAULT_SCREENS;
11925
12359
  return ctx.changes.filter((change) => change.status !== "D" && matchesAny(change.path, screens)).reduce((latest, change) => {
11926
- const stat = ctx.runtime.stat(toAbsolutePath(join23(ctx.paths.root, change.path)));
12360
+ const stat = ctx.runtime.stat(toAbsolutePath(join24(ctx.paths.root, change.path)));
11927
12361
  return Math.max(latest, stat?.mtimeMs ?? 0);
11928
12362
  }, 0);
11929
12363
  };
@@ -11970,13 +12404,13 @@ var manualCheck = (check2, ctx) => {
11970
12404
  };
11971
12405
 
11972
12406
  // src/gate/checks/placeholders.ts
11973
- import { join as join24 } from "node:path";
12407
+ import { join as join25 } from "node:path";
11974
12408
  var MAX_LISTED3 = 10;
11975
12409
  var SKIP_FILES = /\.(md|mdx|txt|rst|lock|snap|svg|png|jpg|gif|json)$|(^|\/)(CHANGELOG|LICENSE)/i;
11976
12410
  var PLACEHOLDER = /\b(TODO|FIXME|XXX)\b|not implemented|NotImplementedError|unimplemented!|todo!\(|lorem ipsum/i;
11977
12411
  var findPlaceholders = (lines3) => lines3.filter((line2) => !isTestFile(line2.path) && !SKIP_FILES.test(line2.path) && PLACEHOLDER.test(line2.text));
11978
12412
  var untrackedAdded = (ctx) => ctx.changes.filter((change) => change.status === "A" && !SKIP_FILES.test(change.path)).flatMap((change) => {
11979
- const text9 = ctx.runtime.readText(toAbsolutePath(join24(ctx.paths.root, change.path)));
12413
+ const text9 = ctx.runtime.readText(toAbsolutePath(join25(ctx.paths.root, change.path)));
11980
12414
  return text9.ok ? text9.value.split("\n").map((line2, index) => ({ path: change.path, line: index + 1, text: line2 })) : [];
11981
12415
  });
11982
12416
  var placeholdersCheck = (_check, ctx) => {
@@ -12211,13 +12645,13 @@ var LINE_MARKER = /<!--\s*finitemonkey:prose-allow\s*-->/;
12211
12645
  var FILE_MARKER = /<!--\s*finitemonkey:prose-allow-file\s*-->/;
12212
12646
  var ANY_MARKER = /<!--\s*finitemonkey:prose-allow(?:-file)?\s*-->/g;
12213
12647
  var INLINE_CODE = /`[^`]*`/g;
12214
- var FENCE2 = /^\s*(```|~~~)/;
12648
+ var FENCE3 = /^\s*(```|~~~)/;
12215
12649
  var outsideCode = (line2) => line2.replace(INLINE_CODE, "");
12216
12650
  var unfencedLines = (text9) => {
12217
12651
  let fenced = false;
12218
12652
  return text9.split("\n").filter((line2) => {
12219
- if (FENCE2.test(line2)) fenced = !fenced;
12220
- return !fenced && !FENCE2.test(line2);
12653
+ if (FENCE3.test(line2)) fenced = !fenced;
12654
+ return !fenced && !FENCE3.test(line2);
12221
12655
  });
12222
12656
  };
12223
12657
  var allowsWholeFile = (text9) => unfencedLines(text9).some((line2) => FILE_MARKER.test(outsideCode(line2)));
@@ -12226,7 +12660,7 @@ var markerCount = (text9) => text9.match(ANY_MARKER)?.length ?? 0;
12226
12660
  var isMarkerOnlyLine = (line2) => allowsLine(line2) && outsideCode(line2).replace(LINE_MARKER, "").trim() === "";
12227
12661
 
12228
12662
  // src/prose/extract.ts
12229
- var FENCE3 = /^\s*(```|~~~)/;
12663
+ var FENCE4 = /^\s*(```|~~~)/;
12230
12664
  var HEADING = /^#{1,6}\s+/;
12231
12665
  var ITEM = /^\s*(?:[-*+]|\d+[.)])\s+/;
12232
12666
  var SKIPPED_LINE = /^\s*[|>]|^\s*<(?!!--)/;
@@ -12304,7 +12738,7 @@ var readBlocks = (lines3) => {
12304
12738
  let commented = false;
12305
12739
  for (let index = skipFrontmatter(lines3); index < lines3.length; index += 1) {
12306
12740
  const raw = lines3[index] ?? "";
12307
- if (FENCE3.test(raw)) {
12741
+ if (FENCE4.test(raw)) {
12308
12742
  fenced = !fenced;
12309
12743
  reader.flush();
12310
12744
  } else if (commented) {
@@ -12338,9 +12772,9 @@ var splitBlock = (block4) => {
12338
12772
  });
12339
12773
  };
12340
12774
  var unitsOf = (blocks) => {
12341
- let section2 = 0;
12775
+ let section3 = 0;
12342
12776
  return blocks.flatMap((block4, paragraph) => {
12343
- if (block4.kind === "heading") section2 += 1;
12777
+ if (block4.kind === "heading") section3 += 1;
12344
12778
  const pieces = splitBlock(block4);
12345
12779
  const next2 = blocks[paragraph + 1];
12346
12780
  const closesSection = block4.kind === "sentence" && (next2 === void 0 || next2.kind === "heading");
@@ -12348,7 +12782,7 @@ var unitsOf = (blocks) => {
12348
12782
  text: piece.text,
12349
12783
  line: piece.line,
12350
12784
  paragraph,
12351
- section: section2,
12785
+ section: section3,
12352
12786
  kind: block4.kind,
12353
12787
  lastInParagraph: index === pieces.length - 1,
12354
12788
  closesSection
@@ -12576,9 +13010,9 @@ var phraseHits = (units, catalog) => units.flatMap(
12576
13010
  })
12577
13011
  );
12578
13012
  var windowEnd = (units, from, size) => {
12579
- const section2 = units[from]?.section;
13013
+ const section3 = units[from]?.section;
12580
13014
  let to = from;
12581
- while (to + 1 < Math.min(units.length, from + size) && units[to + 1]?.section === section2) to += 1;
13015
+ while (to + 1 < Math.min(units.length, from + size) && units[to + 1]?.section === section3) to += 1;
12582
13016
  return to;
12583
13017
  };
12584
13018
  var windowScore = (hits, window) => hits.filter((item2) => item2.unit >= window.from && item2.unit <= window.to).reduce((sum, item2) => sum + item2.weight, 0);
@@ -12745,8 +13179,8 @@ var PROBE_MARGIN_MS = 15e3;
12745
13179
  var MIN_REMAINING_MS2 = 5e3;
12746
13180
  var TAIL2 = 160;
12747
13181
  var lastLine = (text9) => {
12748
- const rows = text9.split(/\r?\n/).filter((row) => row.trim() !== "" && !/^\s*at\s|^Node\.js v\d/.test(row));
12749
- const line2 = (rows.findLast((row) => /error/i.test(row)) ?? rows.at(-1))?.trim() ?? "";
13182
+ const rows = text9.split(/\r?\n/).filter((row2) => row2.trim() !== "" && !/^\s*at\s|^Node\.js v\d/.test(row2));
13183
+ const line2 = (rows.findLast((row2) => /error/i.test(row2)) ?? rows.at(-1))?.trim() ?? "";
12750
13184
  return line2.length > TAIL2 ? `${line2.slice(0, TAIL2)}…` : line2;
12751
13185
  };
12752
13186
  var args = (check2, timeoutS) => [
@@ -12823,23 +13257,23 @@ ${run4.stderr}`)) })]);
12823
13257
  };
12824
13258
 
12825
13259
  // src/secrets/baseline.ts
12826
- import { join as join25 } from "node:path";
13260
+ import { join as join26 } from "node:path";
12827
13261
  var BASELINE_FILE2 = ".finitemonkey/findings/baseline.json";
12828
13262
  var BASELINE_SCHEMA = "finitemonkey/secrets-baseline@1";
12829
13263
  var BASELINE_STATES = ["aceito", "falso-positivo"];
12830
13264
  var EXCEPTION_MAX_DAYS = 90;
12831
13265
  var EMPTY_BASELINE = { schema: BASELINE_SCHEMA, entries: [] };
12832
- var baselinePath = (root) => toAbsolutePath(join25(root, BASELINE_FILE2));
13266
+ var baselinePath = (root) => toAbsolutePath(join26(root, BASELINE_FILE2));
12833
13267
  var text7 = (record4, key) => {
12834
13268
  const value = record4[key];
12835
13269
  return typeof value === "string" && value !== "" ? value : void 0;
12836
13270
  };
12837
- var stateOf = (value) => BASELINE_STATES.find((state) => state === value);
13271
+ var stateOf2 = (value) => BASELINE_STATES.find((state) => state === value);
12838
13272
  var parseEntry2 = (value) => {
12839
13273
  if (!isRecord(value)) return void 0;
12840
13274
  const fields = ["id", "rule", "file", "fingerprint", "owner", "reason", "acceptedAt", "expiresAt"];
12841
13275
  const [id, rule, file, fingerprint, owner, reason, acceptedAt, expiresAt] = fields.map((key) => text7(value, key));
12842
- const state = stateOf(value["state"]);
13276
+ const state = stateOf2(value["state"]);
12843
13277
  const line2 = value["line"];
12844
13278
  if (id === void 0 || rule === void 0 || file === void 0 || fingerprint === void 0) return void 0;
12845
13279
  if (owner === void 0 || reason === void 0 || acceptedAt === void 0 || expiresAt === void 0)
@@ -12889,14 +13323,14 @@ var withoutFixed = (baseline, fixed) => {
12889
13323
  var acceptedFingerprints = (baseline, now) => new Set(baseline.entries.filter((entry2) => entry2.expiresAt >= now).map((entry2) => entry2.fingerprint));
12890
13324
 
12891
13325
  // src/secrets/incidents.ts
12892
- import { join as join26 } from "node:path";
13326
+ import { join as join27 } from "node:path";
12893
13327
  var INCIDENTS_DIR = ".finitemonkey/incidents";
12894
13328
  var INCIDENT_STATES = ["aberto", "exposicao-local", "rotacionado", "fechado"];
12895
13329
  var INCIDENT_SOURCES = ["tool-output", "prompt", "history", "scan"];
12896
13330
  var ID = /^inc-[0-9a-f]{12}$/;
12897
13331
  var incidentId = (rule, fingerprint) => `inc-${sha256Hex(`${rule}\0${fingerprint}`).slice(0, 12)}`;
12898
- var incidentsDir = (root) => toAbsolutePath(join26(root, INCIDENTS_DIR));
12899
- var incidentFile = (root, id) => toAbsolutePath(join26(incidentsDir(root), `${id}.json`));
13332
+ var incidentsDir = (root) => toAbsolutePath(join27(root, INCIDENTS_DIR));
13333
+ var incidentFile = (root, id) => toAbsolutePath(join27(incidentsDir(root), `${id}.json`));
12900
13334
  var pick3 = (options, value) => options.find((option) => option === value);
12901
13335
  var str2 = (record4, key) => {
12902
13336
  const value = record4[key];
@@ -12918,19 +13352,19 @@ var parseIncident = (raw) => {
12918
13352
  const parsed = parseJsonObject(raw);
12919
13353
  if (!parsed.ok) return parsed;
12920
13354
  const record4 = parsed.value;
12921
- const [id, rule, where, fingerprint, createdAt, updatedAt] = ["id", "rule", "where", "fingerprint", "createdAt", "updatedAt"].map((key) => str2(record4, key));
13355
+ const [id, rule, where2, fingerprint, createdAt, updatedAt] = ["id", "rule", "where", "fingerprint", "createdAt", "updatedAt"].map((key) => str2(record4, key));
12922
13356
  const state = pick3(INCIDENT_STATES, record4["state"]);
12923
13357
  const source = pick3(INCIDENT_SOURCES, record4["source"]);
12924
13358
  const history = parseHistory(record4["history"]);
12925
13359
  const firstCommit = str2(record4, "firstCommit");
12926
- if (id === void 0 || !ID.test(id) || rule === void 0 || where === void 0) return reject("incomplete");
13360
+ if (id === void 0 || !ID.test(id) || rule === void 0 || where2 === void 0) return reject("incomplete");
12927
13361
  if (fingerprint === void 0 || createdAt === void 0 || updatedAt === void 0) return reject("incomplete");
12928
13362
  if (state === void 0 || source === void 0 || history === void 0) return reject("incomplete");
12929
13363
  const base = {
12930
13364
  schemaVersion: 1,
12931
13365
  id,
12932
13366
  rule,
12933
- where,
13367
+ where: where2,
12934
13368
  fingerprint,
12935
13369
  source,
12936
13370
  state,
@@ -12951,7 +13385,7 @@ var listIncidents = (runtime, root) => {
12951
13385
  return names.value.filter((name) => name.endsWith(".json")).map((name) => readIncident(runtime, root, name.slice(0, -".json".length))).flatMap((parsed) => parsed.ok ? [parsed.value] : []).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
12952
13386
  };
12953
13387
  var saveIncident = (runtime, root, incident) => {
12954
- const ignore = runtime.writeText(toAbsolutePath(join26(incidentsDir(root), ".gitignore")), "*\n");
13388
+ const ignore = runtime.writeText(toAbsolutePath(join27(incidentsDir(root), ".gitignore")), "*\n");
12955
13389
  if (!ignore.ok) return ignore;
12956
13390
  return runtime.writeText(incidentFile(root, incident.id), `${JSON.stringify(incident, null, 2)}
12957
13391
  `);
@@ -12998,7 +13432,7 @@ var moveIncident = (incident, to, now, by) => ({
12998
13432
  var isRotated = (incident) => incident.state === "rotacionado" || incident.state === "fechado";
12999
13433
 
13000
13434
  // src/secrets/scan.ts
13001
- import { join as join27, relative as relative6 } from "node:path";
13435
+ import { join as join28, relative as relative6 } from "node:path";
13002
13436
 
13003
13437
  // src/secrets/located.ts
13004
13438
  var findingId = (ruleId, file, fingerprint) => `fm-${sha256Hex(`${ruleId}\0${file}\0${fingerprint}`).slice(0, 16)}`;
@@ -13059,7 +13493,7 @@ var diffScanner = () => {
13059
13493
  collect2(chunk, commit2, found);
13060
13494
  chunk = fresh2();
13061
13495
  };
13062
- const header = (line2) => {
13496
+ const header2 = (line2) => {
13063
13497
  const match = COMMIT.exec(line2);
13064
13498
  if (match !== null) {
13065
13499
  flush2();
@@ -13075,7 +13509,7 @@ var diffScanner = () => {
13075
13509
  return {
13076
13510
  push: (raw) => {
13077
13511
  const line2 = withoutCarriageReturn(raw);
13078
- if (!header(line2)) addLine(chunk, line2);
13512
+ if (!header2(line2)) addLine(chunk, line2);
13079
13513
  },
13080
13514
  end: () => {
13081
13515
  flush2();
@@ -13086,7 +13520,7 @@ var diffScanner = () => {
13086
13520
 
13087
13521
  // src/secrets/scan.ts
13088
13522
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
13089
- var SKIPPED = /(?:^|\/)(?:\.git|node_modules|\.finitemonkey)\//;
13523
+ var SKIPPED2 = /(?:^|\/)(?:\.git|node_modules|\.finitemonkey)\//;
13090
13524
  var DIFF_FLAGS = ["-U0", "--no-color", "--no-ext-diff", "--no-renames"];
13091
13525
  var binary = (text9) => text9.slice(0, 8e3).includes("\0");
13092
13526
  var listed2 = (runtime, root, target2) => {
@@ -13098,7 +13532,7 @@ var listed2 = (runtime, root, target2) => {
13098
13532
  const base = toAbsolutePath(target2, root);
13099
13533
  const walked = runtime.walkFiles(base);
13100
13534
  if (!walked.ok) return walked;
13101
- return accept(walked.value.map((name) => toPosixPath(relative6(root, join27(base, toPosixPath(name))))));
13535
+ return accept(walked.value.map((name) => toPosixPath(relative6(root, join28(base, toPosixPath(name))))));
13102
13536
  };
13103
13537
  var scanFile = (runtime, root, file) => {
13104
13538
  const text9 = runtime.readText(toAbsolutePath(file, root));
@@ -13108,12 +13542,12 @@ var scanFile = (runtime, root, file) => {
13108
13542
  var scanPath = (runtime, root, target2) => {
13109
13543
  const files = listed2(runtime, root, target2);
13110
13544
  if (!files.ok) return files;
13111
- return accept(files.value.filter((file) => !SKIPPED.test(file)).flatMap((file) => scanFile(runtime, root, file)));
13545
+ return accept(files.value.filter((file) => !SKIPPED2.test(file)).flatMap((file) => scanFile(runtime, root, file)));
13112
13546
  };
13113
13547
  var diffFindings = (diff) => {
13114
13548
  const scanner = diffScanner();
13115
13549
  for (const line2 of diff.split("\n")) scanner.push(line2);
13116
- return scanner.end().filter((finding4) => !SKIPPED.test(finding4.file));
13550
+ return scanner.end().filter((finding4) => !SKIPPED2.test(finding4.file));
13117
13551
  };
13118
13552
  var scanStaged = (runtime, root, includeUnstaged = false) => {
13119
13553
  const args2 = includeUnstaged ? ["diff", "HEAD", ...DIFF_FLAGS] : ["diff", "--cached", ...DIFF_FLAGS];
@@ -13126,14 +13560,14 @@ var scanHistory = async (runtime, root) => {
13126
13560
  const args2 = ["log", "-p", "--all", "--format=commit %H", ...DIFF_FLAGS];
13127
13561
  const git2 = await runtime.gitLines(args2, root, scanner.push);
13128
13562
  if (!git2.ok) return reject(git2.found ? "git log failed" : "git not found");
13129
- return accept(scanner.end().filter((finding4) => !SKIPPED.test(finding4.file)));
13563
+ return accept(scanner.end().filter((finding4) => !SKIPPED2.test(finding4.file)));
13130
13564
  };
13131
13565
 
13132
13566
  // src/secrets/gate.ts
13133
13567
  var untracked2 = (runtime, root) => {
13134
- const listed4 = runtime.git(["ls-files", "-z", "--others", "--exclude-standard"], root);
13135
- if (!listed4.ok) return [];
13136
- return listed4.stdout.split("\0").map(toPosixPath).filter((file) => file !== "" && !file.startsWith(".finitemonkey/")).flatMap((file) => scanFile(runtime, root, file));
13568
+ const listed5 = runtime.git(["ls-files", "-z", "--others", "--exclude-standard"], root);
13569
+ if (!listed5.ok) return [];
13570
+ return listed5.stdout.split("\0").map(toPosixPath).filter((file) => file !== "" && !file.startsWith(".finitemonkey/")).flatMap((file) => scanFile(runtime, root, file));
13137
13571
  };
13138
13572
  var secretsGateStatus = (runtime, root, since) => {
13139
13573
  const changed = scanStaged(runtime, root, true);
@@ -13168,7 +13602,7 @@ var secretsCheck = (_check, ctx) => {
13168
13602
  };
13169
13603
 
13170
13604
  // src/gate/checks/tests.ts
13171
- import { join as join28 } from "node:path";
13605
+ import { join as join29 } from "node:path";
13172
13606
 
13173
13607
  // src/guards/tests/tree.ts
13174
13608
  var outOfScope = (input, change) => input.testsProtected && input.scopePaths.length > 0 && change.status !== "A" && isTestFile(change.path) && !matchesAny(change.path, input.scopePaths) ? [{ kind: "out_of_scope", path: change.path }] : [];
@@ -13200,7 +13634,7 @@ var treeViolations = (ctx) => {
13200
13634
  changes: relevant,
13201
13635
  before: (path) => fileAt(ctx.git, ctx.base, path),
13202
13636
  after: (path) => {
13203
- const text9 = ctx.runtime.readText(toAbsolutePath(join28(ctx.paths.root, path)));
13637
+ const text9 = ctx.runtime.readText(toAbsolutePath(join29(ctx.paths.root, path)));
13204
13638
  return text9.ok ? text9.value : void 0;
13205
13639
  },
13206
13640
  acceptedConfigs: ctx.trust.configs,
@@ -13221,7 +13655,7 @@ var testsCheck = (check2, ctx) => {
13221
13655
  };
13222
13656
 
13223
13657
  // src/git/tree.ts
13224
- import { join as join29 } from "node:path";
13658
+ import { join as join30 } from "node:path";
13225
13659
 
13226
13660
  // src/secrets/guards/paths.ts
13227
13661
  var SAFE_ENV = /^\.env\.(?:example|sample|template|dist|defaults|schema)$/i;
@@ -13252,15 +13686,15 @@ var run = (runtime, repo, args2, env, options, input) => {
13252
13686
  return result.ok ? accept(result.stdout.trim()) : reject(failure(args2[0] ?? "git", result));
13253
13687
  };
13254
13688
  var dropSensitive = (runtime, repo, env, options) => {
13255
- const listed4 = run(runtime, repo, ["ls-files", "-z"], env, options);
13256
- if (!listed4.ok) return listed4;
13257
- const sensitive = listed4.value.split("\0").filter((path) => path !== "" && isSensitivePath(path));
13689
+ const listed5 = run(runtime, repo, ["ls-files", "-z"], env, options);
13690
+ if (!listed5.ok) return listed5;
13691
+ const sensitive = listed5.value.split("\0").filter((path) => path !== "" && isSensitivePath(path));
13258
13692
  if (sensitive.length === 0) return accept(void 0);
13259
13693
  const removed = run(runtime, repo, ["rm", "--cached", "-q", "--ignore-unmatch", "--", ...sensitive], env, options);
13260
13694
  return removed.ok ? accept(void 0) : removed;
13261
13695
  };
13262
13696
  var withTempIndex = (runtime, repo, options, work) => {
13263
- const path = toAbsolutePath(join29(repo.gitDir, `finitemonkey-${options.stamp}.index`));
13697
+ const path = toAbsolutePath(join30(repo.gitDir, `finitemonkey-${options.stamp}.index`));
13264
13698
  const env = { GIT_INDEX_FILE: path, GIT_OPTIONAL_LOCKS: "0" };
13265
13699
  try {
13266
13700
  return work(env);
@@ -13304,8 +13738,8 @@ var commitTree = (runtime, repo, tree, parents, message, options) => run(
13304
13738
  // src/git/merge/diff-parse.ts
13305
13739
  var HUNK3 = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
13306
13740
  var BINARY = /^Binary files (?:a\/(.+)|\/dev\/null) and (?:b\/(.+)|\/dev\/null) differ$/;
13307
- var pathAfter = (line2, prefix) => {
13308
- const value = line2.slice(prefix.length).replace(/\t$/, "");
13741
+ var pathAfter = (line2, prefix2) => {
13742
+ const value = line2.slice(prefix2.length).replace(/\t$/, "");
13309
13743
  return value === "/dev/null" ? void 0 : value.replace(/^[ab]\//, "");
13310
13744
  };
13311
13745
  var flushHunk = (draft) => {
@@ -13468,7 +13902,7 @@ var mergeCommitsSince = (runtime, repo, since) => {
13468
13902
  var mergeInProgress = (runtime, repo) => revParse(runtime, repo, "MERGE_HEAD") !== void 0;
13469
13903
 
13470
13904
  // src/git/merge/dropped.ts
13471
- import { join as join30 } from "node:path";
13905
+ import { join as join31 } from "node:path";
13472
13906
 
13473
13907
  // src/git/merge/lines.ts
13474
13908
  var normalizeLine = (line2) => line2.replace(/\r$/, "").trim().replace(/\s*,$/, "");
@@ -13548,7 +13982,7 @@ var parseDropped = (text9) => {
13548
13982
  return accept({ items, invalid });
13549
13983
  };
13550
13984
  var loadDropped = (runtime, root, file) => {
13551
- const path = toAbsolutePath(file ?? join30(root, DROPPED_FILE), root);
13985
+ const path = toAbsolutePath(file ?? join31(root, DROPPED_FILE), root);
13552
13986
  const text9 = runtime.readText(path);
13553
13987
  if (!text9.ok) return file === void 0 ? accept({ items: [], invalid: [] }) : reject(`${file}: ${text9.error}`);
13554
13988
  return parseDropped(text9.value);
@@ -13558,7 +13992,7 @@ var isDropped = (finding4, items) => items.some(
13558
13992
  );
13559
13993
 
13560
13994
  // src/git/merge/generated.ts
13561
- import { join as join31 } from "node:path";
13995
+ import { join as join32 } from "node:path";
13562
13996
  var LOCKFILES3 = /* @__PURE__ */ new Set([
13563
13997
  "package-lock.json",
13564
13998
  "npm-shrinkwrap.json",
@@ -13621,7 +14055,7 @@ var verifyGenerated = (runtime, repo, inputs, paths, generators) => {
13621
14055
  if (declared.length === 0) return accept([]);
13622
14056
  const commit2 = resultCommit(runtime, repo, inputs);
13623
14057
  if (!commit2.ok) return commit2;
13624
- const dir = join31(repo.gitDir, `finitemonkey-generate-${runtime.now().replace(/[:.]/g, "-")}`);
14058
+ const dir = join32(repo.gitDir, `finitemonkey-generate-${runtime.now().replace(/[:.]/g, "-")}`);
13625
14059
  if (!runtime.git(["worktree", "add", "--detach", dir, commit2.value], repo.root).ok)
13626
14060
  return reject("could not check out the merge result");
13627
14061
  try {
@@ -13631,7 +14065,7 @@ var verifyGenerated = (runtime, repo, inputs, paths, generators) => {
13631
14065
  }
13632
14066
  return accept(
13633
14067
  declared.flatMap(({ path }) => {
13634
- const fresh3 = runtime.readText(toAbsolutePath(join31(dir, path)));
14068
+ const fresh3 = runtime.readText(toAbsolutePath(join32(dir, path)));
13635
14069
  const merged2 = blobText(runtime, repo, commit2.value, path);
13636
14070
  if (fresh3.ok && fresh3.value === merged2) return [];
13637
14071
  return [{ kind: "generated-differs", file: path, side: void 0, line: void 0, text: path, weak: false }];
@@ -13742,14 +14176,14 @@ var DECLARATION = /\b(?:function\*?|class|interface|type|enum|def|fn|func|struct
13742
14176
  var BINDING = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*[:=]/g;
13743
14177
  var KEY = /^["']?([A-Za-z_$][\w$.-]*)["']?\s*:(?!:)/;
13744
14178
  var TEST = /\b(?:it|test|describe|context)(?:\.\w+)?\s*\(\s*(["'`])(.+?)\1/;
13745
- var RESERVED = /* @__PURE__ */ new Set(["default", "case", "http", "https"]);
14179
+ var RESERVED2 = /* @__PURE__ */ new Set(["default", "case", "http", "https"]);
13746
14180
  var symbolsOf = (line2) => {
13747
14181
  if (isWeak(line2)) return [];
13748
14182
  const names = [...line2.matchAll(DECLARATION), ...line2.matchAll(BINDING)].map((match) => match[1] ?? "");
13749
14183
  const key = KEY.exec(line2)?.[1];
13750
14184
  const title = TEST.exec(line2)?.[2];
13751
14185
  return [
13752
- ...[...names, ...key === void 0 ? [] : [key]].filter((name) => name.length > 1 && !RESERVED.has(name)).map((name) => ({ name, test: false })),
14186
+ ...[...names, ...key === void 0 ? [] : [key]].filter((name) => name.length > 1 && !RESERVED2.has(name)).map((name) => ({ name, test: false })),
13753
14187
  ...title === void 0 ? [] : [{ name: title, test: true }]
13754
14188
  ];
13755
14189
  };
@@ -13917,19 +14351,19 @@ var verifyMerge = (runtime, repo, options = {}) => {
13917
14351
  };
13918
14352
 
13919
14353
  // src/git/ownership.ts
13920
- import { join as join32 } from "node:path";
14354
+ import { join as join33 } from "node:path";
13921
14355
  var OWNERSHIP_DIR = ".finitemonkey/git";
13922
14356
  var ownerKey = (raw) => {
13923
14357
  const cleaned = (raw ?? "").replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 80);
13924
14358
  return cleaned === "" ? "unknown" : cleaned;
13925
14359
  };
13926
- var fileOf = (repo, kind, owner) => toAbsolutePath(join32(repo.root, OWNERSHIP_DIR, kind, `${ownerKey(owner)}.json`));
14360
+ var fileOf = (repo, kind, owner) => toAbsolutePath(join33(repo.root, OWNERSHIP_DIR, kind, `${ownerKey(owner)}.json`));
13927
14361
  var readDirty = (runtime, root) => {
13928
14362
  const status = runtime.git(["status", "--porcelain=v2", "-z", "--untracked-files=all"], root);
13929
14363
  return status.ok ? parsePorcelainV2(status.stdout) : void 0;
13930
14364
  };
13931
14365
  var hashDirty = (runtime, repo, entries) => {
13932
- const present2 = entries.filter((entry2) => runtime.stat(toAbsolutePath(join32(repo.root, entry2.path)))?.kind === "file");
14366
+ const present2 = entries.filter((entry2) => runtime.stat(toAbsolutePath(join33(repo.root, entry2.path)))?.kind === "file");
13933
14367
  const hashed = present2.length === 0 ? void 0 : runtime.gitWith(["hash-object", "--stdin-paths"], repo.root, {
13934
14368
  input: `${present2.map((entry2) => entry2.path).join("\n")}
13935
14369
  `
@@ -14013,11 +14447,11 @@ var foreignFiles = (runtime, repo, owner, targets2) => {
14013
14447
  };
14014
14448
 
14015
14449
  // src/git/gate-status.ts
14016
- var lines = (text9) => text9.split("\n").filter((line2) => line2.trim() !== "");
14450
+ var lines2 = (text9) => text9.split("\n").filter((line2) => line2.trim() !== "");
14017
14451
  var unpushedSince = (runtime, repo, since) => {
14018
- if (lines(runtime.git(["remote"], repo.root).stdout).length === 0) return [];
14452
+ if (lines2(runtime.git(["remote"], repo.root).stdout).length === 0) return [];
14019
14453
  const result = runtime.git(["rev-list", `${since}..HEAD`, "--not", "--remotes"], repo.root);
14020
- return result.ok ? lines(result.stdout) : [];
14454
+ return result.ok ? lines2(result.stdout) : [];
14021
14455
  };
14022
14456
  var worktreeGateStatus = (runtime, repo, owner) => {
14023
14457
  const edits = readEdits(runtime, repo, owner);
@@ -14037,8 +14471,8 @@ var mergeGateStatus = (runtime, repo, owner) => {
14037
14471
  return [...pending, ...commits.map((commit2) => ({ commit: commit2, report: verifyMerge(runtime, repo, { rev: commit2 }) }))];
14038
14472
  };
14039
14473
  var findingLabel = (finding4) => {
14040
- const where = finding4.line === void 0 ? finding4.file : `${finding4.file}:${String(finding4.line)}`;
14041
- return `${where} ${finding4.text === "" ? finding4.kind : `"${finding4.text}"`}`;
14474
+ const where2 = finding4.line === void 0 ? finding4.file : `${finding4.file}:${String(finding4.line)}`;
14475
+ return `${where2} ${finding4.text === "" ? finding4.kind : `"${finding4.text}"`}`;
14042
14476
  };
14043
14477
 
14044
14478
  // src/gate/checks/worktree.ts
@@ -14105,7 +14539,8 @@ var DEFAULT_CHECKS = {
14105
14539
  review: reviewCheck,
14106
14540
  callers: callersCheck,
14107
14541
  runtime: runtimeCheck,
14108
- look: lookCheck
14542
+ look: lookCheck,
14543
+ inventory: inventoryCheck
14109
14544
  };
14110
14545
 
14111
14546
  // src/gate/run.ts
@@ -14299,7 +14734,7 @@ var subagentStopGate = (input) => {
14299
14734
  };
14300
14735
 
14301
14736
  // src/gate/hooks/subagent.ts
14302
- import { join as join33 } from "node:path";
14737
+ import { join as join34 } from "node:path";
14303
14738
  var NO_CONTRACT_AGENTS = /(^|:)(reviewer|verifier|explore|plan|claude-code-guide|statusline-setup)$/i;
14304
14739
  var KNOWN_KINDS = /* @__PURE__ */ new Set([
14305
14740
  "command",
@@ -14336,13 +14771,13 @@ var subagentStart = (input) => {
14336
14771
  const { runtime, t } = input;
14337
14772
  const active = readState(runtime, paths).active;
14338
14773
  if (active === void 0) return { kind: "allow" };
14339
- const disk = (relative11) => {
14340
- const text9 = runtime.readText(toAbsolutePath(join33(paths.root, relative11)));
14774
+ const disk = (relative12) => {
14775
+ const text9 = runtime.readText(toAbsolutePath(join34(paths.root, relative12)));
14341
14776
  return text9.ok ? text9.value : void 0;
14342
14777
  };
14343
14778
  const loaded = loadTask(runtime, paths, { disk, base: () => void 0 }, active);
14344
14779
  if (!loaded.ok) return { kind: "allow" };
14345
- const briefing = runtime.stat(toAbsolutePath(join33(paths.dir, "briefing.md"))) !== void 0;
14780
+ const briefing = runtime.stat(toAbsolutePath(join34(paths.dir, "briefing.md"))) !== void 0;
14346
14781
  return { kind: "allow", output: contextOutput("SubagentStart", contractFacts(t, loaded.value.contract, briefing)) };
14347
14782
  };
14348
14783
 
@@ -14449,14 +14884,14 @@ var resolveFrom = (base, ...parts) => parts.reduce((current2, part2) => {
14449
14884
  import { posix as posix4 } from "node:path";
14450
14885
  var MAGIC = /^:(?:\(([^)]*)\)|([!^/]*))(.*)$/s;
14451
14886
  var GLOB = /[*?[]/;
14452
- var specOf = (raw, prefix) => {
14887
+ var specOf = (raw, prefix2) => {
14453
14888
  const posixRaw = toPosixPath(raw);
14454
14889
  const magic = MAGIC.exec(posixRaw);
14455
14890
  const words = magic === null ? "" : magic[1] ?? magic[2] ?? "";
14456
14891
  const rest = magic === null ? posixRaw : magic[3] ?? "";
14457
14892
  const exclude = /(?:^|,)exclude(?:,|$)|[!^]/.test(words);
14458
14893
  const top = /(?:^|,)top(?:,|$)|\//.test(words);
14459
- const joined = posix4.normalize(top ? rest : posix4.join(prefix === "" ? "." : prefix, rest));
14894
+ const joined = posix4.normalize(top ? rest : posix4.join(prefix2 === "" ? "." : prefix2, rest));
14460
14895
  return { pattern: joined === "./" ? "." : joined.replace(/\/$/, ""), exclude };
14461
14896
  };
14462
14897
  var globRegex3 = (pattern) => {
@@ -14468,8 +14903,8 @@ var matchesOne = (file, pattern) => {
14468
14903
  if (GLOB.test(pattern)) return globRegex3(pattern).test(file);
14469
14904
  return file === pattern || file.startsWith(`${pattern}/`);
14470
14905
  };
14471
- var matchPathspecs = (file, specs, prefix) => {
14472
- const parsed = specs.map((raw) => specOf(raw, prefix));
14906
+ var matchPathspecs = (file, specs, prefix2) => {
14907
+ const parsed = specs.map((raw) => specOf(raw, prefix2));
14473
14908
  const include = parsed.filter((spec2) => !spec2.exclude);
14474
14909
  const included = include.length === 0 || include.some((spec2) => matchesOne(file, spec2.pattern));
14475
14910
  return included && !parsed.some((spec2) => spec2.exclude && matchesOne(file, spec2.pattern));
@@ -15006,11 +15441,11 @@ var expandAlias = (runtime, repo, invocation, depth = 0) => {
15006
15441
  const words = normalizeCommand(`git ${configured}`).commands[0]?.args ?? [];
15007
15442
  return expandAlias(runtime, repo, withAlias(invocation, words), depth + 1);
15008
15443
  };
15009
- var targetsOf = (entries, spec2, prefix) => entries.filter((entry2) => {
15444
+ var targetsOf = (entries, spec2, prefix2) => entries.filter((entry2) => {
15010
15445
  if (spec2.scope === "tracked") return entry2.kind !== "untracked";
15011
15446
  if (spec2.scope === "worktree") return entry2.kind !== "untracked" && entry2.unstaged;
15012
15447
  return true;
15013
- }).filter((entry2) => spec2.pathspecs.length === 0 || matchPathspecs(entry2.path, spec2.pathspecs, prefix)).map((entry2) => entry2.path);
15448
+ }).filter((entry2) => spec2.pathspecs.length === 0 || matchPathspecs(entry2.path, spec2.pathspecs, prefix2)).map((entry2) => entry2.path);
15014
15449
  var shellAliasCommands = (invocation, runtime, repo) => {
15015
15450
  const configured = invocation.config[`alias.${invocation.subcommand.toLowerCase()}`] ?? runtime.git(["config", "--get", `alias.${invocation.subcommand}`], repo.root).stdout.trim();
15016
15451
  return configured.startsWith("!") ? normalizeCommand(configured.slice(1)).commands : [];
@@ -15117,16 +15552,16 @@ var rmVerdict = (runtime, context, command) => {
15117
15552
  return PASS;
15118
15553
  };
15119
15554
  var listWorktrees = (runtime, cwd) => {
15120
- const listed4 = runtime.git(["worktree", "list", "--porcelain"], toAbsolutePath(cwd));
15121
- if (!listed4.ok) return [];
15122
- return listed4.stdout.split("\n").filter((line2) => line2.startsWith("worktree ")).map((line2) => pathKey(line2.slice("worktree ".length)));
15555
+ const listed5 = runtime.git(["worktree", "list", "--porcelain"], toAbsolutePath(cwd));
15556
+ if (!listed5.ok) return [];
15557
+ return listed5.stdout.split("\n").filter((line2) => line2.startsWith("worktree ")).map((line2) => pathKey(line2.slice("worktree ".length)));
15123
15558
  };
15124
15559
 
15125
15560
  // src/git/journal.ts
15126
- import { join as join34 } from "node:path";
15561
+ import { join as join35 } from "node:path";
15127
15562
  var JOURNAL_FILE = ".finitemonkey/git-journal.jsonl";
15128
15563
  var JOURNAL_SCHEMA = "finitemonkey/git-journal@1";
15129
- var journalPath = (root) => toAbsolutePath(join34(root, JOURNAL_FILE));
15564
+ var journalPath = (root) => toAbsolutePath(join35(root, JOURNAL_FILE));
15130
15565
  var headsOf = (value) => isRecord(value) ? Object.fromEntries(
15131
15566
  Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
15132
15567
  ) : {};
@@ -15162,7 +15597,7 @@ var readJournal = (runtime, root) => {
15162
15597
  };
15163
15598
  var appendJournal = (runtime, root, entry2) => {
15164
15599
  const path = journalPath(root);
15165
- const made = runtime.mkdir(toAbsolutePath(join34(root, ".finitemonkey")));
15600
+ const made = runtime.mkdir(toAbsolutePath(join35(root, ".finitemonkey")));
15166
15601
  if (!made.ok) return made;
15167
15602
  const previous = runtime.readText(path);
15168
15603
  const before = previous.ok ? previous.value : "";
@@ -15183,13 +15618,13 @@ var optionalLine = (runtime, repo, args2) => {
15183
15618
  return result.ok && value !== "" ? value : void 0;
15184
15619
  };
15185
15620
  var branchHeads = (runtime, repo) => {
15186
- const listed4 = runtime.git(
15621
+ const listed5 = runtime.git(
15187
15622
  ["for-each-ref", "--format=%(refname) %(objectname)", "refs/heads", "refs/stash"],
15188
15623
  repo.root
15189
15624
  );
15190
- if (!listed4.ok) return {};
15625
+ if (!listed5.ok) return {};
15191
15626
  return Object.fromEntries(
15192
- listed4.stdout.split("\n").flatMap((line2) => {
15627
+ listed5.stdout.split("\n").flatMap((line2) => {
15193
15628
  const [ref, sha] = line2.split(" ");
15194
15629
  return ref === void 0 || sha === void 0 || ref === "" ? [] : [[ref, sha]];
15195
15630
  })
@@ -15244,8 +15679,8 @@ var takeSnapshot = (runtime, repo, request) => {
15244
15679
  return logged.ok ? accept(entry2) : reject(`journal: ${logged.error}`);
15245
15680
  };
15246
15681
  var listSnapshots = (runtime, repo) => {
15247
- const listed4 = runtime.git(["for-each-ref", "--sort=-refname", "--format=%(refname)", BACKUP_PREFIX], repo.root);
15248
- return listed4.ok ? listed4.stdout.split("\n").filter((line2) => line2 !== "") : [];
15682
+ const listed5 = runtime.git(["for-each-ref", "--sort=-refname", "--format=%(refname)", BACKUP_PREFIX], repo.root);
15683
+ return listed5.ok ? listed5.stdout.split("\n").filter((line2) => line2 !== "") : [];
15249
15684
  };
15250
15685
 
15251
15686
  // src/git/hooks/context.ts
@@ -15409,7 +15844,7 @@ var GIT_HANDLERS = {
15409
15844
 
15410
15845
  // src/policy/guards.ts
15411
15846
  var SHADOW_FILE = "shadow.json";
15412
- var numberOf = (value) => typeof value === "number" && Number.isInteger(value) ? value : 0;
15847
+ var numberOf2 = (value) => typeof value === "number" && Number.isInteger(value) ? value : 0;
15413
15848
  var readShadow = (runtime, paths) => {
15414
15849
  const text9 = runtime.readText(paths.cache(SHADOW_FILE));
15415
15850
  const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
@@ -15420,8 +15855,8 @@ var readShadow = (runtime, paths) => {
15420
15855
  [
15421
15856
  id,
15422
15857
  {
15423
- firings: numberOf(value["firings"]),
15424
- falsePositives: numberOf(value["falsePositives"]),
15858
+ firings: numberOf2(value["firings"]),
15859
+ falsePositives: numberOf2(value["falsePositives"]),
15425
15860
  promoted: value["promoted"] === true
15426
15861
  }
15427
15862
  ]
@@ -15795,9 +16230,9 @@ var PROSE_HANDLERS = {
15795
16230
  // src/secrets/guards/describe.ts
15796
16231
  var MAX_LISTED8 = 5;
15797
16232
  var describeFindings = (t, items) => {
15798
- const listed4 = items.slice(0, MAX_LISTED8).map(({ ruleId, where }) => t("secrets.found_item", { rule: ruleId, where }));
15799
- const rest = items.length - listed4.length;
15800
- return rest > 0 ? `${listed4.join("; ")}; ${t("secrets.found_more", { count: rest })}` : listed4.join("; ");
16233
+ const listed5 = items.slice(0, MAX_LISTED8).map(({ ruleId, where: where2 }) => t("secrets.found_item", { rule: ruleId, where: where2 }));
16234
+ const rest = items.length - listed5.length;
16235
+ return rest > 0 ? `${listed5.join("; ")}; ${t("secrets.found_more", { count: rest })}` : listed5.join("; ");
15801
16236
  };
15802
16237
  var lineWhere = (t, finding4) => t("secrets.where.line", { line: finding4.line, column: finding4.column });
15803
16238
 
@@ -15808,12 +16243,12 @@ var liveFindings = (input, texts2) => {
15808
16243
  const accepted2 = acceptedFingerprints(baseline.ok ? baseline.value : EMPTY_BASELINE, input.runtime.now());
15809
16244
  return texts2.flatMap((text9) => scanText(text9, { rules: CREDENTIAL_RULES })).filter((finding4) => !accepted2.has(finding4.fingerprint));
15810
16245
  };
15811
- var record3 = (input, findings, draft, where) => {
16246
+ var record3 = (input, findings, draft, where2) => {
15812
16247
  const ids2 = findings.map((finding4) => {
15813
16248
  const saved = recordIncident(
15814
16249
  input.runtime,
15815
16250
  input.cwd,
15816
- { ...draft, rule: finding4.ruleId, fingerprint: finding4.fingerprint, where: `${where}:${String(finding4.line)}` },
16251
+ { ...draft, rule: finding4.ruleId, fingerprint: finding4.fingerprint, where: `${where2}:${String(finding4.line)}` },
15817
16252
  HOOK_ACTOR
15818
16253
  );
15819
16254
  return saved.ok ? saved.value.id : input.t("secrets.incident.unsaved", { detail: saved.error });
@@ -16057,9 +16492,9 @@ var parseRemotes = (output) => {
16057
16492
  return remotes;
16058
16493
  };
16059
16494
  var remotesWithCredentials = (runtime, root) => {
16060
- const listed4 = runtime.git(["remote", "-v"], root);
16061
- if (!listed4.ok) return [];
16062
- return [...parseRemotes(listed4.stdout)].filter(([, urls]) => urls.some(hasCredential)).map(([name]) => name).sort();
16495
+ const listed5 = runtime.git(["remote", "-v"], root);
16496
+ if (!listed5.ok) return [];
16497
+ return [...parseRemotes(listed5.stdout)].filter(([, urls]) => urls.some(hasCredential)).map(([name]) => name).sort();
16063
16498
  };
16064
16499
 
16065
16500
  // src/secrets/guards/session.ts
@@ -16534,7 +16969,7 @@ var hookSchemaProblems = (hooks, claude) => {
16534
16969
  };
16535
16970
 
16536
16971
  // src/doctor/hook-sources.ts
16537
- import { join as join35 } from "node:path";
16972
+ import { join as join36 } from "node:path";
16538
16973
  var entriesOf = (source, hooks, own) => Object.entries(asRecord(hooks)).flatMap(
16539
16974
  ([event, groups]) => asList(groups).flatMap((group) => {
16540
16975
  const record4 = asRecord(group);
@@ -16547,7 +16982,7 @@ var projectHookFiles = (env) => readSettings(env).flatMap(({ scope: scope2, path
16547
16982
  const source = PROJECT_SCOPES[scope2];
16548
16983
  return source === void 0 || !("hooks" in settings) ? [] : [{ source, path, hooks: settings["hooks"] }];
16549
16984
  });
16550
- var pluginsDir = ({ env, runtime }) => join35(env["CLAUDE_CONFIG_DIR"] ?? join35(runtime.homeDir(), ".claude"), "plugins");
16985
+ var pluginsDir = ({ env, runtime }) => join36(env["CLAUDE_CONFIG_DIR"] ?? join36(runtime.homeDir(), ".claude"), "plugins");
16551
16986
  var installRecord = (value, root) => {
16552
16987
  const records = Array.isArray(value) ? value.filter(isRecord) : isRecord(value) ? [value] : [];
16553
16988
  return records.find((record4) => record4["projectPath"] === root) ?? records[0];
@@ -16558,13 +16993,13 @@ var readJson = (env, path) => {
16558
16993
  return parsed?.ok === true ? parsed.value : void 0;
16559
16994
  };
16560
16995
  var installedPlugin = (env) => {
16561
- const index = readJson(env, toAbsolutePath(join35(pluginsDir(env), "installed_plugins.json")));
16996
+ const index = readJson(env, toAbsolutePath(join36(pluginsDir(env), "installed_plugins.json")));
16562
16997
  const record4 = installRecord(asRecord(index?.["plugins"])[PLUGIN_ID], env.root);
16563
16998
  const installPath = record4 === void 0 ? void 0 : optionalString(record4, "installPath");
16564
16999
  if (record4 === void 0 || installPath === void 0) return void 0;
16565
17000
  const root = toAbsolutePath(installPath);
16566
- const manifest = readJson(env, toAbsolutePath(join35(root, ".claude-plugin", "plugin.json")));
16567
- const hooksPath = toAbsolutePath(join35(root, "hooks", "hooks.json"));
17001
+ const manifest = readJson(env, toAbsolutePath(join36(root, ".claude-plugin", "plugin.json")));
17002
+ const hooksPath = toAbsolutePath(join36(root, "hooks", "hooks.json"));
16568
17003
  const text9 = env.runtime.readText(hooksPath);
16569
17004
  const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
16570
17005
  return {
@@ -16609,7 +17044,7 @@ var schemaFindings = (env, plugin, claude) => {
16609
17044
  const files = [...projectHookFiles(env), ...plugin?.hooksFile === void 0 ? [] : [plugin.hooksFile]];
16610
17045
  const invalidJson = [
16611
17046
  ...unreadableProjectFiles(env),
16612
- ...plugin?.hooksText !== void 0 && plugin.hooksFile === void 0 ? [toAbsolutePath(join36(plugin.root, "hooks", "hooks.json"))] : []
17047
+ ...plugin?.hooksText !== void 0 && plugin.hooksFile === void 0 ? [toAbsolutePath(join37(plugin.root, "hooks", "hooks.json"))] : []
16613
17048
  ];
16614
17049
  return [
16615
17050
  ...invalidJson.flatMap((file) => schemaFinding(file, ["not valid JSON"], label2)),
@@ -16630,7 +17065,7 @@ var pluginFindings = (env, projectOwn) => {
16630
17065
  ];
16631
17066
  };
16632
17067
  var coverageFindings = (env, entries) => {
16633
- const installed = env.runtime.stat(toAbsolutePath(join36(env.root, ...LOCK_FILE.split("/")))) !== void 0;
17068
+ const installed = env.runtime.stat(toAbsolutePath(join37(env.root, ...LOCK_FILE.split("/")))) !== void 0;
16634
17069
  if (entries.length === 0 && !installed) {
16635
17070
  return [finding("hooks.registered", "warn", "doctor.hooks.none", {}, "doctor.hooks.fix_none")];
16636
17071
  }
@@ -16682,18 +17117,18 @@ var checkHooks = (env) => {
16682
17117
  };
16683
17118
 
16684
17119
  // src/doctor/instructions.ts
16685
- import { join as join37 } from "node:path";
17120
+ import { join as join38 } from "node:path";
16686
17121
  var INSTRUCTION_LINE_CEILING = 200;
16687
17122
  var TOP_INSTRUCTIONS = ["AGENTS.md", "CLAUDE.md", ".claude/CLAUDE.md", "CLAUDE.local.md"];
16688
17123
  var RULES_DIR = ".claude/rules";
16689
17124
  var SHOWN = 3;
16690
- var readProjectFile = (env, relative11) => {
16691
- const text9 = env.runtime.readText(toAbsolutePath(join37(env.root, ...relative11.split("/"))));
17125
+ var readProjectFile = (env, relative12) => {
17126
+ const text9 = env.runtime.readText(toAbsolutePath(join38(env.root, ...relative12.split("/"))));
16692
17127
  return text9.ok ? text9.value : void 0;
16693
17128
  };
16694
17129
  var instructionFiles = (env) => {
16695
- const listed4 = env.runtime.listDir(toAbsolutePath(join37(env.root, ...RULES_DIR.split("/"))));
16696
- const rules = listed4.ok ? listed4.value.filter((name) => name.endsWith(".md")).map((name) => `${RULES_DIR}/${name}`) : [];
17130
+ const listed5 = env.runtime.listDir(toAbsolutePath(join38(env.root, ...RULES_DIR.split("/"))));
17131
+ const rules = listed5.ok ? listed5.value.filter((name) => name.endsWith(".md")).map((name) => `${RULES_DIR}/${name}`) : [];
16697
17132
  return [...TOP_INSTRUCTIONS, ...rules].flatMap((path) => {
16698
17133
  const text9 = readProjectFile(env, path);
16699
17134
  return text9 === void 0 ? [] : [{ path, text: text9 }];
@@ -16797,12 +17232,12 @@ var checkInstructions = (env) => {
16797
17232
  };
16798
17233
 
16799
17234
  // src/doctor/line-endings.ts
16800
- import { join as join38 } from "node:path";
17235
+ import { join as join39 } from "node:path";
16801
17236
  var LF_RULE = /^\*\s.*\beol=lf\b/m;
16802
17237
  var checkLineEndings = ({ root, runtime }) => {
16803
17238
  const autocrlf = runtime.git(["config", "--get", "core.autocrlf"], root);
16804
17239
  if (!autocrlf.ok || autocrlf.stdout.trim().toLowerCase() !== "true") return [];
16805
- const attributes = runtime.readText(toAbsolutePath(join38(root, ".gitattributes")));
17240
+ const attributes = runtime.readText(toAbsolutePath(join39(root, ".gitattributes")));
16806
17241
  if (attributes.ok && LF_RULE.test(attributes.value)) return [];
16807
17242
  return [
16808
17243
  finding("git.autocrlf", "warn", "doctor.git.autocrlf", { file: ".gitattributes" }, "doctor.git.fix_autocrlf")
@@ -16810,7 +17245,7 @@ var checkLineEndings = ({ root, runtime }) => {
16810
17245
  };
16811
17246
 
16812
17247
  // src/doctor/memory.ts
16813
- import { join as join39 } from "node:path";
17248
+ import { join as join40 } from "node:path";
16814
17249
  var ceilingOf = (config) => {
16815
17250
  const value = configAt(parseConfigYaml(config ?? ""), "memory", "maxLines");
16816
17251
  const parsed = typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
@@ -16818,9 +17253,9 @@ var ceilingOf = (config) => {
16818
17253
  };
16819
17254
  var lineCount2 = (text9) => text9.replace(/\r?\n$/, "").split(/\r?\n/).length;
16820
17255
  var checkAgentsFile = ({ root, runtime }) => {
16821
- const text9 = runtime.readText(toAbsolutePath(join39(root, AGENTS_FILE)));
17256
+ const text9 = runtime.readText(toAbsolutePath(join40(root, AGENTS_FILE)));
16822
17257
  if (!text9.ok) return [];
16823
- const config = runtime.readText(toAbsolutePath(join39(root, CONFIG_FILE2)));
17258
+ const config = runtime.readText(toAbsolutePath(join40(root, CONFIG_FILE2)));
16824
17259
  const max = ceilingOf(config.ok ? config.value : void 0);
16825
17260
  const lines3 = lineCount2(text9.value);
16826
17261
  const params2 = { file: AGENTS_FILE, lines: lines3, max };
@@ -16828,7 +17263,7 @@ var checkAgentsFile = ({ root, runtime }) => {
16828
17263
  };
16829
17264
 
16830
17265
  // src/doctor/project-files.ts
16831
- import { join as join40 } from "node:path";
17266
+ import { join as join41 } from "node:path";
16832
17267
  var variants = (base, extensions) => extensions.map((extension) => `${base}${extension}`);
16833
17268
  var SCRIPT = [".js", ".cjs", ".mjs", ".ts", ".cts", ".mts"];
16834
17269
  var DATA = ["", ".json", ".yml", ".yaml"];
@@ -16859,7 +17294,7 @@ var CONFIG_GROUPS = [
16859
17294
  files: [...variants("commitlint.config", SCRIPT), ...variants(".commitlintrc", [...DATA, ".js"])]
16860
17295
  }
16861
17296
  ];
16862
- var present = (env, files) => files.filter((file) => env.runtime.stat(toAbsolutePath(join40(env.root, file)))?.kind === "file");
17297
+ var present = (env, files) => files.filter((file) => env.runtime.stat(toAbsolutePath(join41(env.root, file)))?.kind === "file");
16863
17298
  var crowded = (env, groups, build) => groups.flatMap(({ name, files }) => {
16864
17299
  const found = present(env, files);
16865
17300
  return found.length > 1 ? [build(name, found.join(", "))] : [];
@@ -17059,7 +17494,7 @@ var doctorCommand = {
17059
17494
  };
17060
17495
 
17061
17496
  // src/gain/gain.ts
17062
- import { join as join41 } from "node:path";
17497
+ import { join as join42 } from "node:path";
17063
17498
  var GAIN_SCHEMA = "finitemonkey/gain@1";
17064
17499
  var DAY_MS3 = 864e5;
17065
17500
  var SAVE_WINDOW_MS = DAY_MS3;
@@ -17075,8 +17510,8 @@ var readLog = (runtime, paths) => {
17075
17510
  });
17076
17511
  };
17077
17512
  var readGates = (runtime, paths) => {
17078
- const listed4 = runtime.listDir(toAbsolutePath(join41(paths.dir, "evidence")));
17079
- const files = listed4.ok ? listed4.value.filter((name) => name.endsWith(".jsonl")) : [];
17513
+ const listed5 = runtime.listDir(toAbsolutePath(join42(paths.dir, "evidence")));
17514
+ const files = listed5.ok ? listed5.value.filter((name) => name.endsWith(".jsonl")) : [];
17080
17515
  return files.flatMap((name) => readLedger2(runtime, paths.evidence(name.replace(/\.jsonl$/, "")))).filter((entry2) => entry2.check === GATE_CHECK);
17081
17516
  };
17082
17517
  var countsIn = (log, gates, from, to) => {
@@ -17122,7 +17557,7 @@ var ROWS = [
17122
17557
  { key: "gain.row.gate_failed", pick: (counts) => counts.gateFailed },
17123
17558
  { key: "gain.row.gate_unverified", pick: (counts) => counts.gateUnverified }
17124
17559
  ];
17125
- var linesOf = (ctx, report2) => [
17560
+ var linesOf2 = (ctx, report2) => [
17126
17561
  ctx.t("gain.title", { days: report2.days }),
17127
17562
  ...ROWS.map(
17128
17563
  ({ key, pick: pick4 }) => ` ${ctx.t(key, { count: pick4(report2.current) })}${trend(ctx, pick4(report2.current), pick4(report2.previous))}`
@@ -17141,7 +17576,7 @@ var runGain = (ctx, rawDays) => {
17141
17576
  );
17142
17577
  }
17143
17578
  const report2 = gainReport(ctx.runtime, fmPaths(findRepoRoot(ctx.runtime, ctx.cwd) ?? ctx.cwd), days);
17144
- return success(report2, linesOf(ctx, report2));
17579
+ return success(report2, linesOf2(ctx, report2));
17145
17580
  };
17146
17581
  var gainCommand = {
17147
17582
  path: ["gain"],
@@ -17188,13 +17623,13 @@ var gateResult = (ctx, report2, strict) => {
17188
17623
  const human2 = reportLines(ctx.t, report2);
17189
17624
  const failedVerdict = report2.verdict === "failed" || strict && report2.verdict === "incomplete";
17190
17625
  if (!failedVerdict) return success(report2, human2);
17191
- const code = report2.verdict === "failed" ? "gate_failed" : "gate_incomplete";
17626
+ const code2 = report2.verdict === "failed" ? "gate_failed" : "gate_incomplete";
17192
17627
  return {
17193
17628
  ok: false,
17194
17629
  exitCode: ExitCode.CheckFailed,
17195
17630
  data: report2,
17196
17631
  human: human2,
17197
- error: { code, message: ctx.t(`error.${code}`, report2.counts), fix: `finitemonkey gate --task ${report2.task}` }
17632
+ error: { code: code2, message: ctx.t(`error.${code2}`, report2.counts), fix: `finitemonkey gate --task ${report2.task}` }
17198
17633
  };
17199
17634
  };
17200
17635
  var runResult = (ctx, run4, strict) => {
@@ -17282,10 +17717,10 @@ var hasAttribution = (body2) => {
17282
17717
  };
17283
17718
  var commitMessageProblems = (message) => {
17284
17719
  const lines3 = meaningful2(message);
17285
- const header = lines3.find((line2) => line2.trim() !== "") ?? "";
17720
+ const header2 = lines3.find((line2) => line2.trim() !== "") ?? "";
17286
17721
  const problems = [];
17287
- if (!EXEMPT.test(header) && !HEADER.test(header)) problems.push("header");
17288
- if (header.length > MAX_HEADER) problems.push("header_length");
17722
+ if (!EXEMPT.test(header2) && !HEADER.test(header2)) problems.push("header");
17723
+ if (header2.length > MAX_HEADER) problems.push("header_length");
17289
17724
  if (hasAttribution(lines3.join("\n"))) problems.push("attribution");
17290
17725
  return problems;
17291
17726
  };
@@ -17313,8 +17748,8 @@ var commitMessagesInRange = (runtime, root, range) => {
17313
17748
  return { id: "commit-messages", failures, fix: "git rebase -i <base> and reword the listed commits" };
17314
17749
  };
17315
17750
  var mergesInRange = (runtime, repo, range) => {
17316
- const listed4 = runtime.git(["rev-list", "--merges", ...range, "--"], repo.root);
17317
- const merges = listed4.ok ? listed4.stdout.split("\n").filter((line2) => line2 !== "") : [];
17751
+ const listed5 = runtime.git(["rev-list", "--merges", ...range, "--"], repo.root);
17752
+ const merges = listed5.ok ? listed5.stdout.split("\n").filter((line2) => line2 !== "") : [];
17318
17753
  const failures = merges.flatMap((sha) => {
17319
17754
  const report2 = verifyMerge(runtime, repo, { rev: sha });
17320
17755
  if (report2.exit === 0) return [];
@@ -17392,12 +17827,12 @@ var prePushChecks = (runtime, repo, remote, updates) => {
17392
17827
  // src/githooks/render.ts
17393
17828
  var MAX_LISTED9 = 5;
17394
17829
  var detailOf = (check2) => {
17395
- const listed4 = check2.failures.slice(0, MAX_LISTED9).join("; ");
17830
+ const listed5 = check2.failures.slice(0, MAX_LISTED9).join("; ");
17396
17831
  const rest = check2.failures.length - MAX_LISTED9;
17397
- return rest > 0 ? `${listed4}; +${String(rest)}` : listed4;
17832
+ return rest > 0 ? `${listed5}; +${String(rest)}` : listed5;
17398
17833
  };
17399
17834
  var checkLine = (t, check2) => check2.failures.length === 0 ? t("githooks.pass", { check: check2.id }) : t("githooks.fail", { check: check2.id, detail: detailOf(check2), fix: check2.fix });
17400
- var checksResult = (t, schema, code, checks) => {
17835
+ var checksResult = (t, schema, code2, checks) => {
17401
17836
  const data = { schema, checks };
17402
17837
  const bad = failing(checks);
17403
17838
  const human2 = bad.length === 0 ? checks.map((check2) => checkLine(t, check2)) : bad.map((check2) => checkLine(t, check2));
@@ -17409,7 +17844,7 @@ var checksResult = (t, schema, code, checks) => {
17409
17844
  data,
17410
17845
  human: human2,
17411
17846
  error: {
17412
- code,
17847
+ code: code2,
17413
17848
  message: t("githooks.error.failed", { count: bad.length }),
17414
17849
  fix: first?.fix ?? ""
17415
17850
  }
@@ -17582,11 +18017,11 @@ var mergeVerifyCommand = {
17582
18017
  var REFLOG_LIMIT = 300;
17583
18018
  var LIST_LIMIT = 50;
17584
18019
  var SHA = /^[0-9a-f]{40,64}$/;
17585
- var linesOf2 = (text9) => text9.split("\n").filter((line2) => line2.trim() !== "");
18020
+ var linesOf3 = (text9) => text9.split("\n").filter((line2) => line2.trim() !== "");
17586
18021
  var reflogLines = (runtime, repo) => {
17587
18022
  const result = runtime.git(["reflog", "show", "--all", `-n${String(REFLOG_LIMIT)}`, "--format=%H %gD"], repo.root);
17588
18023
  if (!result.ok) return [];
17589
- return linesOf2(result.stdout).flatMap((line2) => {
18024
+ return linesOf3(result.stdout).flatMap((line2) => {
17590
18025
  const [sha, ref] = line2.split(" ");
17591
18026
  return sha === void 0 || ref === void 0 || !SHA.test(sha) || ref.startsWith("refs/finitemonkey/") ? [] : [{ sha, ref }];
17592
18027
  });
@@ -17594,11 +18029,11 @@ var reflogLines = (runtime, repo) => {
17594
18029
  var unreachable = (runtime, repo, shas) => {
17595
18030
  if (shas.length === 0) return /* @__PURE__ */ new Set();
17596
18031
  const result = runtime.git(["rev-list", ...shas, "--not", "--branches", "--tags", "--remotes"], repo.root);
17597
- return new Set(result.ok ? linesOf2(result.stdout) : []);
18032
+ return new Set(result.ok ? linesOf3(result.stdout) : []);
17598
18033
  };
17599
18034
  var fsckDangling = (runtime, repo) => {
17600
18035
  const result = runtime.git(["fsck", "--no-reflogs", "--no-progress", "--dangling"], repo.root);
17601
- const lines3 = linesOf2(result.stdout);
18036
+ const lines3 = linesOf3(result.stdout);
17602
18037
  const pick4 = (kind) => lines3.flatMap((line2) => {
17603
18038
  const [label2, type, sha] = line2.split(" ");
17604
18039
  return label2 === "dangling" && type === kind && sha !== void 0 ? [sha] : [];
@@ -17609,7 +18044,7 @@ var describe2 = (runtime, repo, shas) => {
17609
18044
  if (shas.length === 0) return /* @__PURE__ */ new Map();
17610
18045
  const result = runtime.git(["log", "--no-walk=unsorted", "--format=%H%x09%cr%x09%s", ...shas], repo.root);
17611
18046
  return new Map(
17612
- linesOf2(result.stdout).flatMap((line2) => {
18047
+ linesOf3(result.stdout).flatMap((line2) => {
17613
18048
  const [sha, when, ...subject2] = line2.split(" ");
17614
18049
  return sha === void 0 ? [] : [[sha, [when ?? "", subject2.join(" ")]]];
17615
18050
  })
@@ -17703,7 +18138,7 @@ var rescueCommand = {
17703
18138
  };
17704
18139
 
17705
18140
  // src/git/undo.ts
17706
- import { basename as basename3, dirname as dirname8, join as join42 } from "node:path";
18141
+ import { basename as basename3, dirname as dirname8, join as join43 } from "node:path";
17707
18142
  var INDEX_SUBJECT = "finitemonkey index:";
17708
18143
  var gitLine = (runtime, repo, args2) => {
17709
18144
  const result = runtime.git(args2, repo.root);
@@ -17712,8 +18147,8 @@ var gitLine = (runtime, repo, args2) => {
17712
18147
  };
17713
18148
  var failure2 = (step3, result) => `${step3}: ${result.stderr.trim() || "git failed"}`;
17714
18149
  var parentsOf2 = (runtime, repo, commit2) => {
17715
- const listed4 = gitLine(runtime, repo, ["rev-list", "--parents", "-n", "1", commit2])?.split(" ").slice(1) ?? [];
17716
- return listed4.reduce((found, parent) => {
18150
+ const listed5 = gitLine(runtime, repo, ["rev-list", "--parents", "-n", "1", commit2])?.split(" ").slice(1) ?? [];
18151
+ return listed5.reduce((found, parent) => {
17717
18152
  const subject2 = gitLine(runtime, repo, ["log", "-1", "--format=%s", parent]) ?? "";
17718
18153
  return subject2.startsWith(INDEX_SUBJECT) ? { ...found, index: parent } : { ...found, head: parent };
17719
18154
  }, {});
@@ -17746,7 +18181,7 @@ var findSnapshot = (views, wanted) => {
17746
18181
  };
17747
18182
  var stampOf2 = (iso) => iso.replace(/[:.]/g, "-");
17748
18183
  var worktreePath = (runtime, repo) => {
17749
- const base = join42(dirname8(repo.root), `${basename3(repo.root)}-undo-${stampOf2(runtime.now())}`);
18184
+ const base = join43(dirname8(repo.root), `${basename3(repo.root)}-undo-${stampOf2(runtime.now())}`);
17750
18185
  return toAbsolutePath(runtime.stat(toAbsolutePath(base)) === void 0 ? base : `${base}-2`);
17751
18186
  };
17752
18187
  var intoWorktree = (runtime, repo, view2) => {
@@ -17981,7 +18416,7 @@ var hookCommand = {
17981
18416
  };
17982
18417
 
17983
18418
  // src/commands/i18n-check.ts
17984
- import { join as join43 } from "node:path";
18419
+ import { join as join44 } from "node:path";
17985
18420
 
17986
18421
  // src/i18n/message.ts
17987
18422
  var ARG = /^\s*([a-zA-Z][a-zA-Z0-9_]*)\s*(?:,\s*(plural|select)\s*,)?/;
@@ -18207,7 +18642,7 @@ var unreadable = (dir, file, detail) => ({
18207
18642
  exitCode: ExitCode.NotConfigured
18208
18643
  });
18209
18644
  var readCatalog = (ctx, dir, lang) => {
18210
- const file = toAbsolutePath(join43(dir, `${lang}.json`));
18645
+ const file = toAbsolutePath(join44(dir, `${lang}.json`));
18211
18646
  const text9 = ctx.runtime.readText(file);
18212
18647
  if (!text9.ok) return reject(unreadable(dir, file, text9.error));
18213
18648
  const parsed = parseJsonObject(text9.value);
@@ -18230,7 +18665,7 @@ var describeProblem = (ctx, problem4) => {
18230
18665
  return ` ${problem4.lang} ${problem4.key} ${kind}`;
18231
18666
  };
18232
18667
  var loadReview = (ctx, dir) => {
18233
- const text9 = ctx.runtime.readText(toAbsolutePath(join43(dir, "review.json")));
18668
+ const text9 = ctx.runtime.readText(toAbsolutePath(join44(dir, "review.json")));
18234
18669
  const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
18235
18670
  if (parsed?.ok !== true) return void 0;
18236
18671
  const statuses = (lang) => {
@@ -18271,7 +18706,7 @@ var i18nCheckCommand = {
18271
18706
  };
18272
18707
 
18273
18708
  // src/history/look-back.ts
18274
- import { join as join44 } from "node:path";
18709
+ import { join as join45 } from "node:path";
18275
18710
 
18276
18711
  // src/history/patterns.ts
18277
18712
  import { posix as posix5 } from "node:path";
@@ -18284,7 +18719,7 @@ var READ_COMMAND = /(^|[\s;&|(])(cat|less|more|head|tail|bat|type|Get-Content|so
18284
18719
  var PUSH = /(^|[\s;&|(])git\s+push\b([^;&|\n]*)/;
18285
18720
  var FORCE = /\s(--force(-with-lease)?(=\S*)?|-f|-[A-Za-z]*f[A-Za-z]*)(?=\s|$)|\s\+\S/;
18286
18721
  var NO_VERIFY = /(^|[\s;&|(])git\s+(commit|push|merge|rebase|am|cherry-pick)\b[^;&|\n]*\s--no-verify\b/;
18287
- var TEST_FILE = /(\.(test|spec)\.[cm]?[jt]sx?$|(^|\/)__tests__\/|_test\.(go|py)$|(^|\/)test_[^/]+\.py$)/;
18722
+ var TEST_FILE2 = /(\.(test|spec)\.[cm]?[jt]sx?$|(^|\/)__tests__\/|_test\.(go|py)$|(^|\/)test_[^/]+\.py$)/;
18288
18723
  var SKIP = /\b(it|test|describe)\.skip\s*\(|\bx(it|describe|test)\s*\(|@pytest\.mark\.skip|\bt\.Skip\(|\.only\s*\(/;
18289
18724
  var commandOf2 = (tool) => {
18290
18725
  if (tool.name !== "Bash" && tool.name !== "PowerShell") return void 0;
@@ -18312,7 +18747,7 @@ var forcePush = (command) => {
18312
18747
  };
18313
18748
  var addedSkip = (tool) => {
18314
18749
  const path = stringField(tool, "file_path");
18315
- if (!TEST_FILE.test(path.replace(/\\/g, "/"))) return false;
18750
+ if (!TEST_FILE2.test(path.replace(/\\/g, "/"))) return false;
18316
18751
  if (tool.name === "Write") return SKIP.test(stringField(tool, "content"));
18317
18752
  if (tool.name === "Edit")
18318
18753
  return SKIP.test(stringField(tool, "new_string")) && !SKIP.test(stringField(tool, "old_string"));
@@ -18407,11 +18842,11 @@ var parseTranscript = (jsonl) => {
18407
18842
  // src/history/look-back.ts
18408
18843
  var SAMPLES = 3;
18409
18844
  var projectLogKey = (root) => root.replace(/[^A-Za-z0-9-]/g, "-");
18410
- var projectLogDir = (runtime, root, env) => join44(env["CLAUDE_CONFIG_DIR"] ?? join44(runtime.homeDir(), ".claude"), "projects", projectLogKey(root));
18845
+ var projectLogDir = (runtime, root, env) => join45(env["CLAUDE_CONFIG_DIR"] ?? join45(runtime.homeDir(), ".claude"), "projects", projectLogKey(root));
18411
18846
  var lookBack = (runtime, root, env) => {
18412
18847
  const dir = projectLogDir(runtime, root, env);
18413
- const listed4 = runtime.listDir(toAbsolutePath(dir));
18414
- const files = listed4.ok ? listed4.value.filter((name) => name.endsWith(".jsonl")).sort() : [];
18848
+ const listed5 = runtime.listDir(toAbsolutePath(dir));
18849
+ const files = listed5.ok ? listed5.value.filter((name) => name.endsWith(".jsonl")).sort() : [];
18415
18850
  const counts = {
18416
18851
  done_after_failing_tests: 0,
18417
18852
  env_read: 0,
@@ -18428,7 +18863,7 @@ var lookBack = (runtime, root, env) => {
18428
18863
  };
18429
18864
  let turns = 0;
18430
18865
  for (const name of files) {
18431
- const text9 = runtime.readText(toAbsolutePath(join44(dir, name)));
18866
+ const text9 = runtime.readText(toAbsolutePath(join45(dir, name)));
18432
18867
  if (!text9.ok) continue;
18433
18868
  for (const turn of parseTranscript(text9.value)) {
18434
18869
  turns += 1;
@@ -18448,10 +18883,10 @@ var MAX_LCS_CELLS = 4e6;
18448
18883
  var table = (a, b) => {
18449
18884
  const rows = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
18450
18885
  for (let i = a.length - 1; i >= 0; i -= 1) {
18451
- const row = rows[i] ?? [];
18886
+ const row2 = rows[i] ?? [];
18452
18887
  const below = rows[i + 1] ?? [];
18453
18888
  for (let j = b.length - 1; j >= 0; j -= 1) {
18454
- row[j] = a[i] === b[j] ? (below[j + 1] ?? 0) + 1 : Math.max(below[j] ?? 0, row[j + 1] ?? 0);
18889
+ row2[j] = a[i] === b[j] ? (below[j + 1] ?? 0) + 1 : Math.max(below[j] ?? 0, row2[j + 1] ?? 0);
18455
18890
  }
18456
18891
  }
18457
18892
  return rows;
@@ -18484,17 +18919,17 @@ var sharedPrefix = (a, b) => {
18484
18919
  while (count4 < a.length && count4 < b.length && a[count4] === b[count4]) count4 += 1;
18485
18920
  return count4;
18486
18921
  };
18487
- var sharedSuffix = (a, b, prefix) => {
18922
+ var sharedSuffix = (a, b, prefix2) => {
18488
18923
  let count4 = 0;
18489
- while (count4 < a.length - prefix && count4 < b.length - prefix && a[a.length - 1 - count4] === b[b.length - 1 - count4])
18924
+ while (count4 < a.length - prefix2 && count4 < b.length - prefix2 && a[a.length - 1 - count4] === b[b.length - 1 - count4])
18490
18925
  count4 += 1;
18491
18926
  return count4;
18492
18927
  };
18493
18928
  var changedOps = (a, b) => {
18494
- const prefix = sharedPrefix(a, b);
18495
- const suffix = sharedSuffix(a, b, prefix);
18496
- const left = a.slice(prefix, a.length - suffix);
18497
- const right = b.slice(prefix, b.length - suffix);
18929
+ const prefix2 = sharedPrefix(a, b);
18930
+ const suffix = sharedSuffix(a, b, prefix2);
18931
+ const left = a.slice(prefix2, a.length - suffix);
18932
+ const right = b.slice(prefix2, b.length - suffix);
18498
18933
  if (left.length * right.length <= MAX_LCS_CELLS) return walk2(left, right).filter(([op]) => op !== "=");
18499
18934
  return [...left.map((line2) => ["-", line2]), ...right.map((line2) => ["+", line2])];
18500
18935
  };
@@ -18502,9 +18937,9 @@ var lineDiff = (path, before, after) => {
18502
18937
  const changes = changedOps(splitLines(before), splitLines(after));
18503
18938
  if (changes.length === 0) return [];
18504
18939
  const lines3 = changes.map(([op, line2]) => `${op} ${line2}`);
18505
- const shown3 = lines3.slice(0, MAX_DIFF_LINES);
18506
- const hidden = lines3.length - shown3.length;
18507
- return [`--- ${path}`, `+++ ${path}`, ...shown3, ...hidden > 0 ? [`… ${String(hidden)} more`] : []];
18940
+ const shown4 = lines3.slice(0, MAX_DIFF_LINES);
18941
+ const hidden = lines3.length - shown4.length;
18942
+ return [`--- ${path}`, `+++ ${path}`, ...shown4, ...hidden > 0 ? [`… ${String(hidden)} more`] : []];
18508
18943
  };
18509
18944
 
18510
18945
  // src/install/report.ts
@@ -18654,6 +19089,346 @@ var initCommand = {
18654
19089
  }
18655
19090
  };
18656
19091
 
19092
+ // src/inventory/run.ts
19093
+ import { relative as relative10 } from "node:path";
19094
+
19095
+ // src/inventory/dependencies.ts
19096
+ var MANIFESTS = /(^|\/)(package\.json|requirements\.txt|go\.mod)$/;
19097
+ var MAX_MANIFESTS = 8;
19098
+ var PACKAGE_SECTIONS = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
19099
+ var REQUIREMENT = /^([A-Za-z0-9_.-]+)\s*(?:[=<>~!]=?\s*([^\s;#]+))?/;
19100
+ var GO_REQUIRE = /^\s*(?:require\s+)?([\w.-]+\.[\w./-]+)\s+(v[\w.+-]+)/;
19101
+ var lineOf = (lines3, needle) => lines3.findIndex((line2) => line2.includes(needle)) + 1;
19102
+ var fromPackage = (file, text9) => {
19103
+ const parsed = parseJsonObject(text9);
19104
+ if (!parsed.ok) return [];
19105
+ const lines3 = text9.split("\n");
19106
+ return PACKAGE_SECTIONS.flatMap((section3) => {
19107
+ const block4 = parsed.value[section3];
19108
+ if (!isRecord(block4)) return [];
19109
+ return Object.entries(block4).map(([name, version]) => ({
19110
+ name,
19111
+ version: typeof version === "string" ? version : "",
19112
+ at: `${file}:${String(lineOf(lines3, `"${name}"`))}`
19113
+ }));
19114
+ });
19115
+ };
19116
+ var fromLines = (file, text9, pattern) => text9.split("\n").flatMap((line2, index) => {
19117
+ const match = pattern.exec(line2.trim() === "" || line2.trimStart().startsWith("#") ? "" : line2);
19118
+ if (match?.[1] === void 0) return [];
19119
+ return [{ name: match[1], version: match[2] ?? "", at: `${file}:${String(index + 1)}` }];
19120
+ });
19121
+ var parseManifest = (file, text9) => {
19122
+ if (file.endsWith("package.json")) return fromPackage(file, text9);
19123
+ return fromLines(file, text9, file.endsWith("go.mod") ? GO_REQUIRE : REQUIREMENT);
19124
+ };
19125
+ var dependenciesAt = (git2, hash, files) => files.filter((file) => MANIFESTS.test(file)).slice(0, MAX_MANIFESTS).flatMap((file) => {
19126
+ const text9 = showAt(git2, hash, file);
19127
+ return text9 === void 0 ? [] : parseManifest(file, text9);
19128
+ });
19129
+
19130
+ // src/inventory/read.ts
19131
+ var MAX_TEXT = 120;
19132
+ var HOTSPOT_COMMITS = 300;
19133
+ var MAX_HOTSPOTS = 10;
19134
+ var where = (hit3) => `${hit3.file}:${String(hit3.line)}`;
19135
+ var shown3 = (hits) => hits.map((hit3) => ({ ...hit3, text: maskText(hit3.text).slice(0, MAX_TEXT) }));
19136
+ var moduleOf = (file) => {
19137
+ const parts = file.split("/");
19138
+ return parts.length <= 1 ? "." : parts.slice(0, Math.min(2, parts.length - 1)).join("/");
19139
+ };
19140
+ var modulesOf = (files) => {
19141
+ const counts = /* @__PURE__ */ new Map();
19142
+ for (const file of files.filter((item2) => !TEST_FILE.test(item2))) {
19143
+ counts.set(moduleOf(file), (counts.get(moduleOf(file)) ?? 0) + 1);
19144
+ }
19145
+ return [...counts].map(([path, count4]) => ({ path, files: count4 })).sort((a, b) => a.path.localeCompare(b.path));
19146
+ };
19147
+ var envOf = (hits) => {
19148
+ const names = /* @__PURE__ */ new Map();
19149
+ for (const hit3 of hits) {
19150
+ for (const name of envNamesIn(hit3.text)) names.set(name, [...names.get(name) ?? [], where(hit3)]);
19151
+ }
19152
+ return [...names].map(([name, at]) => ({ name, at: [...new Set(at)] })).sort((a, b) => a.name.localeCompare(b.name));
19153
+ };
19154
+ var credentialsOf = (hits) => hits.flatMap(
19155
+ (hit3) => scanText(hit3.text, { path: hit3.file }).map((finding4) => ({ at: where(hit3), rule: finding4.ruleId }))
19156
+ );
19157
+ var hotspotsOf = (git2, hash, covered) => {
19158
+ const out = git2.out([
19159
+ "log",
19160
+ "--format=",
19161
+ "--name-only",
19162
+ "-n",
19163
+ String(HOTSPOT_COMMITS),
19164
+ hash,
19165
+ "--",
19166
+ ...pathspecOf(covered)
19167
+ ]);
19168
+ const counts = /* @__PURE__ */ new Map();
19169
+ for (const file of linesOf(out)) counts.set(file, (counts.get(file) ?? 0) + 1);
19170
+ return [...counts].map(([path, changes]) => ({ path, changes })).filter((item2) => item2.changes > 1).sort((a, b) => b.changes - a.changes || a.path.localeCompare(b.path)).slice(0, MAX_HOTSPOTS);
19171
+ };
19172
+ var worktreesOf = (git2) => {
19173
+ const paths = linesOf(git2.out(["worktree", "list", "--porcelain"])).filter((line2) => line2.startsWith("worktree ")).map((line2) => line2.slice("worktree ".length));
19174
+ return paths.length > 1 ? paths : [];
19175
+ };
19176
+ var readRepo = (git2, path, ref, hash, covered) => {
19177
+ const files = filesAt(git2, hash, covered);
19178
+ return {
19179
+ path,
19180
+ hash,
19181
+ ref,
19182
+ modules: modulesOf(files),
19183
+ routes: shown3(grepAt(git2, hash, ROUTES, covered)),
19184
+ tables: shown3(grepAt(git2, hash, TABLES, covered)),
19185
+ queues: shown3(grepAt(git2, hash, QUEUES, covered)),
19186
+ jobs: shown3(grepAt(git2, hash, JOBS, covered)),
19187
+ env: envOf(grepAt(git2, hash, ENV, covered)),
19188
+ dependencies: dependenciesAt(git2, hash, files),
19189
+ tests: files.filter((file) => TEST_FILE.test(file)),
19190
+ credentials: credentialsOf(grepAt(git2, hash, CREDENTIAL_HINTS, covered)),
19191
+ deploy: files.filter((file) => DEPLOY_FILE.test(file)),
19192
+ hotspots: hotspotsOf(git2, hash, covered),
19193
+ worktrees: worktreesOf(git2)
19194
+ };
19195
+ };
19196
+
19197
+ // src/plan/markdown.ts
19198
+ var cell = (text9) => text9.replace(/\r?\n/g, " ").replace(/\|/g, "\\|");
19199
+ var code = (text9) => `\`${text9.replace(/`/g, "'")}\``;
19200
+ var row = (cells) => `| ${cells.map(cell).join(" | ")} |`;
19201
+ var table2 = (header2, rows) => [
19202
+ row(header2),
19203
+ `|${header2.map(() => "---").join("|")}|`,
19204
+ ...rows.length === 0 ? [row(header2.map(() => ""))] : rows.map(row)
19205
+ ];
19206
+ var section = (heading, lines3) => [
19207
+ `## ${heading}`,
19208
+ "",
19209
+ ...lines3,
19210
+ ""
19211
+ ];
19212
+
19213
+ // src/inventory/render.ts
19214
+ var MAX_LISTED10 = 40;
19215
+ var capped = (t, lines3) => lines3.length > MAX_LISTED10 ? [...lines3.slice(0, MAX_LISTED10), `- ${t("inventory.more", { count: lines3.length - MAX_LISTED10 })}`] : lines3;
19216
+ var listed4 = (t, lines3) => lines3.length === 0 ? [t("inventory.none")] : capped(t, lines3);
19217
+ var hitLines = (t, hits) => listed4(
19218
+ t,
19219
+ hits.map((hit3) => `- ${code(`${hit3.file}:${String(hit3.line)}`)} ${code(hit3.text)}`)
19220
+ );
19221
+ var prefix = (repo, many2) => many2 ? `${repo.path}: ` : "";
19222
+ var riskRows = (t, repos) => repos.flatMap(
19223
+ (repo) => repo.credentials.map((hit3) => [
19224
+ t("inventory.risk.credential", { rule: hit3.rule }),
19225
+ t("inventory.risk.yes"),
19226
+ code(`${prefix(repo, repos.length > 1)}${hit3.at}`),
19227
+ t("inventory.risk.fix_credential")
19228
+ ])
19229
+ );
19230
+ var codeSections = (t, repo) => [
19231
+ ...section(
19232
+ t("inventory.modules"),
19233
+ listed4(
19234
+ t,
19235
+ repo.modules.map((item2) => `- ${code(item2.path)}: ${t("inventory.files", { count: item2.files })}`)
19236
+ )
19237
+ ),
19238
+ ...section(t("inventory.routes"), hitLines(t, repo.routes)),
19239
+ ...section(t("inventory.tables"), hitLines(t, repo.tables)),
19240
+ ...section(t("inventory.queues"), hitLines(t, repo.queues)),
19241
+ ...section(t("inventory.jobs"), hitLines(t, repo.jobs)),
19242
+ ...section(
19243
+ t("inventory.env"),
19244
+ listed4(
19245
+ t,
19246
+ repo.env.map((item2) => `- ${code(item2.name)}: ${item2.at.map(code).join(", ")}`)
19247
+ )
19248
+ ),
19249
+ ...section(
19250
+ t("inventory.dependencies"),
19251
+ listed4(
19252
+ t,
19253
+ repo.dependencies.map((item2) => `- ${code(item2.name)} ${item2.version}: ${code(item2.at)}`)
19254
+ )
19255
+ ),
19256
+ ...section(t("inventory.tests"), [
19257
+ t("inventory.test_count", { count: repo.tests.length }),
19258
+ ...capped(
19259
+ t,
19260
+ repo.tests.map((file) => `- ${code(file)}`)
19261
+ )
19262
+ ])
19263
+ ];
19264
+ var optional2 = (heading, lines3) => lines3.length === 0 ? [] : section(heading, lines3);
19265
+ var whenPresent = (t, repo, incidents) => [
19266
+ ...optional2(
19267
+ t("inventory.credentials"),
19268
+ repo.credentials.length === 0 ? [] : [t("inventory.credentials_note"), "", ...repo.credentials.map((hit3) => `- ${code(hit3.at)} (${hit3.rule})`)]
19269
+ ),
19270
+ ...optional2(
19271
+ t("inventory.deploy"),
19272
+ repo.deploy.map((file) => `- ${code(file)}`)
19273
+ ),
19274
+ ...optional2(
19275
+ t("inventory.history"),
19276
+ incidents.map((name) => `- ${code(`.finitemonkey/incidents/${name}`)}`)
19277
+ ),
19278
+ ...optional2(
19279
+ t("inventory.worktrees"),
19280
+ repo.worktrees.map((path) => `- ${code(path)}`)
19281
+ ),
19282
+ ...optional2(
19283
+ t("inventory.hotspots"),
19284
+ repo.hotspots.map((item2) => `- ${code(item2.path)}: ${t("inventory.changes", { count: item2.changes })}`)
19285
+ )
19286
+ ];
19287
+ var repoBlock = (t, inventory, repo) => [
19288
+ ...inventory.repos.length > 1 ? [`# ${code(repo.path)}`, ""] : [],
19289
+ ...codeSections(t, repo),
19290
+ ...whenPresent(t, repo, repo.path === "." ? inventory.incidents : [])
19291
+ ];
19292
+ var header = (t, inventory) => [
19293
+ `# ${t("inventory.title", { topic: inventory.topic })}`,
19294
+ "",
19295
+ ...inventory.repos.map(
19296
+ (repo) => t("inventory.read_at", { path: repo.path, hash: repo.hash, date: inventory.date, ref: repo.ref })
19297
+ ),
19298
+ ""
19299
+ ];
19300
+ var deltaBlock = (t, inventory) => inventory.previousHash === void 0 ? [] : section(
19301
+ t("inventory.delta", { hash: inventory.previousHash }),
19302
+ listed4(
19303
+ t,
19304
+ inventory.delta.map((line2) => `- ${code(line2)}`)
19305
+ )
19306
+ );
19307
+ var renderInventory = (t, inventory) => [
19308
+ ...header(t, inventory),
19309
+ ...section(
19310
+ t("inventory.risks"),
19311
+ table2(
19312
+ [t("inventory.col.risk"), t("inventory.col.still"), t("inventory.col.evidence"), t("inventory.col.fix")],
19313
+ riskRows(t, inventory.repos)
19314
+ )
19315
+ ),
19316
+ ...section(
19317
+ t("inventory.gap"),
19318
+ table2([t("inventory.col.plan"), t("inventory.col.today"), t("inventory.col.effect")], [])
19319
+ ),
19320
+ ...deltaBlock(t, inventory),
19321
+ ...inventory.repos.flatMap((repo) => repoBlock(t, inventory, repo))
19322
+ ].join("\n");
19323
+
19324
+ // src/inventory/run.ts
19325
+ var readOne = (runtime, root, dir, request) => {
19326
+ const top = gitRoot(gitAt(runtime, toAbsolutePath(dir, root)));
19327
+ if (top === void 0) return reject({ kind: "repo", repo: dir });
19328
+ const git2 = gitAt(runtime, top);
19329
+ const hash = resolveCommit(git2, request.ref);
19330
+ if (hash === void 0) return reject({ kind: "ref", repo: dir, ref: request.ref });
19331
+ const path = toPosixPath(relative10(runtime.realpath(root), runtime.realpath(top))) || ".";
19332
+ return accept(readRepo(git2, path, request.ref, hash, request.covered));
19333
+ };
19334
+ var deltaOf = (runtime, root, previous, repo, covered) => {
19335
+ if (previous === void 0 || previous === repo.hash) return [];
19336
+ const git2 = gitAt(runtime, toAbsolutePath(repo.path, root));
19337
+ return linesOf(git2.out(["diff", "--name-status", previous, repo.hash, "--", ...pathspecOf(covered)]));
19338
+ };
19339
+ var frontOf = (inventory) => ({
19340
+ schema: INVENTORY_SCHEMA,
19341
+ topic: inventory.topic,
19342
+ date: inventory.date,
19343
+ repos: inventory.repos.map((repo) => ({ path: repo.path, ref: repo.ref, hash: repo.hash })),
19344
+ covered: [...inventory.covered]
19345
+ });
19346
+ var assemble2 = (runtime, root, request, repos, previousText) => {
19347
+ const previous = previousText === void 0 ? [] : reposOf(readFront(previousText).data);
19348
+ const main2 = repos[0];
19349
+ const previousHash = previous.find((item2) => item2.path === main2?.path)?.hash;
19350
+ const incidents = runtime.listDir(incidentsDir(root));
19351
+ return {
19352
+ schema: INVENTORY_SCHEMA,
19353
+ topic: request.topic,
19354
+ date: runtime.now().slice(0, 10),
19355
+ covered: request.covered.length === 0 ? ["."] : request.covered,
19356
+ repos,
19357
+ delta: main2 === void 0 ? [] : deltaOf(runtime, root, previousHash, main2, request.covered),
19358
+ ...previousHash === void 0 || previousHash === main2?.hash ? {} : { previousHash },
19359
+ incidents: incidents.ok ? incidents.value.filter((name) => name.endsWith(".json")) : []
19360
+ };
19361
+ };
19362
+ var runInventory = (runtime, t, root, request) => {
19363
+ const reads = (request.repos.length === 0 ? ["."] : request.repos).map((dir) => readOne(runtime, root, dir, request));
19364
+ const repos = [];
19365
+ for (const read4 of reads) {
19366
+ if (!read4.ok) return read4;
19367
+ repos.push(read4.value);
19368
+ }
19369
+ const names = planFiles(runtime, root);
19370
+ const name = findByKind(names, "today", request.topic) ?? `${nextNumber(names)}a-today-${slugOf(request.topic)}.md`;
19371
+ const target2 = planPath(root, name);
19372
+ const before = runtime.readText(target2);
19373
+ const inventory = assemble2(runtime, root, request, repos, before.ok ? before.value : void 0);
19374
+ const made = runtime.mkdir(planDir(root));
19375
+ const written = made.ok ? runtime.writeText(target2, writeFront(frontOf(inventory), renderInventory(t, inventory))) : made;
19376
+ if (!written.ok) return reject({ kind: "write", detail: written.error });
19377
+ return accept({ file: `${PLAN_DIR}/${name}`, inventory });
19378
+ };
19379
+
19380
+ // src/commands/inventory.ts
19381
+ var USAGE2 = "finitemonkey inventory <topic> [--ref <ref>] [--path <dir>]... [--repo <dir>]...";
19382
+ var problemResult = (ctx, problem4) => {
19383
+ if (problem4.kind === "repo") return failureFrom(ctx.t, notRepository(problem4.repo));
19384
+ if (problem4.kind === "ref")
19385
+ return failureFrom(
19386
+ ctx.t,
19387
+ usageProblem("invalid_arguments", "inventory.error.ref", USAGE2, { ref: problem4.ref, repo: problem4.repo })
19388
+ );
19389
+ return failureFrom(ctx.t, {
19390
+ code: "plan_write_failed",
19391
+ messageKey: "plan.error.write",
19392
+ params: { detail: problem4.detail },
19393
+ fix: "check that docs/plan can be written",
19394
+ exitCode: ExitCode.CheckFailed
19395
+ });
19396
+ };
19397
+ var inventoryCommand = {
19398
+ path: ["inventory"],
19399
+ summaryKey: "command.inventory.summary",
19400
+ usage: USAGE2,
19401
+ status: "ready",
19402
+ maxPositionals: 1,
19403
+ options: {
19404
+ ref: { type: "string" },
19405
+ path: { type: "string", multiple: true },
19406
+ repo: { type: "string", multiple: true }
19407
+ },
19408
+ run: (ctx, args2) => {
19409
+ const repo = repoOf(ctx);
19410
+ if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
19411
+ const topic = args2.positionals[0]?.trim() ?? "";
19412
+ if (topic === "")
19413
+ return Promise.resolve(failureFrom(ctx.t, usageProblem("invalid_arguments", "inventory.error.topic", USAGE2)));
19414
+ const written = runInventory(ctx.runtime, ctx.t, repo.root, {
19415
+ topic,
19416
+ ref: stringOption(args2.values, "ref") ?? "HEAD",
19417
+ covered: listOption(args2.values, "path"),
19418
+ repos: listOption(args2.values, "repo")
19419
+ });
19420
+ if (!written.ok) return Promise.resolve(problemResult(ctx, written.error));
19421
+ const { file, inventory } = written.value;
19422
+ const repos = inventory.repos.map((item2) => ({ path: item2.path, ref: item2.ref, hash: item2.hash }));
19423
+ return Promise.resolve(
19424
+ success({ schema: INVENTORY_SCHEMA, file, topic, covered: inventory.covered, repos, delta: inventory.delta }, [
19425
+ ctx.t("inventory.written", { file, count: repos.length }),
19426
+ ...repos.map((item2) => ` ${item2.path} ${item2.hash}`)
19427
+ ])
19428
+ );
19429
+ }
19430
+ };
19431
+
18657
19432
  // src/look/findings.ts
18658
19433
  var MAX_PER_RULE = 5;
18659
19434
  var finding3 = (result, rule, detail) => ({
@@ -18690,7 +19465,7 @@ var applyAllows = (findings, allows, decided) => {
18690
19465
 
18691
19466
  // src/look/node-capture.ts
18692
19467
  import { mkdirSync as mkdirSync3 } from "node:fs";
18693
- import { join as join45 } from "node:path";
19468
+ import { join as join46 } from "node:path";
18694
19469
 
18695
19470
  // src/look/measure.ts
18696
19471
  var measurePage = (doc) => {
@@ -18728,7 +19503,7 @@ var parseMeasure = (value) => {
18728
19503
  var HEIGHT = 900;
18729
19504
  var MAX_EVENTS = 20;
18730
19505
  var RESOURCE_ERROR = /^Failed to load resource\b/;
18731
- var slugOf = (route) => {
19506
+ var slugOf2 = (route) => {
18732
19507
  const slug2 = route.replace(/^\//, "").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
18733
19508
  return slug2 === "" ? "root" : slug2.slice(0, 80);
18734
19509
  };
@@ -18766,7 +19541,7 @@ var captureOne = async (browser, request, route, viewport) => {
18766
19541
  const context = await browser.newContext({ viewport: { width: viewport, height: HEIGHT }, reducedMotion: "reduce" });
18767
19542
  const page = await context.newPage();
18768
19543
  const events = watch(page);
18769
- const path = join45(request.dir, `${slugOf(route)}-${String(viewport)}.png`);
19544
+ const path = join46(request.dir, `${slugOf2(route)}-${String(viewport)}.png`);
18770
19545
  const loadError = await loadAndShoot(page, `${request.baseUrl}${route}`, path, request.timeoutMs);
18771
19546
  const measure = loadError === void 0 ? parseMeasure(await page.evaluate(MEASURE_SCRIPT)) : void 0;
18772
19547
  await context.close();
@@ -18783,7 +19558,7 @@ var captureAll = async (browser, request) => {
18783
19558
 
18784
19559
  // src/look/node-playwright.ts
18785
19560
  import { createRequire } from "node:module";
18786
- import { join as join46 } from "node:path";
19561
+ import { join as join47 } from "node:path";
18787
19562
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
18788
19563
  var NAMES = ["playwright", "playwright-core"];
18789
19564
  var isPlaywright = (value) => isRecord(value) && isRecord(value["chromium"]) && typeof value["chromium"]["launch"] === "function";
@@ -18798,7 +19573,7 @@ var tryLoad = async (base, name) => {
18798
19573
  }
18799
19574
  };
18800
19575
  var loadPlaywright = async (root) => {
18801
- for (const base of [join46(root, "package.json"), fileURLToPath3(import.meta.url)]) {
19576
+ for (const base of [join47(root, "package.json"), fileURLToPath3(import.meta.url)]) {
18802
19577
  for (const name of NAMES) {
18803
19578
  const found = await tryLoad(base, name);
18804
19579
  if (found !== void 0) return found;
@@ -18819,7 +19594,7 @@ var launchBrowser = async (playwright) => {
18819
19594
 
18820
19595
  // src/look/node-sheet.ts
18821
19596
  import { readFileSync as readFileSync2 } from "node:fs";
18822
- import { join as join47 } from "node:path";
19597
+ import { join as join48 } from "node:path";
18823
19598
  var escapeHtml = (text9) => text9.replace(/[&<>"]/g, (char) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[char] ?? char);
18824
19599
  var card = (result) => {
18825
19600
  const image = (() => {
@@ -18838,11 +19613,11 @@ var STYLE = [
18838
19613
  "img{display:block;width:100%;max-height:720px;object-fit:cover;object-position:top}"
18839
19614
  ].join("");
18840
19615
  var contactSheet = async (browser, results, dir) => {
18841
- const shown3 = results.filter((result) => result.loadError === void 0);
18842
- if (shown3.length === 0) return void 0;
19616
+ const shown4 = results.filter((result) => result.loadError === void 0);
19617
+ if (shown4.length === 0) return void 0;
18843
19618
  const page = await browser.newPage({ viewport: { width: 1600, height: 900 } });
18844
- await page.setContent(`<!doctype html><style>${STYLE}</style>${shown3.map(card).join("")}`);
18845
- const path = join47(dir, "contact-sheet.png");
19619
+ await page.setContent(`<!doctype html><style>${STYLE}</style>${shown4.map(card).join("")}`);
19620
+ const path = join48(dir, "contact-sheet.png");
18846
19621
  await page.screenshot({ path, fullPage: true });
18847
19622
  await page.close();
18848
19623
  return path;
@@ -19050,7 +19825,7 @@ var policyFalsePositiveCommand = {
19050
19825
  };
19051
19826
 
19052
19827
  // src/commands/prose-check.ts
19053
- import { relative as relative10 } from "node:path";
19828
+ import { relative as relative11 } from "node:path";
19054
19829
 
19055
19830
  // src/commands/prose-view.ts
19056
19831
  var PROSE_CHECK_SCHEMA = "finitemonkey/prose-check@1";
@@ -19103,14 +19878,14 @@ var proseCheckHuman = (t, reports, data) => [
19103
19878
  // src/commands/prose-check.ts
19104
19879
  var TEXT_FILE = /\.(?:md|markdown|mdx|txt)$/i;
19105
19880
  var STDIN_LABEL = "<stdin>";
19106
- var problem2 = (code, messageKey, params2) => ({
19107
- code,
19881
+ var problem2 = (code2, messageKey, params2) => ({
19882
+ code: code2,
19108
19883
  messageKey,
19109
19884
  params: params2,
19110
19885
  fix: "finitemonkey help prose check",
19111
19886
  exitCode: ExitCode.Usage
19112
19887
  });
19113
- var underRoot = (root, path) => toPosixPath(relative10(root, path));
19888
+ var underRoot = (root, path) => toPosixPath(relative11(root, path));
19114
19889
  var expand2 = (ctx, root, target2) => {
19115
19890
  const path = toAbsolutePath(target2, ctx.cwd);
19116
19891
  const stat = ctx.runtime.stat(path);
@@ -19182,12 +19957,12 @@ var proseCheckCommand = {
19182
19957
  };
19183
19958
 
19184
19959
  // src/review/packet.ts
19185
- import { join as join48 } from "node:path";
19960
+ import { join as join49 } from "node:path";
19186
19961
  var REVIEW_STATE_FILE = ".finitemonkey/review/state.json";
19187
19962
  var FULL_ROUNDS = 3;
19188
19963
  var REVIEW_FILE_MAX_LINES = 200;
19189
19964
  var LEDGER_MAX_LINES = 100;
19190
- var statePath = (root) => toAbsolutePath(join48(root, REVIEW_STATE_FILE));
19965
+ var statePath = (root) => toAbsolutePath(join49(root, REVIEW_STATE_FILE));
19191
19966
  var readReviewState = (runtime, root) => {
19192
19967
  const raw = runtime.readText(statePath(root));
19193
19968
  const parsed = raw.ok ? parseJsonObject(raw.value) : void 0;
@@ -19200,7 +19975,7 @@ var git = (runtime, root, args2) => {
19200
19975
  return result.ok ? result.stdout : void 0;
19201
19976
  };
19202
19977
  var lastLines = (text9, max) => text9.split("\n").slice(-max).join("\n");
19203
- var section = (title, body2, empty) => `## ${title}
19978
+ var section2 = (title, body2, empty) => `## ${title}
19204
19979
 
19205
19980
  ${body2 === void 0 || body2.trim() === "" ? empty : body2.trimEnd()}
19206
19981
  `;
@@ -19208,12 +19983,12 @@ var taskSections = (runtime, root) => {
19208
19983
  const paths = fmPaths(root);
19209
19984
  const active = readState(runtime, paths).active;
19210
19985
  if (active === void 0)
19211
- return [section("Contract", void 0, "(no active task)"), section("Evidence ledger", void 0, "(none)")];
19212
- const contract = runtime.readText(toAbsolutePath(join48(root, taskRelative(active))));
19986
+ return [section2("Contract", void 0, "(no active task)"), section2("Evidence ledger", void 0, "(none)")];
19987
+ const contract = runtime.readText(toAbsolutePath(join49(root, taskRelative(active))));
19213
19988
  const ledger = runtime.readText(paths.evidence(active));
19214
19989
  return [
19215
- section("Contract", contract.ok ? contract.value : void 0, "(contract file missing)"),
19216
- section("Evidence ledger", ledger.ok ? lastLines(ledger.value, LEDGER_MAX_LINES) : void 0, "(none)")
19990
+ section2("Contract", contract.ok ? contract.value : void 0, "(contract file missing)"),
19991
+ section2("Evidence ledger", ledger.ok ? lastLines(ledger.value, LEDGER_MAX_LINES) : void 0, "(none)")
19217
19992
  ];
19218
19993
  };
19219
19994
  var reviewFile = (runtime, root, base) => {
@@ -19235,11 +20010,11 @@ var buildPacket = (runtime, root, base, head) => {
19235
20010
  `# Review packet (round ${String(state.rounds)}, ${range})`,
19236
20011
  "",
19237
20012
  ...state.rounds > FULL_ROUNDS ? ["Only the diff since the last review round is included.", ""] : [],
19238
- section("Diff", diff, "(empty diff)"),
20013
+ section2("Diff", diff, "(empty diff)"),
19239
20014
  ...taskSections(runtime, root),
19240
- ...owners === void 0 ? [] : [section("REVIEW.md (base branch)", owners, "")]
20015
+ ...owners === void 0 ? [] : [section2("REVIEW.md (base branch)", owners, "")]
19241
20016
  ].join("\n");
19242
- runtime.mkdir(toAbsolutePath(join48(root, ".finitemonkey", "review")));
20017
+ runtime.mkdir(toAbsolutePath(join49(root, ".finitemonkey", "review")));
19243
20018
  runtime.writeAtomic(statePath(root), `${JSON.stringify(state, null, 2)}
19244
20019
  `);
19245
20020
  return { round: state.rounds, base, head, range, text: text9 };
@@ -19394,7 +20169,7 @@ var secretsAcceptCommand = {
19394
20169
  };
19395
20170
 
19396
20171
  // src/secrets/runbooks.ts
19397
- import { dirname as dirname9, join as join49 } from "node:path";
20172
+ import { dirname as dirname9, join as join50 } from "node:path";
19398
20173
  var PROVIDER_OF_RULE = {
19399
20174
  "aws-access-token": "aws",
19400
20175
  "aws-secret-access-key": "aws",
@@ -19416,13 +20191,13 @@ var RUNBOOKS_URL = "https://github.com/gabrielvieiraf/finitemonkey/blob/main/run
19416
20191
  var runbookProvider = (rule) => PROVIDER_OF_RULE[rule] ?? "generic";
19417
20192
  var runbookLink = (runtime, rule) => {
19418
20193
  const file = `${runbookProvider(rule)}.md`;
19419
- const local = toAbsolutePath(join49(dirname9(dirname9(runtime.selfPath)), "runbooks", file));
20194
+ const local = toAbsolutePath(join50(dirname9(dirname9(runtime.selfPath)), "runbooks", file));
19420
20195
  return runtime.stat(local)?.kind === "file" ? local : `${RUNBOOKS_URL}/${file}`;
19421
20196
  };
19422
20197
 
19423
20198
  // src/commands/secrets-incident.ts
19424
20199
  var INCIDENT_ACTIONS = ["list", "rotated", "close", "purge"];
19425
- var USAGE2 = "finitemonkey secrets incident [list | rotated <id> | close <id> | purge <id>]";
20200
+ var USAGE3 = "finitemonkey secrets incident [list | rotated <id> | close <id> | purge <id>]";
19426
20201
  var TARGET = { rotated: "rotacionado", close: "fechado" };
19427
20202
  var PURGE_STEPS = [
19428
20203
  "secrets.purge.step1",
@@ -19480,7 +20255,7 @@ var move = (ctx, root, incident, action) => {
19480
20255
  if (!canMove(incident.state, to))
19481
20256
  return action === "close" ? notRotated(ctx, incident) : fail2(
19482
20257
  ctx,
19483
- usageProblem("invalid_arguments", "secrets.error.bad_transition", USAGE2, {
20258
+ usageProblem("invalid_arguments", "secrets.error.bad_transition", USAGE3, {
19484
20259
  id: incident.id,
19485
20260
  from: incident.state,
19486
20261
  to
@@ -19503,7 +20278,7 @@ var actionOf = (raw) => INCIDENT_ACTIONS.find((action) => action === (raw ?? "li
19503
20278
  var secretsIncidentCommand = {
19504
20279
  path: ["secrets", "incident"],
19505
20280
  summaryKey: "secrets.command.incident",
19506
- usage: USAGE2,
20281
+ usage: USAGE3,
19507
20282
  status: "ready",
19508
20283
  maxPositionals: 2,
19509
20284
  run: (ctx, args2) => {
@@ -19513,7 +20288,7 @@ var secretsIncidentCommand = {
19513
20288
  return Promise.resolve(
19514
20289
  fail2(
19515
20290
  ctx,
19516
- usageProblem("invalid_arguments", "error.invalid_arguments", USAGE2, { detail: args2.positionals[0] ?? "" })
20291
+ usageProblem("invalid_arguments", "error.invalid_arguments", USAGE3, { detail: args2.positionals[0] ?? "" })
19517
20292
  )
19518
20293
  );
19519
20294
  if (action === "list") return Promise.resolve(list2(ctx, root));
@@ -19585,8 +20360,8 @@ var liveOutcome = (result) => {
19585
20360
 
19586
20361
  // src/commands/secrets-live.ts
19587
20362
  var SECRETS_LIVE_SCHEMA = "finitemonkey/secrets-live@1";
19588
- var USAGE3 = `finitemonkey secrets live <${LIVE_PROVIDERS.join("|")}> --env <VARIABLE> [--send]`;
19589
- var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
20363
+ var USAGE4 = `finitemonkey secrets live <${LIVE_PROVIDERS.join("|")}> --env <VARIABLE> [--send]`;
20364
+ var ENV_NAME2 = /^[A-Za-z_][A-Za-z0-9_]*$/;
19590
20365
  var target = (provider) => {
19591
20366
  const probe = LIVE_PROBES[provider];
19592
20367
  return { host: probe.host, url: `https://${probe.host}${probe.path}` };
@@ -19616,18 +20391,18 @@ var send = (ctx, provider, variable, key) => {
19616
20391
  var secretsLiveCommand = {
19617
20392
  path: ["secrets", "live"],
19618
20393
  summaryKey: "secrets.command.live",
19619
- usage: USAGE3,
20394
+ usage: USAGE4,
19620
20395
  status: "ready",
19621
20396
  maxPositionals: 1,
19622
20397
  options: { env: { type: "string" }, send: { type: "boolean" } },
19623
20398
  run: (ctx, args2) => {
19624
20399
  const provider = liveProvider(args2.positionals[0]);
19625
20400
  const variable = stringOption(args2.values, "env") ?? "";
19626
- if (provider === void 0 || !ENV_NAME.test(variable))
20401
+ if (provider === void 0 || !ENV_NAME2.test(variable))
19627
20402
  return Promise.resolve(
19628
20403
  failureFrom(
19629
20404
  ctx.t,
19630
- usageProblem("invalid_arguments", "error.invalid_arguments", USAGE3, {
20405
+ usageProblem("invalid_arguments", "error.invalid_arguments", USAGE4, {
19631
20406
  detail: provider === void 0 ? args2.positionals[0] ?? "<provider>" : "--env <VARIABLE>"
19632
20407
  })
19633
20408
  )
@@ -19637,7 +20412,7 @@ var secretsLiveCommand = {
19637
20412
  return Promise.resolve(
19638
20413
  failureFrom(
19639
20414
  ctx.t,
19640
- needsPersonProblem("secrets.error.person_only", `${USAGE3.replace(" [--send]", "")} --send`, {
20415
+ needsPersonProblem("secrets.error.person_only", `${USAGE4.replace(" [--send]", "")} --send`, {
19641
20416
  action: ctx.t("secrets.action.live")
19642
20417
  })
19643
20418
  )
@@ -19645,7 +20420,7 @@ var secretsLiveCommand = {
19645
20420
  const key = ctx.env[variable] ?? "";
19646
20421
  if (key === "")
19647
20422
  return Promise.resolve(
19648
- failureFrom(ctx.t, usageProblem("invalid_arguments", "secrets.live.no_value", USAGE3, { variable }))
20423
+ failureFrom(ctx.t, usageProblem("invalid_arguments", "secrets.live.no_value", USAGE4, { variable }))
19649
20424
  );
19650
20425
  return Promise.resolve(send(ctx, provider, variable, key));
19651
20426
  }
@@ -19673,8 +20448,8 @@ var modeOf3 = (values) => {
19673
20448
  };
19674
20449
  var normalizedTarget = (target2) => target2.replace(/\\/g, "/").replace(/^\.\/?/, "").replace(/\/$/, "");
19675
20450
  var scopeOf = (target2) => {
19676
- const prefix = normalizedTarget(target2);
19677
- return (file) => prefix === "" || file === prefix || file.startsWith(`${prefix}/`);
20451
+ const prefix2 = normalizedTarget(target2);
20452
+ return (file) => prefix2 === "" || file === prefix2 || file.startsWith(`${prefix2}/`);
19678
20453
  };
19679
20454
  var runScan = async (ctx, root, mode, target2) => {
19680
20455
  if (mode === "staged") return scanStaged(ctx.runtime, root);
@@ -19976,43 +20751,7 @@ var statuslineCommand = {
19976
20751
  };
19977
20752
 
19978
20753
  // src/task/create.ts
19979
- import { join as join50 } from "node:path";
19980
-
19981
- // src/core/yaml/write.ts
19982
- var PLAIN_SAFE = /^[A-Za-z_./][A-Za-z0-9_./ ()-]*$/;
19983
- var RESERVED2 = /^(true|false|null|~|-?\d.*)$/;
19984
- var scalarText = (value) => {
19985
- if (value === null) return "null";
19986
- if (typeof value !== "string") return String(value);
19987
- return PLAIN_SAFE.test(value) && !RESERVED2.test(value) && !value.endsWith(" ") ? value : JSON.stringify(value);
19988
- };
19989
- var isScalar = (value) => value === null || typeof value !== "object";
19990
- var inline2 = (value) => {
19991
- if (isScalar(value)) return scalarText(value);
19992
- if (Array.isArray(value)) {
19993
- if (value.length === 0) return "[]";
19994
- return value.every(isScalar) ? `[${value.map(scalarText).join(", ")}]` : void 0;
19995
- }
19996
- return Object.keys(value).length === 0 ? "{}" : void 0;
19997
- };
19998
- var lines2 = (value, indent) => {
19999
- if (Array.isArray(value)) {
20000
- return value.flatMap((item2) => {
20001
- const flat = inline2(item2);
20002
- if (flat !== void 0) return [`${indent}- ${flat}`];
20003
- const [head = "", ...rest] = lines2(item2, `${indent} `);
20004
- return [`${indent}- ${head.trimStart()}`, ...rest];
20005
- });
20006
- }
20007
- if (isScalar(value)) return [`${indent}${scalarText(value)}`];
20008
- return Object.entries(value).flatMap(([key, item2]) => {
20009
- const flat = inline2(item2);
20010
- const name = scalarText(key);
20011
- return flat === void 0 ? [`${indent}${name}:`, ...lines2(item2, `${indent} `)] : [`${indent}${name}: ${flat}`];
20012
- });
20013
- };
20014
- var stringifyYaml = (value) => `${lines2(value, "").join("\n")}
20015
- `;
20754
+ import { join as join51 } from "node:path";
20016
20755
 
20017
20756
  // src/task/serialize.ts
20018
20757
  var checkYaml = (check2) => ({
@@ -20064,11 +20803,11 @@ var explicitChecks = (draft) => [
20064
20803
  ];
20065
20804
  var draftChecks = (runtime, paths, draft) => {
20066
20805
  const disk = {
20067
- read: (relative11) => {
20068
- const text9 = runtime.readText(toAbsolutePath(join50(paths.root, relative11)));
20806
+ read: (relative12) => {
20807
+ const text9 = runtime.readText(toAbsolutePath(join51(paths.root, relative12)));
20069
20808
  return text9.ok ? text9.value : void 0;
20070
20809
  },
20071
- exists: (relative11) => runtime.stat(toAbsolutePath(join50(paths.root, relative11))) !== void 0
20810
+ exists: (relative12) => runtime.stat(toAbsolutePath(join51(paths.root, relative12))) !== void 0
20072
20811
  };
20073
20812
  const detected = detectChecks(disk);
20074
20813
  const base = draft.runs.length > 0 || draft.tests !== void 0 ? explicitChecks(draft) : detected;
@@ -20083,7 +20822,7 @@ var draftChecks = (runtime, paths, draft) => {
20083
20822
  { id: "merge", kind: "merge" },
20084
20823
  { id: "callers", kind: "callers" },
20085
20824
  ...draft.paths.length > 0 || draft.budget !== void 0 ? [{ id: "diff", kind: "diff" }] : [],
20086
- ...runtime.stat(toAbsolutePath(join50(paths.root, LOOK_CONFIG))) === void 0 ? [] : [{ id: "look", kind: "look" }],
20825
+ ...runtime.stat(toAbsolutePath(join51(paths.root, LOOK_CONFIG))) === void 0 ? [] : [{ id: "look", kind: "look" }],
20087
20826
  ...draft.git.length > 0 ? [{ id: "git", kind: "git", require: draft.git }] : [],
20088
20827
  { id: "pr", kind: "pr", requireOpen: draft.git.includes("pr_open") },
20089
20828
  ...draft.manual.map((description, index) => ({
@@ -20125,8 +20864,8 @@ var createTask = (runtime, paths, draft) => {
20125
20864
  };
20126
20865
 
20127
20866
  // src/commands/task.ts
20128
- var problem3 = (code, messageKey, fix, params2 = {}, exitCode = ExitCode.Usage) => ({
20129
- code,
20867
+ var problem3 = (code2, messageKey, fix, params2 = {}, exitCode = ExitCode.Usage) => ({
20868
+ code: code2,
20130
20869
  messageKey,
20131
20870
  params: params2,
20132
20871
  fix,
@@ -20325,9 +21064,9 @@ var applyItem = (project, item2) => {
20325
21064
  if (item2.after === void 0 || item2.action === "gone" || item2.action.startsWith("keep")) return accept(void 0);
20326
21065
  return writeText(project, item2.path, item2.after);
20327
21066
  };
20328
- var isEmptyDir = (project, relative11) => {
20329
- const listed4 = project.runtime.listDir(project.abs(relative11));
20330
- return listed4.ok && listed4.value.length === 0;
21067
+ var isEmptyDir = (project, relative12) => {
21068
+ const listed5 = project.runtime.listDir(project.abs(relative12));
21069
+ return listed5.ok && listed5.value.length === 0;
20331
21070
  };
20332
21071
  var finish3 = (project, plan) => {
20333
21072
  const target2 = plan.purge ? STATE_DIR : LOCK_FILE;
@@ -20367,7 +21106,7 @@ var dataOf3 = (root, plan, applied) => ({
20367
21106
  kept: plan.items.filter((item2) => item2.action.startsWith("keep")).map(({ path }) => path),
20368
21107
  outside: OUTSIDE.map(({ command }) => command)
20369
21108
  });
20370
- var linesOf3 = (ctx, data) => [
21109
+ var linesOf4 = (ctx, data) => [
20371
21110
  ctx.t("install.uninstall.title", { root: data.root }),
20372
21111
  ...data.actions.map(({ path, action }) => ` ${action.padEnd(15)}${path}`),
20373
21112
  ...data.purge ? [ctx.t("install.uninstall.purge")] : [ctx.t("install.uninstall.keeps_state")],
@@ -20388,16 +21127,16 @@ var runUninstall = (ctx, args2) => {
20388
21127
  const options = { purge: args2.values["purge"] === true, keepGenerated: args2.values["keep-generated"] === true };
20389
21128
  const plan = planRemoval(project, lock.value, options);
20390
21129
  const preview = dataOf3(project.root, plan, false);
20391
- if (ctx.dryRun) return success(preview, [...linesOf3(ctx, preview), ctx.t("install.dry_run")]);
21130
+ if (ctx.dryRun) return success(preview, [...linesOf4(ctx, preview), ctx.t("install.dry_run")]);
20392
21131
  if (!ctx.yes) {
20393
21132
  const next2 = fixFor(args2);
20394
21133
  const problem4 = confirmProblem("uninstall", ctx.t("install.uninstall.question"), next2);
20395
- return { ...failureFrom(ctx.t, { ...problem4, data: preview }, linesOf3(ctx, preview)), next: next2 };
21134
+ return { ...failureFrom(ctx.t, { ...problem4, data: preview }, linesOf4(ctx, preview)), next: next2 };
20396
21135
  }
20397
21136
  const removed = applyRemoval(project, plan);
20398
21137
  if (!removed.ok) return failureFrom(ctx.t, applyProblem(removed.error));
20399
21138
  const data = dataOf3(project.root, plan, true);
20400
- return success(data, [...linesOf3(ctx, data), ctx.t("install.uninstall.done")]);
21139
+ return success(data, [...linesOf4(ctx, data), ctx.t("install.uninstall.done")]);
20401
21140
  };
20402
21141
  var uninstallCommand = {
20403
21142
  path: ["uninstall"],
@@ -20493,12 +21232,13 @@ var REGISTRY = [
20493
21232
  reviewPacketCommand,
20494
21233
  statuslineCommand,
20495
21234
  critiqueVerifyCommand,
21235
+ inventoryCommand,
20496
21236
  versionCommand,
20497
21237
  helpCommand
20498
21238
  ];
20499
21239
 
20500
21240
  // src/i18n/lang-settings.ts
20501
- import { join as join51 } from "node:path";
21241
+ import { join as join52 } from "node:path";
20502
21242
  var read3 = (runtime, path) => {
20503
21243
  const text9 = runtime.readText(toAbsolutePath(path));
20504
21244
  return text9.ok ? text9.value : void 0;
@@ -20513,14 +21253,14 @@ var languageIn = (text9) => {
20513
21253
  const value = parsed.ok && isRecord(parsed.value) ? parsed.value["language"] : void 0;
20514
21254
  return typeof value === "string" && value.trim() !== "" ? value : void 0;
20515
21255
  };
20516
- var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join51(runtime.homeDir(), ".claude");
21256
+ var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join52(runtime.homeDir(), ".claude");
20517
21257
  var langSettings = (runtime, cwd, env, platform) => {
20518
21258
  const root = findGitRoot(cwd, runtime) ?? cwd;
20519
- const config = parseConfigYaml(read3(runtime, join51(root, CONFIG_FILE2)) ?? "");
21259
+ const config = parseConfigYaml(read3(runtime, join52(root, CONFIG_FILE2)) ?? "");
20520
21260
  const claudeFiles = [
20521
- join51(root, ".claude", "settings.local.json"),
20522
- join51(root, ".claude", "settings.json"),
20523
- join51(claudeDir(runtime, env), "settings.json")
21261
+ join52(root, ".claude", "settings.local.json"),
21262
+ join52(root, ".claude", "settings.json"),
21263
+ join52(claudeDir(runtime, env), "settings.json")
20524
21264
  ];
20525
21265
  return {
20526
21266
  config: configText(config, "lang"),
@@ -20532,18 +21272,18 @@ var langSettings = (runtime, cwd, env, platform) => {
20532
21272
 
20533
21273
  // src/core/suggest.ts
20534
21274
  var distance2 = (a, b) => {
20535
- const row = Array.from({ length: b.length + 1 }, (_, index) => index);
21275
+ const row2 = Array.from({ length: b.length + 1 }, (_, index) => index);
20536
21276
  for (let i = 1; i <= a.length; i += 1) {
20537
- let diagonal = row[0] ?? 0;
20538
- row[0] = i;
21277
+ let diagonal = row2[0] ?? 0;
21278
+ row2[0] = i;
20539
21279
  for (let j = 1; j <= b.length; j += 1) {
20540
- const above = row[j] ?? 0;
21280
+ const above = row2[j] ?? 0;
20541
21281
  const cost = a[i - 1] === b[j - 1] ? 0 : 1;
20542
- row[j] = Math.min(above + 1, (row[j - 1] ?? 0) + 1, diagonal + cost);
21282
+ row2[j] = Math.min(above + 1, (row2[j - 1] ?? 0) + 1, diagonal + cost);
20543
21283
  diagonal = above;
20544
21284
  }
20545
21285
  }
20546
- return row[b.length] ?? 0;
21286
+ return row2[b.length] ?? 0;
20547
21287
  };
20548
21288
  var closest = (input, candidates) => {
20549
21289
  const group = candidates.find((name) => name.startsWith(`${input} `));
@@ -20676,8 +21416,8 @@ var run3 = async (options) => {
20676
21416
  import { fileURLToPath as fileURLToPath4 } from "node:url";
20677
21417
  var UNKNOWN_VERSION = "0.0.0-unknown";
20678
21418
  var VERSION_SOURCES = ["../package.json", "../.claude-plugin/plugin.json"];
20679
- var versionAt = (readText, moduleUrl, relative11) => {
20680
- const text9 = readText(toAbsolutePath(fileURLToPath4(new URL(relative11, moduleUrl))));
21419
+ var versionAt = (readText, moduleUrl, relative12) => {
21420
+ const text9 = readText(toAbsolutePath(fileURLToPath4(new URL(relative12, moduleUrl))));
20681
21421
  if (!text9.ok) return void 0;
20682
21422
  const parsed = parseJson(text9.value);
20683
21423
  if (!parsed.ok || !isRecord(parsed.value)) return void 0;
@@ -20685,8 +21425,8 @@ var versionAt = (readText, moduleUrl, relative11) => {
20685
21425
  return typeof version === "string" ? version : void 0;
20686
21426
  };
20687
21427
  var readPackageVersion = (readText, moduleUrl) => {
20688
- for (const relative11 of VERSION_SOURCES) {
20689
- const version = versionAt(readText, moduleUrl, relative11);
21428
+ for (const relative12 of VERSION_SOURCES) {
21429
+ const version = versionAt(readText, moduleUrl, relative12);
20690
21430
  if (version !== void 0) return version;
20691
21431
  }
20692
21432
  return UNKNOWN_VERSION;
@@ -20700,7 +21440,7 @@ var flush = (stream) => new Promise((resolve2) => {
20700
21440
  });
20701
21441
  var main = async () => {
20702
21442
  const runtime = nodeRuntime();
20703
- const code = await run3({
21443
+ const code2 = await run3({
20704
21444
  argv: process.argv.slice(2),
20705
21445
  env: process.env,
20706
21446
  cwd: toAbsolutePath(process.cwd()),
@@ -20710,7 +21450,7 @@ var main = async () => {
20710
21450
  runtime
20711
21451
  });
20712
21452
  await Promise.all([flush(process.stdout), flush(process.stderr)]);
20713
- process.exit(code);
21453
+ process.exit(code2);
20714
21454
  };
20715
21455
  if (isEntryPoint(import.meta.url, process.argv[1], nodeRealpath, process.platform)) await main();
20716
21456
  export {