finitemonkey 0.1.0-next.25 → 0.1.0-next.26

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.
Files changed (2) hide show
  1. package/dist/finitemonkey.mjs +1221 -894
  2. package/package.json +1 -1
@@ -28,15 +28,15 @@ import { dirname } from "node:path";
28
28
  var accept = (value) => ({ ok: true, value });
29
29
  var reject = (error) => ({ ok: false, error });
30
30
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
31
- var parseJson = (text8) => {
31
+ var parseJson = (text9) => {
32
32
  try {
33
- return accept(JSON.parse(text8));
33
+ return accept(JSON.parse(text9));
34
34
  } catch (error) {
35
35
  return reject(error instanceof Error ? error.message : String(error));
36
36
  }
37
37
  };
38
- var parseJsonObject = (text8) => {
39
- const parsed = parseJson(text8);
38
+ var parseJsonObject = (text9) => {
39
+ const parsed = parseJson(text9);
40
40
  if (!parsed.ok) return parsed;
41
41
  return isRecord(parsed.value) ? accept(parsed.value) : reject("expected a JSON object");
42
42
  };
@@ -160,13 +160,13 @@ var attempt = (work) => {
160
160
  return reject(messageOf(error));
161
161
  }
162
162
  };
163
- var nodeWriteText = (path, text8) => {
163
+ var nodeWriteText = (path, text9) => {
164
164
  const temp = `${path}.${String(process.pid)}.tmp`;
165
165
  try {
166
166
  mkdirSync2(dirname2(path), { recursive: true, mode: 448 });
167
167
  const fd = openSync(temp, "w", 384);
168
168
  try {
169
- writeSync(fd, text8);
169
+ writeSync(fd, text9);
170
170
  fsyncSync(fd);
171
171
  } finally {
172
172
  closeSync(fd);
@@ -770,6 +770,497 @@ var appCheckCommand = {
770
770
  }
771
771
  };
772
772
 
773
+ // src/git/facts.ts
774
+ var memo = (compute) => {
775
+ let cached3;
776
+ return () => {
777
+ cached3 ??= { value: compute() };
778
+ return cached3.value;
779
+ };
780
+ };
781
+ var memoBy = (compute) => {
782
+ const cache = /* @__PURE__ */ new Map();
783
+ return (key) => {
784
+ const hit3 = cache.get(key);
785
+ if (hit3 !== void 0) return hit3.value;
786
+ const value = compute(key);
787
+ cache.set(key, { value });
788
+ return value;
789
+ };
790
+ };
791
+ var STATE_PREFIX = ".finitemonkey/";
792
+ var PORCELAIN_FIELDS = { ordinary: 8, renamed: 9, unmerged: 10 };
793
+ var entryOf = (kind, xy, path) => ({
794
+ path,
795
+ kind,
796
+ staged: kind !== "untracked" && !xy.startsWith("."),
797
+ unstaged: kind === "untracked" || xy.charAt(1) !== "."
798
+ });
799
+ var afterFields = (record3, count4) => record3.split(" ").slice(count4).join(" ");
800
+ var parsePorcelainV2 = (stdout) => {
801
+ const records = stdout.split("\0");
802
+ const out = [];
803
+ for (let index = 0; index < records.length; index += 1) {
804
+ const record3 = records[index] ?? "";
805
+ const xy = record3.split(" ")[1] ?? "..";
806
+ if (record3.startsWith("1 ")) out.push(entryOf("changed", xy, afterFields(record3, PORCELAIN_FIELDS.ordinary)));
807
+ else if (record3.startsWith("2 ")) {
808
+ out.push(entryOf("changed", xy, afterFields(record3, PORCELAIN_FIELDS.renamed)));
809
+ index += 1;
810
+ } else if (record3.startsWith("u ")) {
811
+ out.push(entryOf("unmerged", xy, afterFields(record3, PORCELAIN_FIELDS.unmerged)));
812
+ } else if (record3.startsWith("? ")) out.push(entryOf("untracked", "??", record3.slice(2)));
813
+ }
814
+ return out.filter((entry2) => entry2.path !== "" && !entry2.path.startsWith(STATE_PREFIX));
815
+ };
816
+
817
+ // src/git/project-config.ts
818
+ import { join as join2 } from "node:path";
819
+ var CONFIG_FILE = ".finitemonkey/config.yml";
820
+ var unquote = (value) => value.trim().replace(/^(["'])(.*)\1$/s, "$2");
821
+ var stripComment = (line2) => {
822
+ let quote;
823
+ for (let index = 0; index < line2.length; index += 1) {
824
+ const char = line2.charAt(index);
825
+ if (quote === void 0 && (char === '"' || char === "'")) quote = char;
826
+ else if (char === quote) quote = void 0;
827
+ else if (quote === void 0 && char === "#" && (index === 0 || /\s/.test(line2.charAt(index - 1)))) {
828
+ return line2.slice(0, index);
829
+ }
830
+ }
831
+ return line2;
832
+ };
833
+ var flowList = (value) => {
834
+ const trimmed = value.trim();
835
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
836
+ return trimmed.slice(1, -1).split(",").map(unquote).filter((item2) => item2 !== "");
837
+ };
838
+ var keyValue = (body2) => {
839
+ const match = /^("[^"]*"|'[^']*'|[^:]+?)\s*:(?:\s+(.*))?$/.exec(body2);
840
+ if (match === null) return void 0;
841
+ return { key: unquote(match[1] ?? ""), value: match[2] ?? "" };
842
+ };
843
+ var addItem = (frame, item2) => {
844
+ if (frame.key === void 0) return;
845
+ const parent = frame.tree[frame.key];
846
+ if (Array.isArray(parent)) parent.push(item2);
847
+ else frame.tree[frame.key] = [item2];
848
+ };
849
+ var placeLine = (stack, indent, body2) => {
850
+ while (stack.length > 1 && (stack[stack.length - 1]?.indent ?? 0) >= indent && !body2.startsWith("- ")) stack.pop();
851
+ const top = stack[stack.length - 1];
852
+ if (top === void 0) return;
853
+ if (body2.startsWith("- ")) {
854
+ addItem(top, unquote(body2.slice(2)));
855
+ return;
856
+ }
857
+ const pair = keyValue(body2);
858
+ if (pair === void 0) return;
859
+ const parentTree = top.key === void 0 ? top.tree : ensureTree(top.tree, top.key);
860
+ if (pair.value === "") {
861
+ stack.push({ indent, tree: parentTree, key: pair.key });
862
+ return;
863
+ }
864
+ parentTree[pair.key] = flowList(pair.value) ?? unquote(pair.value);
865
+ };
866
+ var ensureTree = (tree, key) => {
867
+ const existing = tree[key];
868
+ if (existing !== void 0 && typeof existing !== "string" && !Array.isArray(existing)) return existing;
869
+ const fresh3 = {};
870
+ tree[key] = fresh3;
871
+ return fresh3;
872
+ };
873
+ var parseConfigYaml = (text9) => {
874
+ const root = {};
875
+ const stack = [{ indent: -1, tree: root, key: void 0 }];
876
+ for (const raw of text9.split(/\r?\n/)) {
877
+ const line2 = stripComment(raw).trimEnd();
878
+ if (line2.trim() === "" || line2.trim() === "---") continue;
879
+ const indent = line2.length - line2.trimStart().length;
880
+ placeLine(stack, indent, line2.trim());
881
+ }
882
+ return root;
883
+ };
884
+ var isTree = (node) => typeof node === "object" && !Array.isArray(node);
885
+ var configAt = (tree, ...path) => path.reduce((node, key) => isTree(node) ? node[key] : void 0, tree);
886
+ var configList = (tree, ...path) => {
887
+ const node = configAt(tree, ...path);
888
+ if (typeof node === "string") return node === "" ? [] : [node];
889
+ return Array.isArray(node) ? node.filter((item2) => typeof item2 === "string") : [];
890
+ };
891
+ var configMap = (tree, ...path) => {
892
+ const node = configAt(tree, ...path);
893
+ if (!isTree(node)) return {};
894
+ return Object.fromEntries(
895
+ Object.entries(node).filter((entry2) => typeof entry2[1] === "string")
896
+ );
897
+ };
898
+ var loadProjectConfig = (runtime, root) => {
899
+ const text9 = runtime.readText(toAbsolutePath(join2(root, CONFIG_FILE)));
900
+ return text9.ok ? parseConfigYaml(text9.value) : {};
901
+ };
902
+
903
+ // src/git/repo.ts
904
+ var DEFAULT_PROTECTED = ["main", "master", "release/*", "prod", "production", "rc-*"];
905
+ var openRepo = (runtime, dir) => {
906
+ const result = runtime.git(["rev-parse", "--show-toplevel", "--absolute-git-dir", "--show-prefix"], dir);
907
+ if (!result.ok) return void 0;
908
+ const [root, gitDir, prefix] = result.stdout.split("\n");
909
+ if (root === void 0 || root === "" || gitDir === void 0 || gitDir === "") return void 0;
910
+ return {
911
+ root: toAbsolutePath(root),
912
+ gitDir: toAbsolutePath(gitDir),
913
+ prefix: (prefix ?? "").replace(/\/$/, "")
914
+ };
915
+ };
916
+ var globRegex = (pattern) => new RegExp(`^${pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`);
917
+ var matchesBranch = (branch, patterns) => patterns.some((pattern) => globRegex(pattern).test(branch));
918
+ var line = (runtime, root, args2) => {
919
+ const result = runtime.git(args2, root);
920
+ const value = result.stdout.trim();
921
+ return result.ok && value !== "" ? value : void 0;
922
+ };
923
+ var refExists = (runtime, root, ref) => runtime.git(["show-ref", "--verify", "--quiet", ref], root).ok;
924
+ var defaultBranchOf = (runtime, root) => {
925
+ const remote = line(runtime, root, ["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"]);
926
+ if (remote !== void 0) return remote.replace(/^[^/]+\//, "");
927
+ const configured = line(runtime, root, ["config", "--get", "init.defaultBranch"]);
928
+ if (configured !== void 0 && refExists(runtime, root, `refs/heads/${configured}`)) return configured;
929
+ return refExists(runtime, root, "refs/heads/master") && !refExists(runtime, root, "refs/heads/main") ? "master" : "main";
930
+ };
931
+ var upstreamOf = (runtime, root, branch) => {
932
+ const remote = line(runtime, root, ["config", "--get", `branch.${branch}.remote`]);
933
+ const merge = line(runtime, root, ["config", "--get", `branch.${branch}.merge`]);
934
+ if (remote === void 0 || merge === void 0 || remote === ".") return void 0;
935
+ const name = merge.replace(/^refs\/heads\//, "");
936
+ return { branch: name, remoteRef: `refs/remotes/${remote}/${name}` };
937
+ };
938
+ var integrationRefs = (runtime, root, defaultBranch) => [`refs/heads/${defaultBranch}`, `refs/remotes/origin/${defaultBranch}`].filter(
939
+ (ref) => refExists(runtime, root, ref)
940
+ );
941
+ var contained = (runtime, root, branch, targets2) => {
942
+ const tip = line(runtime, root, ["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`]);
943
+ if (tip === void 0) return true;
944
+ return targets2.filter((target2) => target2 !== `refs/heads/${branch}`).some((target2) => runtime.git(["merge-base", "--is-ancestor", tip, target2], root).ok);
945
+ };
946
+ var shared = (runtime, root, upstream, base) => {
947
+ if (upstream === void 0 || !refExists(runtime, root, upstream.remoteRef)) return false;
948
+ const own = /<([^>]*)>/.exec(line(runtime, root, ["var", "GIT_AUTHOR_IDENT"]) ?? "")?.[1] ?? "";
949
+ const log = runtime.git(["log", "--format=%ae", upstream.remoteRef, ...base.map((ref) => `^${ref}`)], root);
950
+ return log.ok && log.stdout.split("\n").some((email) => email.trim() !== "" && email.trim() !== own);
951
+ };
952
+ var protectedPatterns = (config, defaultBranch) => [
953
+ defaultBranch,
954
+ ...DEFAULT_PROTECTED,
955
+ ...configList(config, "git", "protected")
956
+ ];
957
+ var repoFacts = (runtime, repo, config = loadProjectConfig(runtime, repo.root)) => {
958
+ const { root } = repo;
959
+ const defaultBranch = memo(() => defaultBranchOf(runtime, root));
960
+ const patterns = memo(() => protectedPatterns(config, defaultBranch()));
961
+ const upstream = memoBy((branch) => upstreamOf(runtime, root, branch));
962
+ const integration = memo(() => integrationRefs(runtime, root, defaultBranch()));
963
+ return {
964
+ prefix: repo.prefix,
965
+ currentBranch: memo(() => line(runtime, root, ["symbolic-ref", "--quiet", "--short", "HEAD"])),
966
+ defaultBranch,
967
+ isProtected: (branch) => matchesBranch(branch, patterns()),
968
+ dirty: memo(() => {
969
+ const status = runtime.git(["status", "--porcelain=v2", "-z", "--untracked-files=all"], root);
970
+ return status.ok ? parsePorcelainV2(status.stdout) : void 0;
971
+ }),
972
+ isRef: memoBy((name) => runtime.git(["rev-parse", "--verify", "--quiet", `${name}^{commit}`], root).ok),
973
+ branchExists: memoBy((name) => refExists(runtime, root, `refs/heads/${name}`)),
974
+ branchContained: memoBy((branch) => {
975
+ const up = upstream(branch);
976
+ return contained(runtime, root, branch, [...up === void 0 ? [] : [up.remoteRef], ...integration()]);
977
+ }),
978
+ upstream,
979
+ sharedBranch: memoBy((branch) => shared(runtime, root, upstream(branch), integration()))
980
+ };
981
+ };
982
+
983
+ // src/hooks/stdin.ts
984
+ var readAll = (source) => new Promise((resolve2, reject2) => {
985
+ if (source.isTTY === true) {
986
+ resolve2("");
987
+ return;
988
+ }
989
+ const chunks = [];
990
+ source.setEncoding?.("utf8");
991
+ source.on("data", (chunk) => {
992
+ chunks.push(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
993
+ });
994
+ source.on("end", () => {
995
+ resolve2(chunks.join(""));
996
+ });
997
+ source.on("error", reject2);
998
+ });
999
+ var DeadlineError = class extends Error {
1000
+ ms;
1001
+ constructor(ms) {
1002
+ super(`deadline of ${String(ms)} ms exceeded`);
1003
+ this.name = "DeadlineError";
1004
+ this.ms = ms;
1005
+ }
1006
+ };
1007
+ var withDeadline = (work, ms, startTimer) => new Promise((resolve2, reject2) => {
1008
+ const cancel = startTimer(ms, () => {
1009
+ reject2(new DeadlineError(ms));
1010
+ });
1011
+ work.then(
1012
+ (value) => {
1013
+ cancel();
1014
+ resolve2(value);
1015
+ },
1016
+ (error) => {
1017
+ cancel();
1018
+ reject2(error instanceof Error ? error : new Error(String(error)));
1019
+ }
1020
+ );
1021
+ });
1022
+
1023
+ // src/review/findings.ts
1024
+ var GROUPS = ["must", "should", "nice"];
1025
+ var VERDICTS = ["approve", "approve-with-changes", "request-changes"];
1026
+ var text2 = (record3, key) => {
1027
+ const value = record3[key];
1028
+ return typeof value === "string" ? value.trim() : "";
1029
+ };
1030
+ var reproOf = (value) => {
1031
+ if (!isRecord(value)) return void 0;
1032
+ const command = text2(value, "command");
1033
+ const expect = text2(value, "expect");
1034
+ return command === "" || expect === "" ? void 0 : { command, expect };
1035
+ };
1036
+ var findingOf = (value, index) => {
1037
+ if (!isRecord(value)) return `finding ${String(index + 1)} is not an object`;
1038
+ const group = GROUPS.find((item2) => item2 === value["group"]);
1039
+ if (group === void 0) return `finding ${String(index + 1)} needs group must, should or nice`;
1040
+ const fields = ["what", "problem", "fix", "ifNotFixed"];
1041
+ const missing2 = fields.filter((key) => text2(value, key) === "");
1042
+ if (missing2.length > 0) return `finding ${String(index + 1)} is missing ${missing2.join(", ")}`;
1043
+ const repro = reproOf(value["repro"]);
1044
+ const demoted = group === "must" && repro === void 0;
1045
+ return {
1046
+ id: index + 1,
1047
+ group: demoted ? "should" : group,
1048
+ what: text2(value, "what"),
1049
+ problem: text2(value, "problem"),
1050
+ fix: text2(value, "fix"),
1051
+ ifNotFixed: text2(value, "ifNotFixed"),
1052
+ ...repro === void 0 ? {} : { repro },
1053
+ activeRisk: value["activeRisk"] === true,
1054
+ demoted
1055
+ };
1056
+ };
1057
+ var parseCritique = (value) => {
1058
+ if (!isRecord(value)) return ["the critique must be a JSON object"];
1059
+ const verdict = VERDICTS.find((item2) => item2 === value["verdict"]);
1060
+ const list4 = value["findings"];
1061
+ const parsed = Array.isArray(list4) ? list4.map(findingOf) : ["findings must be a list"];
1062
+ const errors = [
1063
+ ...verdict === void 0 ? [`verdict must be one of ${VERDICTS.join(", ")}`] : [],
1064
+ ...parsed.filter((item2) => typeof item2 === "string")
1065
+ ];
1066
+ if (errors.length > 0 || verdict === void 0) return errors;
1067
+ return { verdict, findings: parsed.filter((item2) => typeof item2 !== "string") };
1068
+ };
1069
+
1070
+ // src/review/report.ts
1071
+ import { join as join4 } from "node:path";
1072
+
1073
+ // src/review/reproduce.ts
1074
+ import { join as join3 } from "node:path";
1075
+ var REVIEW_LEDGER = ".finitemonkey/review/ledger.jsonl";
1076
+ var REPRO_TIMEOUT_MS = 12e4;
1077
+ var fails = (run4) => run4 !== void 0 && run4.exitCode !== 0;
1078
+ var outcomeOf = (base, head) => {
1079
+ if (base === void 0 || head === void 0 || head.exitCode === null || base.exitCode === null) return "not-run";
1080
+ if (fails(head) && !fails(base)) return "head-only";
1081
+ return fails(head) && fails(base) ? "already-there" : "not-reproduced";
1082
+ };
1083
+ var worktreeDir = (root, side) => toAbsolutePath(join3(root, ".finitemonkey", "review", `worktree-${side}`));
1084
+ var runAt = (runtime, root, side, rev, findings) => {
1085
+ const dir = worktreeDir(root, side);
1086
+ runtime.git(["worktree", "remove", "--force", dir], root);
1087
+ const added = runtime.git(["worktree", "add", "--detach", "--force", dir, rev], root);
1088
+ if (!added.ok) return [];
1089
+ try {
1090
+ return findings.flatMap((finding4) => {
1091
+ if (finding4.repro === void 0) return [];
1092
+ const result = runtime.exec({
1093
+ command: finding4.repro.command,
1094
+ args: [],
1095
+ cwd: dir,
1096
+ timeoutMs: REPRO_TIMEOUT_MS,
1097
+ shell: true
1098
+ });
1099
+ const exitCode = result.found && !result.timedOut ? result.exitCode : null;
1100
+ return [{ finding: finding4.id, rev, side, command: finding4.repro.command, exitCode, at: runtime.now() }];
1101
+ });
1102
+ } finally {
1103
+ runtime.git(["worktree", "remove", "--force", dir], root);
1104
+ }
1105
+ };
1106
+ var reproduce = (runtime, root, sides, findings) => {
1107
+ const musts = findings.filter((finding4) => finding4.group === "must");
1108
+ const runs = [...runAt(runtime, root, "base", sides.base, musts), ...runAt(runtime, root, "head", sides.head, musts)];
1109
+ if (runs.length > 0) {
1110
+ runtime.mkdir(toAbsolutePath(join3(root, ".finitemonkey", "review")));
1111
+ runtime.appendText(
1112
+ toAbsolutePath(join3(root, REVIEW_LEDGER)),
1113
+ runs.map((run4) => `${JSON.stringify(run4)}
1114
+ `).join("")
1115
+ );
1116
+ }
1117
+ return findings.map((finding4) => {
1118
+ if (finding4.group !== "must") return { finding: finding4, outcome: "not-run" };
1119
+ const base = runs.find((run4) => run4.finding === finding4.id && run4.side === "base");
1120
+ const head = runs.find((run4) => run4.finding === finding4.id && run4.side === "head");
1121
+ return {
1122
+ finding: finding4,
1123
+ ...base === void 0 ? {} : { base },
1124
+ ...head === void 0 ? {} : { head },
1125
+ outcome: outcomeOf(base, head)
1126
+ };
1127
+ });
1128
+ };
1129
+ var settle = (item2) => {
1130
+ const { finding: finding4, outcome: outcome2 } = item2;
1131
+ if (finding4.group !== "must" || outcome2 === "head-only" || outcome2 === "not-run")
1132
+ return { finding: finding4, group: finding4.group, decisionNeeded: false };
1133
+ if (outcome2 === "already-there") return { finding: finding4, group: "already-there", decisionNeeded: finding4.activeRisk };
1134
+ return { finding: finding4, group: "not-reproduced", decisionNeeded: false };
1135
+ };
1136
+
1137
+ // src/review/report.ts
1138
+ var readLedger = (runtime, root) => {
1139
+ const raw = runtime.readText(toAbsolutePath(join4(root, REVIEW_LEDGER)));
1140
+ if (!raw.ok) return [];
1141
+ return raw.value.split("\n").flatMap((line2) => {
1142
+ const parsed = line2.trim() === "" ? void 0 : parseJsonObject(line2);
1143
+ if (parsed === void 0 || !parsed.ok || !isRecord(parsed.value)) return [];
1144
+ const { finding: finding4, side, command, exitCode } = parsed.value;
1145
+ if (typeof finding4 !== "number" || typeof side !== "string" || typeof command !== "string") return [];
1146
+ return [{ finding: finding4, side, command, exitCode: typeof exitCode === "number" ? exitCode : null }];
1147
+ });
1148
+ };
1149
+ 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)}`;
1153
+ };
1154
+ var findingLines = (t, settled) => GROUP_ORDER.flatMap((group) => {
1155
+ const items = settled.filter((item2) => item2.group === group);
1156
+ if (items.length === 0) return [];
1157
+ return [
1158
+ "",
1159
+ `## ${t(`critique.group.${group}`)}`,
1160
+ ...items.map(
1161
+ ({ finding: finding4 }) => t("critique.finding", {
1162
+ id: finding4.id,
1163
+ what: finding4.what,
1164
+ problem: finding4.problem,
1165
+ fix: finding4.fix,
1166
+ risk: finding4.ifNotFixed
1167
+ })
1168
+ )
1169
+ ];
1170
+ });
1171
+ var tableLines = (t, checked, ledger) => {
1172
+ const rows = checked.filter((item2) => item2.finding.group === "must");
1173
+ if (rows.length === 0) return [];
1174
+ const last = (id, side) => ledger.filter((row) => row.finding === id && row.side === side).at(-1);
1175
+ return [
1176
+ "",
1177
+ `| # | ${t("critique.table.command")} | base | head | ${t("critique.table.outcome")} |`,
1178
+ "|---|---|---|---|---|",
1179
+ ...rows.map(
1180
+ (item2) => `| ${String(item2.finding.id)} | \`${item2.finding.repro?.command ?? "-"}\` | ${resultText(last(item2.finding.id, "base"))} | ${resultText(last(item2.finding.id, "head"))} | ${t(`critique.outcome.${item2.outcome}`)} |`
1181
+ )
1182
+ ];
1183
+ };
1184
+ var critiqueLines = (t, critique, checked, ledger) => {
1185
+ const settled = checked.map(settle);
1186
+ const demoted = critique.findings.filter((finding4) => finding4.demoted);
1187
+ const decisions = settled.filter((item2) => item2.decisionNeeded);
1188
+ return [
1189
+ `# ${t("critique.title")}`,
1190
+ ...findingLines(t, settled),
1191
+ ...tableLines(t, checked, ledger),
1192
+ ...demoted.map((finding4) => t("critique.demoted", { id: finding4.id })),
1193
+ ...decisions.map((item2) => t("critique.decision", { id: item2.finding.id, what: item2.finding.what })),
1194
+ "",
1195
+ t("critique.verdict", { verdict: critique.verdict })
1196
+ ];
1197
+ };
1198
+
1199
+ // src/commands/git-problems.ts
1200
+ var notRepository = (dir) => ({
1201
+ code: "not_a_git_repo",
1202
+ messageKey: "error.not_a_git_repo",
1203
+ params: { dir },
1204
+ fix: "git init",
1205
+ exitCode: ExitCode.NotConfigured
1206
+ });
1207
+ var gitProblem = (code, messageKey, detail, fix, exitCode = ExitCode.CheckFailed) => ({ code, messageKey, params: { detail }, fix, exitCode });
1208
+ var repoOf = (ctx) => openRepo(ctx.runtime, ctx.cwd);
1209
+
1210
+ // src/commands/critique.ts
1211
+ var CRITIQUE_SCHEMA = "finitemonkey/critique@1";
1212
+ var USAGE = "finitemonkey critique verify [--base <ref>] [--file <findings.json>] (or the findings JSON on stdin)";
1213
+ var findingsText = async (ctx, root, file) => {
1214
+ if (file === void 0) return ctx.io.stdin.isTTY === true ? void 0 : readAll(ctx.io.stdin);
1215
+ const text9 = ctx.runtime.readText(toAbsolutePath(file, root));
1216
+ return text9.ok ? text9.value : void 0;
1217
+ };
1218
+ var critiqueVerifyCommand = {
1219
+ path: ["critique", "verify"],
1220
+ summaryKey: "critique.command.verify",
1221
+ usage: USAGE,
1222
+ status: "ready",
1223
+ options: { file: { type: "string" }, base: { type: "string" } },
1224
+ run: async (ctx, args2) => {
1225
+ const repo = repoOf(ctx);
1226
+ if (repo === void 0) return failureFrom(ctx.t, notRepository(ctx.cwd));
1227
+ const file = stringOption(args2.values, "file");
1228
+ const raw = await findingsText(ctx, repo.root, file);
1229
+ const parsed = raw === void 0 ? void 0 : parseJsonObject(raw);
1230
+ const critique = parsed?.ok === true ? parseCritique(parsed.value) : [`${file ?? "stdin"} is missing or not JSON`];
1231
+ if (Array.isArray(critique) || !("verdict" in critique))
1232
+ return failureFrom(
1233
+ ctx.t,
1234
+ usageProblem("invalid_arguments", "critique.error.invalid", USAGE, { detail: critique.join("; ") })
1235
+ );
1236
+ const base = stringOption(args2.values, "base") ?? repoFacts(ctx.runtime, repo).defaultBranch();
1237
+ const head = ctx.runtime.git(["rev-parse", "HEAD"], repo.root).stdout.trim();
1238
+ const checked = reproduce(ctx.runtime, repo.root, { base, head }, critique.findings);
1239
+ const human2 = critiqueLines(ctx.t, critique, checked, readLedger(ctx.runtime, repo.root));
1240
+ const confirmed = checked.map(settle).filter((item2) => item2.group === "must").length;
1241
+ const data = {
1242
+ schema: CRITIQUE_SCHEMA,
1243
+ verdict: critique.verdict,
1244
+ base,
1245
+ head,
1246
+ checked: checked.map((item2) => ({ ...item2, settled: settle(item2).group }))
1247
+ };
1248
+ if (confirmed === 0) return success(data, human2);
1249
+ return failureFrom(
1250
+ ctx.t,
1251
+ {
1252
+ code: "critique_must_fix",
1253
+ messageKey: "critique.error.must_fix",
1254
+ params: { count: confirmed },
1255
+ fix: "fix the must-fix findings, then run finitemonkey critique verify again",
1256
+ exitCode: ExitCode.CheckFailed,
1257
+ data
1258
+ },
1259
+ human2
1260
+ );
1261
+ }
1262
+ };
1263
+
773
1264
  // src/commands/doctor.ts
774
1265
  import { dirname as dirname7 } from "node:path";
775
1266
 
@@ -778,13 +1269,13 @@ import { dirname as dirname3 } from "node:path";
778
1269
 
779
1270
  // src/install/hash.ts
780
1271
  import { createHash } from "node:crypto";
781
- var hashText = (text8) => `sha256:${createHash("sha256").update(text8, "utf8").digest("hex")}`;
1272
+ var hashText = (text9) => `sha256:${createHash("sha256").update(text9, "utf8").digest("hex")}`;
782
1273
 
783
1274
  // src/install/layout.ts
784
1275
  var STATE_DIR = ".finitemonkey";
785
1276
  var SETTINGS_FILE = ".claude/settings.json";
786
1277
  var GITIGNORE_FILE = ".gitignore";
787
- var CONFIG_FILE = ".finitemonkey/config.yml";
1278
+ var CONFIG_FILE2 = ".finitemonkey/config.yml";
788
1279
  var LOCK_FILE = ".finitemonkey/lock.json";
789
1280
  var BACKUPS_DIR = ".finitemonkey/backups";
790
1281
  var LOCKS_DIR = ".finitemonkey/locks";
@@ -869,8 +1360,8 @@ var statusLineCommand = (value) => {
869
1360
  const command = value["command"];
870
1361
  return value["type"] === "command" && typeof command === "string" && command.trim() !== "" ? command : void 0;
871
1362
  };
872
- var statusLineIn = (text8) => {
873
- const parsed = text8 === void 0 ? void 0 : parseJsonObject(text8);
1363
+ var statusLineIn = (text9) => {
1364
+ const parsed = text9 === void 0 ? void 0 : parseJsonObject(text9);
874
1365
  return parsed?.ok === true ? parsed.value["statusLine"] : void 0;
875
1366
  };
876
1367
  var parseStatusLineAdded = (value) => {
@@ -926,139 +1417,53 @@ var parseEntry = (value) => {
926
1417
  };
927
1418
  var parseEntries = (value) => {
928
1419
  if (!Array.isArray(value)) return void 0;
929
- const entries = value.map(parseEntry);
930
- return entries.every((entry2) => entry2 !== void 0) ? entries : void 0;
931
- };
932
- var fromRecord = (record3) => {
933
- const { schema, version, preset, installedAt, updatedAt } = record3;
934
- const known2 = PRESETS.find((item2) => item2 === preset);
935
- const entries = parseEntries(record3["entries"]);
936
- const createdDirs = strings(record3["createdDirs"]);
937
- if (schema !== LOCK_SCHEMA) return reject(`schema is not ${LOCK_SCHEMA}`);
938
- if (typeof version !== "string" || typeof installedAt !== "string" || typeof updatedAt !== "string") {
939
- return reject("version, installedAt and updatedAt must be text");
940
- }
941
- if (known2 === void 0 || entries === void 0 || createdDirs === void 0) {
942
- return reject("preset, entries or createdDirs is malformed");
943
- }
944
- return accept({ schema, version, preset: known2, installedAt, updatedAt, createdDirs, entries });
945
- };
946
- var parseLock = (text8) => {
947
- const parsed = parseJsonObject(text8);
948
- return parsed.ok ? fromRecord(parsed.value) : reject(parsed.error);
949
- };
950
- var renderLock = (lock) => `${JSON.stringify(lock, null, 2)}
951
- `;
952
-
953
- // src/git/project-config.ts
954
- import { join as join2 } from "node:path";
955
- var CONFIG_FILE2 = ".finitemonkey/config.yml";
956
- var unquote = (value) => value.trim().replace(/^(["'])(.*)\1$/s, "$2");
957
- var stripComment = (line2) => {
958
- let quote;
959
- for (let index = 0; index < line2.length; index += 1) {
960
- const char = line2.charAt(index);
961
- if (quote === void 0 && (char === '"' || char === "'")) quote = char;
962
- else if (char === quote) quote = void 0;
963
- else if (quote === void 0 && char === "#" && (index === 0 || /\s/.test(line2.charAt(index - 1)))) {
964
- return line2.slice(0, index);
965
- }
966
- }
967
- return line2;
968
- };
969
- var flowList = (value) => {
970
- const trimmed = value.trim();
971
- if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
972
- return trimmed.slice(1, -1).split(",").map(unquote).filter((item2) => item2 !== "");
973
- };
974
- var keyValue = (body2) => {
975
- const match = /^("[^"]*"|'[^']*'|[^:]+?)\s*:(?:\s+(.*))?$/.exec(body2);
976
- if (match === null) return void 0;
977
- return { key: unquote(match[1] ?? ""), value: match[2] ?? "" };
978
- };
979
- var addItem = (frame, item2) => {
980
- if (frame.key === void 0) return;
981
- const parent = frame.tree[frame.key];
982
- if (Array.isArray(parent)) parent.push(item2);
983
- else frame.tree[frame.key] = [item2];
984
- };
985
- var placeLine = (stack, indent, body2) => {
986
- while (stack.length > 1 && (stack[stack.length - 1]?.indent ?? 0) >= indent && !body2.startsWith("- ")) stack.pop();
987
- const top = stack[stack.length - 1];
988
- if (top === void 0) return;
989
- if (body2.startsWith("- ")) {
990
- addItem(top, unquote(body2.slice(2)));
991
- return;
992
- }
993
- const pair = keyValue(body2);
994
- if (pair === void 0) return;
995
- const parentTree = top.key === void 0 ? top.tree : ensureTree(top.tree, top.key);
996
- if (pair.value === "") {
997
- stack.push({ indent, tree: parentTree, key: pair.key });
998
- return;
999
- }
1000
- parentTree[pair.key] = flowList(pair.value) ?? unquote(pair.value);
1001
- };
1002
- var ensureTree = (tree, key) => {
1003
- const existing = tree[key];
1004
- if (existing !== void 0 && typeof existing !== "string" && !Array.isArray(existing)) return existing;
1005
- const fresh3 = {};
1006
- tree[key] = fresh3;
1007
- return fresh3;
1008
- };
1009
- var parseConfigYaml = (text8) => {
1010
- const root = {};
1011
- const stack = [{ indent: -1, tree: root, key: void 0 }];
1012
- for (const raw of text8.split(/\r?\n/)) {
1013
- const line2 = stripComment(raw).trimEnd();
1014
- if (line2.trim() === "" || line2.trim() === "---") continue;
1015
- const indent = line2.length - line2.trimStart().length;
1016
- placeLine(stack, indent, line2.trim());
1017
- }
1018
- return root;
1019
- };
1020
- var isTree = (node) => typeof node === "object" && !Array.isArray(node);
1021
- var configAt = (tree, ...path) => path.reduce((node, key) => isTree(node) ? node[key] : void 0, tree);
1022
- var configList = (tree, ...path) => {
1023
- const node = configAt(tree, ...path);
1024
- if (typeof node === "string") return node === "" ? [] : [node];
1025
- return Array.isArray(node) ? node.filter((item2) => typeof item2 === "string") : [];
1420
+ const entries = value.map(parseEntry);
1421
+ return entries.every((entry2) => entry2 !== void 0) ? entries : void 0;
1026
1422
  };
1027
- var configMap = (tree, ...path) => {
1028
- const node = configAt(tree, ...path);
1029
- if (!isTree(node)) return {};
1030
- return Object.fromEntries(
1031
- Object.entries(node).filter((entry2) => typeof entry2[1] === "string")
1032
- );
1423
+ var fromRecord = (record3) => {
1424
+ const { schema, version, preset, installedAt, updatedAt } = record3;
1425
+ const known2 = PRESETS.find((item2) => item2 === preset);
1426
+ const entries = parseEntries(record3["entries"]);
1427
+ const createdDirs = strings(record3["createdDirs"]);
1428
+ if (schema !== LOCK_SCHEMA) return reject(`schema is not ${LOCK_SCHEMA}`);
1429
+ if (typeof version !== "string" || typeof installedAt !== "string" || typeof updatedAt !== "string") {
1430
+ return reject("version, installedAt and updatedAt must be text");
1431
+ }
1432
+ if (known2 === void 0 || entries === void 0 || createdDirs === void 0) {
1433
+ return reject("preset, entries or createdDirs is malformed");
1434
+ }
1435
+ return accept({ schema, version, preset: known2, installedAt, updatedAt, createdDirs, entries });
1033
1436
  };
1034
- var loadProjectConfig = (runtime, root) => {
1035
- const text8 = runtime.readText(toAbsolutePath(join2(root, CONFIG_FILE2)));
1036
- return text8.ok ? parseConfigYaml(text8.value) : {};
1437
+ var parseLock = (text9) => {
1438
+ const parsed = parseJsonObject(text9);
1439
+ return parsed.ok ? fromRecord(parsed.value) : reject(parsed.error);
1037
1440
  };
1441
+ var renderLock = (lock) => `${JSON.stringify(lock, null, 2)}
1442
+ `;
1038
1443
 
1039
1444
  // src/install/managed-block.ts
1040
- var lineBreak = (text8) => text8.includes("\r\n") ? "\r\n" : "\n";
1445
+ var lineBreak = (text9) => text9.includes("\r\n") ? "\r\n" : "\n";
1041
1446
  var blockRange = (lines3, markers) => {
1042
1447
  const start = lines3.findIndex((line2) => line2.trimEnd() === markers.begin);
1043
1448
  if (start < 0) return void 0;
1044
1449
  const end = lines3.findIndex((line2, index) => index > start && line2.trimEnd() === markers.end);
1045
1450
  return end < 0 ? void 0 : { start, end };
1046
1451
  };
1047
- var withManagedBlock = (text8, markers, body2) => {
1048
- const eol = text8 === void 0 ? "\n" : lineBreak(text8);
1452
+ var withManagedBlock = (text9, markers, body2) => {
1453
+ const eol = text9 === void 0 ? "\n" : lineBreak(text9);
1049
1454
  const block4 = [markers.begin, ...body2, markers.end];
1050
- if (text8 === void 0 || text8 === "") return `${block4.join(eol)}${eol}`;
1051
- const lines3 = text8.split(eol);
1455
+ if (text9 === void 0 || text9 === "") return `${block4.join(eol)}${eol}`;
1456
+ const lines3 = text9.split(eol);
1052
1457
  const range = blockRange(lines3, markers);
1053
1458
  if (range !== void 0) return [...lines3.slice(0, range.start), ...block4, ...lines3.slice(range.end + 1)].join(eol);
1054
- const separator = text8.endsWith(eol) ? "" : eol;
1055
- return `${text8}${separator}${block4.join(eol)}${eol}`;
1459
+ const separator = text9.endsWith(eol) ? "" : eol;
1460
+ return `${text9}${separator}${block4.join(eol)}${eol}`;
1056
1461
  };
1057
- var withoutManagedBlock = (text8, markers) => {
1058
- const eol = lineBreak(text8);
1059
- const lines3 = text8.split(eol);
1462
+ var withoutManagedBlock = (text9, markers) => {
1463
+ const eol = lineBreak(text9);
1464
+ const lines3 = text9.split(eol);
1060
1465
  const range = blockRange(lines3, markers);
1061
- if (range === void 0) return text8;
1466
+ if (range === void 0) return text9;
1062
1467
  return [...lines3.slice(0, range.start), ...lines3.slice(range.end + 1)].join(eol);
1063
1468
  };
1064
1469
 
@@ -1155,7 +1560,7 @@ var configTemplate = (preset, lang) => [
1155
1560
  `lang: ${lang}`,
1156
1561
  ""
1157
1562
  ].join("\n");
1158
- var hasMarker = (text8) => text8.includes(GENERATED_MARKER);
1563
+ var hasMarker = (text9) => text9.includes(GENERATED_MARKER);
1159
1564
 
1160
1565
  // src/install/skill-template.ts
1161
1566
  var cliCommand = (runner, args2) => {
@@ -1220,7 +1625,7 @@ var lefthookBody = (runner) => [
1220
1625
  ` run: ${cliCommand(runner, "push check {1}")}`,
1221
1626
  " use_stdin: true"
1222
1627
  ];
1223
- var definesHook = (text8) => GIT_HOOK_NAMES.some((hook) => new RegExp(`^${hook}:`, "m").test(withoutManagedBlock(text8, HOOK_MARKERS)));
1628
+ var definesHook = (text9) => GIT_HOOK_NAMES.some((hook) => new RegExp(`^${hook}:`, "m").test(withoutManagedBlock(text9, HOOK_MARKERS)));
1224
1629
  var lefthookItem = (project, runner, path) => {
1225
1630
  const before = project.read(path);
1226
1631
  const after = withManagedBlock(before, HOOK_MARKERS, lefthookBody(runner));
@@ -1245,8 +1650,8 @@ var preCommitBody = (runner, indent) => {
1245
1650
  ];
1246
1651
  };
1247
1652
  var TOP_KEY = /^[A-Za-z_][\w-]*:/;
1248
- var reposIsLast = (text8) => {
1249
- const keys = withoutManagedBlock(text8, HOOK_MARKERS).split(/\r?\n/).filter((line2) => TOP_KEY.test(line2));
1653
+ var reposIsLast = (text9) => {
1654
+ const keys = withoutManagedBlock(text9, HOOK_MARKERS).split(/\r?\n/).filter((line2) => TOP_KEY.test(line2));
1250
1655
  return keys.at(-1)?.startsWith("repos:") === true;
1251
1656
  };
1252
1657
  var preCommitItem = (project, runner) => {
@@ -1269,7 +1674,7 @@ var gitHookItems = (project, runner) => {
1269
1674
  };
1270
1675
 
1271
1676
  // src/policy/policy.ts
1272
- import { join as join3 } from "node:path";
1677
+ import { join as join5 } from "node:path";
1273
1678
  var POLICY_FILE = ".finitemonkey/policy.yml";
1274
1679
  var GUARD_MODES = ["off", "shadow", "active"];
1275
1680
  var LEVELS = ["light", "standard", "strict"];
@@ -1284,8 +1689,8 @@ var DEFAULT_POLICY = {
1284
1689
  guards: {}
1285
1690
  };
1286
1691
  var pick = (options, value, fallback) => options.find((option) => option === value) ?? fallback;
1287
- var parsePolicy = (text8) => {
1288
- const tree = parseConfigYaml(text8);
1692
+ var parsePolicy = (text9) => {
1693
+ const tree = parseConfigYaml(text9);
1289
1694
  const raw = configAt(tree, "promoteAfter");
1290
1695
  const promoteAfter = typeof raw === "string" ? Number.parseInt(raw, 10) : Number.NaN;
1291
1696
  const guards = Object.fromEntries(
@@ -1302,10 +1707,10 @@ var parsePolicy = (text8) => {
1302
1707
  guards
1303
1708
  };
1304
1709
  };
1305
- var policyPath = (root) => toAbsolutePath(join3(root, POLICY_FILE));
1710
+ var policyPath = (root) => toAbsolutePath(join5(root, POLICY_FILE));
1306
1711
  var loadPolicy = (runtime, root) => {
1307
- const text8 = runtime.readText(policyPath(root));
1308
- return text8.ok ? parsePolicy(text8.value) : DEFAULT_POLICY;
1712
+ const text9 = runtime.readText(policyPath(root));
1713
+ return text9.ok ? parsePolicy(text9.value) : DEFAULT_POLICY;
1309
1714
  };
1310
1715
 
1311
1716
  // src/review/agents.ts
@@ -1322,6 +1727,7 @@ var REVIEWER_BODY = [
1322
1727
  "3. Write numbered findings in three groups: must fix, should fix, nice to have. For each: what it is, the problem, the fix, and what happens if it is not fixed.",
1323
1728
  "4. Every must-fix finding carries a reproduction command and the expected result. A finding without one goes under should fix.",
1324
1729
  "5. End with a verdict: approve, approve with changes, or request changes.",
1730
+ '6. Close with the same findings as one JSON block for `finitemonkey critique verify`: `{"verdict": "approve|approve-with-changes|request-changes", "findings": [{"group": "must|should|nice", "what", "problem", "fix", "ifNotFixed", "repro": {"command", "expect"}, "activeRisk": true|false}]}`.',
1325
1731
  "",
1326
1732
  "You cannot edit files or start other agents. Report; do not repair."
1327
1733
  ].join("\n");
@@ -1330,9 +1736,9 @@ var VERIFIER_BODY = [
1330
1736
  "",
1331
1737
  "You check claims with commands, not with opinions. Your input is the review packet and, when there is one, the reviewer's must-fix findings with their reproduction commands. You never see the conversation that produced the change.",
1332
1738
  "",
1333
- "1. Run each reproduction command in a throwaway worktree on the base commit and on the head commit (`git worktree add <tmp> <rev>`), never in the person's working tree.",
1334
- "2. Record both exit codes. Fails only on head: the finding stands. Fails on both: it was already there. Fails on neither: not reproduced.",
1335
- "3. Report one line per finding with the command and both results, then remove the worktrees.",
1739
+ "1. Pipe the reviewer's JSON block into `finitemonkey critique verify --base <base>` on stdin. It runs each reproduction command in throwaway worktrees on the base and head commits, never in the person's working tree, and records both exit codes in `.finitemonkey/review/ledger.jsonl`.",
1740
+ "2. Fails only on head: the finding stands. Fails on both: it was already there. Fails on neither: not reproduced.",
1741
+ "3. Report the table it prints, unchanged. Do not rerun a command by hand to get a different answer.",
1336
1742
  "",
1337
1743
  "You cannot edit files or start other agents. Report; do not repair."
1338
1744
  ].join("\n");
@@ -1370,16 +1776,16 @@ var agentMarkdown = (spec2, marker) => [
1370
1776
  // src/install/agents-block.ts
1371
1777
  var MARKDOWN_MARKERS = { begin: MD_BLOCK_BEGIN, end: MD_BLOCK_END };
1372
1778
  var IMPORT_LINE = /^@AGENTS\.md[ \t]*$/m;
1373
- var withAgentsBlock = (text8, runner, lang) => withManagedBlock(
1374
- text8,
1779
+ var withAgentsBlock = (text9, runner, lang) => withManagedBlock(
1780
+ text9,
1375
1781
  MARKDOWN_MARKERS,
1376
1782
  TEMPLATES[lang].agentsBlock({
1377
1783
  gate: cliCommand(runner, "gate --fast --json"),
1378
1784
  doctor: cliCommand(runner, "doctor --json")
1379
1785
  })
1380
1786
  );
1381
- var withAgentsImport = (text8) => text8 !== void 0 && IMPORT_LINE.test(withoutManagedBlock(text8, MARKDOWN_MARKERS)) ? text8 : withManagedBlock(text8, MARKDOWN_MARKERS, [AGENTS_IMPORT]);
1382
- var withoutMarkdownBlock = (text8) => withoutManagedBlock(text8, MARKDOWN_MARKERS);
1787
+ var withAgentsImport = (text9) => text9 !== void 0 && IMPORT_LINE.test(withoutManagedBlock(text9, MARKDOWN_MARKERS)) ? text9 : withManagedBlock(text9, MARKDOWN_MARKERS, [AGENTS_IMPORT]);
1788
+ var withoutMarkdownBlock = (text9) => withoutManagedBlock(text9, MARKDOWN_MARKERS);
1383
1789
 
1384
1790
  // src/install/ask-floor.ts
1385
1791
  var DEFAULT_BRANCHES = ["main", "master"];
@@ -1455,7 +1861,7 @@ var ruleFor = (raw) => {
1455
1861
  };
1456
1862
  var claudeignoreRules = (texts2) => [
1457
1863
  ...new Set(
1458
- texts2.flatMap((text8) => (text8 ?? "").split("\n")).map(ruleFor).filter((rule) => rule !== void 0)
1864
+ texts2.flatMap((text9) => (text9 ?? "").split("\n")).map(ruleFor).filter((rule) => rule !== void 0)
1459
1865
  )
1460
1866
  ];
1461
1867
 
@@ -1484,16 +1890,16 @@ var DENY_FLOOR = {
1484
1890
  var DENY_RULES = [...DENY_FLOOR.secrets, ...DENY_FLOOR.git, ...DENY_FLOOR.proof];
1485
1891
 
1486
1892
  // src/install/detect.ts
1487
- import { join as join4 } from "node:path";
1893
+ import { join as join6 } from "node:path";
1488
1894
 
1489
1895
  // src/install/json-text.ts
1490
1896
  var DEFAULT_STYLE = { indent: " ", finalNewline: true };
1491
- var styleOf = (text8) => {
1492
- const indented = /\n([ \t]+)\S/.exec(text8);
1493
- return { indent: indented?.[1] ?? DEFAULT_STYLE.indent, finalNewline: text8.endsWith("\n") };
1897
+ var styleOf = (text9) => {
1898
+ const indented = /\n([ \t]+)\S/.exec(text9);
1899
+ return { indent: indented?.[1] ?? DEFAULT_STYLE.indent, finalNewline: text9.endsWith("\n") };
1494
1900
  };
1495
1901
  var renderJson = (value, style) => `${JSON.stringify(value, null, style.indent)}${style.finalNewline ? "\n" : ""}`;
1496
- var parseSettings = (text8) => text8 === void 0 || text8.trim() === "" ? { ok: true, value: {} } : parseJsonObject(text8);
1902
+ var parseSettings = (text9) => text9 === void 0 || text9.trim() === "" ? { ok: true, value: {} } : parseJsonObject(text9);
1497
1903
  var asList = (value) => Array.isArray(value) ? value : [];
1498
1904
  var asRecord = (value) => isRecord(value) ? value : {};
1499
1905
  var setKey = (record3, key, value) => {
@@ -1530,8 +1936,8 @@ var WORKFLOW_DIRS = [
1530
1936
  { dir: "openspec", name: "openspec" }
1531
1937
  ];
1532
1938
  var fromPackageJson = (project) => {
1533
- const text8 = project.read("package.json");
1534
- const parsed = text8 === void 0 ? void 0 : parseJsonObject(text8);
1939
+ const text9 = project.read("package.json");
1940
+ const parsed = text9 === void 0 ? void 0 : parseJsonObject(text9);
1535
1941
  const field = parsed?.ok === true ? parsed.value["packageManager"] : void 0;
1536
1942
  if (typeof field !== "string") return void 0;
1537
1943
  return PACKAGE_MANAGERS.find(({ name }) => field.startsWith(`${name}@`))?.name;
@@ -1547,9 +1953,9 @@ var allHandlers = (settings) => {
1547
1953
  const hooks = settings["hooks"];
1548
1954
  return isRecord(hooks) ? Object.keys(hooks).flatMap((event) => handlersOf(settings, event)) : [];
1549
1955
  };
1550
- var settingsInfo = (text8) => {
1551
- if (text8 === void 0) return { exists: false, valid: true, userHooks: 0, ownHooks: 0 };
1552
- const parsed = text8.trim() === "" ? { ok: true, value: {} } : parseJsonObject(text8);
1956
+ var settingsInfo = (text9) => {
1957
+ if (text9 === void 0) return { exists: false, valid: true, userHooks: 0, ownHooks: 0 };
1958
+ const parsed = text9.trim() === "" ? { ok: true, value: {} } : parseJsonObject(text9);
1553
1959
  if (!parsed.ok) return { exists: true, valid: false, userHooks: 0, ownHooks: 0 };
1554
1960
  const handlers = allHandlers(parsed.value);
1555
1961
  const own = handlers.filter(isOwnHandler).length;
@@ -1563,8 +1969,8 @@ var pluginAuthorities = (settings, source) => {
1563
1969
  return LOOP_PLUGINS.includes(name) ? [{ kind: "loop-plugin", source, name }] : [];
1564
1970
  });
1565
1971
  };
1566
- var scopeAuthorities = (text8, source) => {
1567
- const parsed = text8 === void 0 ? void 0 : parseJsonObject(text8);
1972
+ var scopeAuthorities = (text9, source) => {
1973
+ const parsed = text9 === void 0 ? void 0 : parseJsonObject(text9);
1568
1974
  if (parsed?.ok !== true) return [];
1569
1975
  const stops = ["Stop", "SubagentStop"].flatMap(
1570
1976
  (event) => handlersOf(parsed.value, event).filter((handler) => !isOwnHandler(handler)).map((handler) => {
@@ -1577,9 +1983,9 @@ var scopeAuthorities = (text8, source) => {
1577
1983
  var userSettingsText = (project, env) => {
1578
1984
  const home = env["CLAUDE_CONFIG_DIR"] ?? env["HOME"] ?? env["USERPROFILE"];
1579
1985
  if (home === void 0) return void 0;
1580
- const file = env["CLAUDE_CONFIG_DIR"] === void 0 ? join4(home, ".claude", "settings.json") : join4(home, "settings.json");
1581
- const text8 = project.runtime.readText(toAbsolutePath(file));
1582
- return text8.ok ? text8.value : void 0;
1986
+ const file = env["CLAUDE_CONFIG_DIR"] === void 0 ? join6(home, ".claude", "settings.json") : join6(home, "settings.json");
1987
+ const text9 = project.runtime.readText(toAbsolutePath(file));
1988
+ return text9.ok ? text9.value : void 0;
1583
1989
  };
1584
1990
  var detectAuthorities = (project, env) => [
1585
1991
  ...scopeAuthorities(project.read(SETTINGS_FILE), "project"),
@@ -1599,8 +2005,8 @@ var chooseRunner = (project, bin) => {
1599
2005
  // src/install/gitignore-block.ts
1600
2006
  var MARKERS = { begin: BLOCK_BEGIN, end: BLOCK_END };
1601
2007
  var body = () => [`${STATE_DIR}/*`, ...COMMITTED_STATE.map((path) => `!${STATE_DIR}/${path}`)];
1602
- var withBlock = (text8) => withManagedBlock(text8, MARKERS, body());
1603
- var withoutBlock = (text8) => withoutManagedBlock(text8, MARKERS);
2008
+ var withBlock = (text9) => withManagedBlock(text9, MARKERS, body());
2009
+ var withoutBlock = (text9) => withoutManagedBlock(text9, MARKERS);
1604
2010
 
1605
2011
  // src/core/semver.ts
1606
2012
  var SEMVER = /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
@@ -1643,7 +2049,7 @@ var pendingMigrations = (migrations, from, to) => {
1643
2049
  };
1644
2050
  var migrateItem = (item2, previous, steps) => {
1645
2051
  if (steps.length === 0 || item2.before === void 0) return item2;
1646
- const migrated = steps.reduce((text8, step3) => step3(text8), item2.after);
2052
+ const migrated = steps.reduce((text9, step3) => step3(text9), item2.after);
1647
2053
  if (migrated === item2.after) return item2;
1648
2054
  const recorded = previous.entries.find((entry2) => entry2.path === item2.path)?.hash;
1649
2055
  if (recorded !== hashText(item2.before)) {
@@ -1809,9 +2215,9 @@ var LOCAL_SETTINGS_FILE = ".claude/settings.local.json";
1809
2215
  var existingStatusLine = (project, projectSettings, env) => statusLineCommand(projectSettings["statusLine"]) ?? statusLineCommand(statusLineIn(project.read(LOCAL_SETTINGS_FILE))) ?? statusLineCommand(statusLineIn(userSettingsText(project, env)));
1810
2216
 
1811
2217
  // src/install/vendor.ts
1812
- var isBundle = (text8) => text8.startsWith("#!/usr/bin/env node") && text8.includes("finitemonkey");
1813
- var isOwnPackage = (text8) => {
1814
- const parsed = parseJsonObject(text8);
2218
+ var isBundle = (text9) => text9.startsWith("#!/usr/bin/env node") && text9.includes("finitemonkey");
2219
+ var isOwnPackage = (text9) => {
2220
+ const parsed = parseJsonObject(text9);
1815
2221
  return parsed.ok && parsed.value["name"] === "finitemonkey";
1816
2222
  };
1817
2223
  var vendoredPackage = (version) => `${JSON.stringify({ name: "finitemonkey", version, private: true, type: "module" }, null, 2)}
@@ -1842,9 +2248,9 @@ var detect = (project, gitRoot2, input) => ({
1842
2248
  location: locationOf(project.root, input.env)
1843
2249
  });
1844
2250
  var readPreviousLock = (project) => {
1845
- const text8 = project.read(LOCK_FILE);
1846
- if (text8 === void 0) return accept(void 0);
1847
- const parsed = parseLock(text8);
2251
+ const text9 = project.read(LOCK_FILE);
2252
+ if (text9 === void 0) return accept(void 0);
2253
+ const parsed = parseLock(text9);
1848
2254
  return parsed.ok ? accept(parsed.value) : reject({ kind: "lock", detail: parsed.error });
1849
2255
  };
1850
2256
  var actionFor = (before, after, existing) => {
@@ -1893,7 +2299,7 @@ var settingsItem = (project, detection, input, previous) => {
1893
2299
  });
1894
2300
  };
1895
2301
  var markdownItems = (project, runner, lang) => [
1896
- { path: AGENTS_FILE, render: (text8) => withAgentsBlock(text8, runner, lang) },
2302
+ { path: AGENTS_FILE, render: (text9) => withAgentsBlock(text9, runner, lang) },
1897
2303
  { path: CLAUDE_FILE, render: withAgentsImport }
1898
2304
  ].map(({ path, render: render3 }) => {
1899
2305
  const before = project.read(path);
@@ -1920,14 +2326,14 @@ var generatedItem = (project, file, previous) => {
1920
2326
  };
1921
2327
  var PLAYWRIGHT_VERIFIER = ".claude/agents/verifier.md";
1922
2328
  var playwrightVerifier = (project) => {
1923
- const wanted = configAt(parseConfigYaml(project.read(CONFIG_FILE) ?? ""), "review", "playwright") === "true";
2329
+ const wanted = configAt(parseConfigYaml(project.read(CONFIG_FILE2) ?? ""), "review", "playwright") === "true";
1924
2330
  if (!wanted) return [];
1925
2331
  const template = agentMarkdown(withPlaywright(VERIFIER), GENERATED_MARKER);
1926
- return [{ path: PLAYWRIGHT_VERIFIER, template, isOurs: (text8) => text8.includes(GENERATED_MARKER) }];
2332
+ return [{ path: PLAYWRIGHT_VERIFIER, template, isOurs: (text9) => text9.includes(GENERATED_MARKER) }];
1927
2333
  };
1928
2334
  var generatedFiles = (project, input, runner, vendor) => {
1929
2335
  const own = [
1930
- { path: CONFIG_FILE, template: configTemplate(input.preset, input.lang), isOurs: hasMarker },
2336
+ { path: CONFIG_FILE2, template: configTemplate(input.preset, input.lang), isOurs: hasMarker },
1931
2337
  { path: VERIFY_SKILL, template: verifySkillTemplate(runner, input.lang), isOurs: isOwnSkill },
1932
2338
  { path: POLICY_FILE, template: policyTemplate(input.lang), isOurs: hasMarker },
1933
2339
  ...playwrightVerifier(project)
@@ -1968,9 +2374,9 @@ var needsWrite = (plan) => hasChanges(plan) || plan.previous === void 0;
1968
2374
  // src/install/apply.ts
1969
2375
  var stamp = (iso) => iso.replace(/[:.]/g, "-");
1970
2376
  var ensureParent = (project, relative11) => project.runtime.mkdir(project.abs(dirname3(relative11)));
1971
- var writeText = (project, relative11, text8) => {
2377
+ var writeText = (project, relative11, text9) => {
1972
2378
  const parent = ensureParent(project, relative11);
1973
- const written = parent.ok ? project.runtime.writeAtomic(project.abs(relative11), text8) : parent;
2379
+ const written = parent.ok ? project.runtime.writeAtomic(project.abs(relative11), text9) : parent;
1974
2380
  return written.ok ? accept(void 0) : reject({ path: relative11, detail: written.error });
1975
2381
  };
1976
2382
  var restore = (project, items) => {
@@ -1988,7 +2394,7 @@ var backupItems = (project, dir, items) => {
1988
2394
  return writeText(project, `${dir}/manifest.json`, `${JSON.stringify(manifest, null, 2)}
1989
2395
  `);
1990
2396
  };
1991
- var entryOf = (plan, item2, backupDir) => {
2397
+ var entryOf2 = (plan, item2, backupDir) => {
1992
2398
  const previous = plan.previous?.entries.find((entry2) => entry2.path === item2.path);
1993
2399
  const existed = previous?.existed ?? item2.before !== void 0;
1994
2400
  const backup = previous?.backup ?? (existed && backupDir !== void 0 ? `${backupDir}/${item2.path}` : void 0);
@@ -2011,7 +2417,7 @@ var nextLock = (plan, version, now, backupDir, createdDirs, only) => ({
2011
2417
  updatedAt: now,
2012
2418
  createdDirs: [.../* @__PURE__ */ new Set([...plan.previous?.createdDirs ?? [], ...createdDirs])],
2013
2419
  entries: plan.items.filter((item2) => item2.action !== "skip-foreign").flatMap((item2) => {
2014
- if (only === void 0 || only.includes(item2.path)) return [entryOf(plan, item2, backupDir)];
2420
+ if (only === void 0 || only.includes(item2.path)) return [entryOf2(plan, item2, backupDir)];
2015
2421
  const kept = plan.previous?.entries.find((entry2) => entry2.path === item2.path);
2016
2422
  return kept === void 0 ? [] : [kept];
2017
2423
  })
@@ -2110,16 +2516,16 @@ var detectLang = (flag2, env, settings = {}) => {
2110
2516
  };
2111
2517
 
2112
2518
  // src/install/project.ts
2113
- import { dirname as dirname4, join as join5 } from "node:path";
2519
+ import { dirname as dirname4, join as join7 } from "node:path";
2114
2520
  var projectAt = (root, runtime) => {
2115
- const abs = (relative11) => toAbsolutePath(join5(root, ...relative11.split("/")));
2521
+ const abs = (relative11) => toAbsolutePath(join7(root, ...relative11.split("/")));
2116
2522
  return {
2117
2523
  root,
2118
2524
  runtime,
2119
2525
  abs,
2120
2526
  read: (relative11) => {
2121
- const text8 = runtime.readText(abs(relative11));
2122
- return text8.ok ? text8.value : void 0;
2527
+ const text9 = runtime.readText(abs(relative11));
2528
+ return text9.ok ? text9.value : void 0;
2123
2529
  },
2124
2530
  exists: (relative11) => runtime.stat(abs(relative11)) !== void 0,
2125
2531
  isDir: (relative11) => runtime.stat(abs(relative11))?.kind === "dir"
@@ -2128,7 +2534,7 @@ var projectAt = (root, runtime) => {
2128
2534
  var findGitRoot = (start, runtime) => {
2129
2535
  let current2 = start;
2130
2536
  for (; ; ) {
2131
- if (runtime.stat(toAbsolutePath(join5(current2, ".git"))) !== void 0) return toAbsolutePath(current2);
2537
+ if (runtime.stat(toAbsolutePath(join7(current2, ".git"))) !== void 0) return toAbsolutePath(current2);
2132
2538
  const parent = dirname4(current2);
2133
2539
  if (parent === current2) return void 0;
2134
2540
  current2 = parent;
@@ -2192,8 +2598,8 @@ var invalidPreset = (raw) => problem("invalid_arguments", "error.invalid_preset"
2192
2598
  });
2193
2599
  var selfBundle = (ctx) => {
2194
2600
  if (!isBundlePath(ctx.runtime.selfPath)) return void 0;
2195
- const text8 = ctx.runtime.readText(ctx.runtime.selfPath);
2196
- return text8.ok ? text8.value : void 0;
2601
+ const text9 = ctx.runtime.readText(ctx.runtime.selfPath);
2602
+ return text9.ok ? text9.value : void 0;
2197
2603
  };
2198
2604
  var INHERITED = /* @__PURE__ */ new Set(["default", "config", "claude-settings"]);
2199
2605
  var planInputFor = (ctx, preset, bin, marketplace) => ({
@@ -2229,7 +2635,7 @@ var applyFixes = (ctx, write3) => {
2229
2635
  };
2230
2636
 
2231
2637
  // src/doctor/report-file.ts
2232
- import { join as join7 } from "node:path";
2638
+ import { join as join9 } from "node:path";
2233
2639
 
2234
2640
  // src/evidence/log.ts
2235
2641
  var logEvent = (runtime, paths, event, data) => runtime.appendText(paths.log, `${JSON.stringify({ at: runtime.now(), event, ...data })}
@@ -2297,13 +2703,13 @@ var isPlaceholder = (value) => {
2297
2703
  var isTestOrLockPath = (path) => path !== void 0 && (LOCKFILES.test(toPosixPath(path)) || TEST_PATH.test(toPosixPath(path)));
2298
2704
 
2299
2705
  // src/secrets/entropy.ts
2300
- var shannonEntropy = (text8) => {
2301
- if (text8.length === 0) return 0;
2706
+ var shannonEntropy = (text9) => {
2707
+ if (text9.length === 0) return 0;
2302
2708
  const counts = /* @__PURE__ */ new Map();
2303
- for (const char of text8) counts.set(char, (counts.get(char) ?? 0) + 1);
2709
+ for (const char of text9) counts.set(char, (counts.get(char) ?? 0) + 1);
2304
2710
  let entropy = 0;
2305
2711
  for (const count4 of counts.values()) {
2306
- const p = count4 / text8.length;
2712
+ const p = count4 / text9.length;
2307
2713
  entropy -= p * Math.log2(p);
2308
2714
  }
2309
2715
  return entropy;
@@ -2311,7 +2717,7 @@ var shannonEntropy = (text8) => {
2311
2717
 
2312
2718
  // src/secrets/fingerprint.ts
2313
2719
  import { createHash as createHash2 } from "node:crypto";
2314
- var sha256Hex = (text8) => createHash2("sha256").update(text8).digest("hex");
2720
+ var sha256Hex = (text9) => createHash2("sha256").update(text9).digest("hex");
2315
2721
  var fingerprintOf = (secret) => sha256Hex(`finitemonkey:secret:${secret}`).slice(0, 16);
2316
2722
  var ALNUM = /[A-Za-z0-9]/g;
2317
2723
  var MIN_TAIL_LENGTH = 12;
@@ -2321,9 +2727,9 @@ var tailOf = (secret) => {
2321
2727
  };
2322
2728
 
2323
2729
  // src/secrets/lines.ts
2324
- var lineLocator = (text8) => {
2730
+ var lineLocator = (text9) => {
2325
2731
  const starts = [0];
2326
- for (let index = text8.indexOf("\n"); index !== -1; index = text8.indexOf("\n", index + 1)) starts.push(index + 1);
2732
+ for (let index = text9.indexOf("\n"); index !== -1; index = text9.indexOf("\n", index + 1)) starts.push(index + 1);
2327
2733
  return (offset) => {
2328
2734
  let low = 0;
2329
2735
  let high = starts.length - 1;
@@ -2617,12 +3023,12 @@ var hasRandomRun = (secret, length) => (secret.match(new RegExp(`[A-Za-z0-9]{${S
2617
3023
  );
2618
3024
  var accepted = (rule, secret) => secret.length >= rule.minLength && (!rule.requireDigit || DIGIT.test(secret)) && (rule.minRandomRun === 0 || hasRandomRun(secret, rule.minRandomRun)) && (!rule.placeholders || !isPlaceholder(secret)) && (rule.minEntropy === 0 || shannonEntropy(secret) >= rule.minEntropy);
2619
3025
  var NAME_CHAR = /[A-Za-z0-9_.-]/;
2620
- var nameAround = (text8, at) => {
3026
+ var nameAround = (text9, at) => {
2621
3027
  let start = at;
2622
- while (start > 0 && NAME_CHAR.test(text8.charAt(start - 1))) start -= 1;
3028
+ while (start > 0 && NAME_CHAR.test(text9.charAt(start - 1))) start -= 1;
2623
3029
  let end = at;
2624
- while (end < text8.length && NAME_CHAR.test(text8.charAt(end))) end += 1;
2625
- return text8.slice(start, end).toLowerCase();
3030
+ while (end < text9.length && NAME_CHAR.test(text9.charAt(end))) end += 1;
3031
+ return text9.slice(start, end).toLowerCase();
2626
3032
  };
2627
3033
  var ignoredName = (rule, match) => rule.ignoredNames.length > 0 && rule.ignoredNames.includes(nameAround(match.input, match.index));
2628
3034
  var spanOf = (rule, match) => {
@@ -2632,9 +3038,9 @@ var spanOf = (rule, match) => {
2632
3038
  const secret = match.input.slice(start, end);
2633
3039
  return accepted(rule, secret) ? { rule, start, end, secret } : void 0;
2634
3040
  };
2635
- var ruleSpans = (rule, text8) => {
3041
+ var ruleSpans = (rule, text9) => {
2636
3042
  const spans = [];
2637
- for (const match of text8.matchAll(rule.pattern)) {
3043
+ for (const match of text9.matchAll(rule.pattern)) {
2638
3044
  const span = spanOf(rule, match);
2639
3045
  if (span !== void 0) spans.push(span);
2640
3046
  }
@@ -2660,31 +3066,31 @@ var toFinding = (span, locate2) => {
2660
3066
  tail: tailOf(span.secret)
2661
3067
  };
2662
3068
  };
2663
- var scanText = (text8, options = {}) => {
2664
- if (text8.length === 0) return [];
2665
- const rules = applicable(options.rules ?? RULES, text8.toLowerCase(), options.path);
3069
+ var scanText = (text9, options = {}) => {
3070
+ if (text9.length === 0) return [];
3071
+ const rules = applicable(options.rules ?? RULES, text9.toLowerCase(), options.path);
2666
3072
  if (rules.length === 0) return [];
2667
3073
  const taken = [];
2668
3074
  for (const rule of rules) {
2669
- for (const span of ruleSpans(rule, text8)) if (!overlaps(taken, span)) taken.push(span);
3075
+ for (const span of ruleSpans(rule, text9)) if (!overlaps(taken, span)) taken.push(span);
2670
3076
  }
2671
3077
  if (taken.length === 0) return [];
2672
- const locate2 = lineLocator(text8);
3078
+ const locate2 = lineLocator(text9);
2673
3079
  return taken.sort((a, b) => a.start - b.start).map((span) => toFinding(span, locate2));
2674
3080
  };
2675
3081
 
2676
3082
  // src/mask/mask.ts
2677
3083
  var maskToken = (finding4) => `«secret:${finding4.ruleId}:${finding4.tail}»`;
2678
- var maskText = (text8) => {
2679
- const findings = scanText(text8, { rules: CREDENTIAL_RULES });
2680
- if (findings.length === 0) return text8;
3084
+ var maskText = (text9) => {
3085
+ const findings = scanText(text9, { rules: CREDENTIAL_RULES });
3086
+ if (findings.length === 0) return text9;
2681
3087
  let out = "";
2682
3088
  let cursor = 0;
2683
3089
  for (const finding4 of findings) {
2684
- out += text8.slice(cursor, finding4.start) + maskToken(finding4);
3090
+ out += text9.slice(cursor, finding4.start) + maskToken(finding4);
2685
3091
  cursor = finding4.end;
2686
3092
  }
2687
- return out + text8.slice(cursor);
3093
+ return out + text9.slice(cursor);
2688
3094
  };
2689
3095
  var maskDeep = (value) => {
2690
3096
  if (typeof value === "string") return maskText(value);
@@ -2696,11 +3102,11 @@ var maskDeep = (value) => {
2696
3102
  };
2697
3103
 
2698
3104
  // src/task/paths.ts
2699
- import { join as join6 } from "node:path";
3105
+ import { join as join8 } from "node:path";
2700
3106
  var FM_DIR = ".finitemonkey";
2701
3107
  var fmPaths = (root) => {
2702
- const dir = toAbsolutePath(join6(root, FM_DIR));
2703
- const at = (...parts) => toAbsolutePath(join6(dir, ...parts));
3108
+ const dir = toAbsolutePath(join8(root, FM_DIR));
3109
+ const at = (...parts) => toAbsolutePath(join8(dir, ...parts));
2704
3110
  return {
2705
3111
  root,
2706
3112
  dir,
@@ -2902,14 +3308,14 @@ var toolVersion = (env, command) => {
2902
3308
  return result.ok ? result.stdout.trim().split("\n")[0] ?? "" : "not found";
2903
3309
  };
2904
3310
  var lastHookFailures = (env) => {
2905
- const text8 = env.runtime.readText(fmPaths(env.root).log);
2906
- if (!text8.ok) return [];
2907
- return text8.value.split("\n").filter((line2) => {
3311
+ const text9 = env.runtime.readText(fmPaths(env.root).log);
3312
+ if (!text9.ok) return [];
3313
+ return text9.value.split("\n").filter((line2) => {
2908
3314
  const parsed = line2.trim() === "" ? void 0 : parseJsonObject(line2);
2909
3315
  return parsed?.ok === true && isRecord(parsed.value) && parsed.value["event"] === HOOK_FAILURE_EVENT;
2910
3316
  }).slice(-MAX_FAILURES);
2911
3317
  };
2912
- var withoutHome = (text8, home) => home.length > 1 ? text8.split(home).join("~") : text8;
3318
+ var withoutHome = (text9, home) => home.length > 1 ? text9.split(home).join("~") : text9;
2913
3319
  var reportMarkdown = (env, ctx, envelope) => {
2914
3320
  const failures = lastHookFailures(env);
2915
3321
  const lines3 = [
@@ -2936,18 +3342,18 @@ var reportMarkdown = (env, ctx, envelope) => {
2936
3342
  ];
2937
3343
  return maskText(withoutHome(lines3.join("\n"), env.runtime.homeDir()));
2938
3344
  };
2939
- var reportPath = (env) => toAbsolutePath(join7(env.root, ...REPORTS_DIR.split("/"), `doctor-${stamp(env.runtime.now())}.md`));
3345
+ var reportPath = (env) => toAbsolutePath(join9(env.root, ...REPORTS_DIR.split("/"), `doctor-${stamp(env.runtime.now())}.md`));
2940
3346
 
2941
3347
  // src/install/plugin-yield.ts
2942
- import { join as join8 } from "node:path";
3348
+ import { join as join10 } from "node:path";
2943
3349
  var PLUGIN_VIA = "plugin";
2944
3350
  var YIELD_STDOUT = `${JSON.stringify({ continue: true })}
2945
3351
  `;
2946
3352
  var projectOwnsHooks = (ctx) => {
2947
3353
  const root = ctx.env["CLAUDE_PROJECT_DIR"] ?? ctx.cwd;
2948
- const text8 = ctx.runtime.readText(toAbsolutePath(join8(root, ...SETTINGS_FILE.split("/"))));
2949
- if (!text8.ok) return false;
2950
- const parsed = parseJsonObject(text8.value);
3354
+ const text9 = ctx.runtime.readText(toAbsolutePath(join10(root, ...SETTINGS_FILE.split("/"))));
3355
+ if (!text9.ok) return false;
3356
+ const parsed = parseJsonObject(text9.value);
2951
3357
  return parsed.ok && ownHandlersIn(parsed.value) > 0;
2952
3358
  };
2953
3359
 
@@ -2959,7 +3365,7 @@ var STABLE_PLUGIN_ID = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
2959
3365
  var NEXT_PLUGIN_ID = `${PLUGIN_NAME}@${NEXT_MARKETPLACE_NAME}`;
2960
3366
 
2961
3367
  // src/doctor/settings.ts
2962
- import { join as join9 } from "node:path";
3368
+ import { join as join11 } from "node:path";
2963
3369
  var MANAGED = {
2964
3370
  darwin: "/Library/Application Support/ClaudeCode/managed-settings.json",
2965
3371
  linux: "/etc/claude-code/managed-settings.json",
@@ -2968,7 +3374,7 @@ var MANAGED = {
2968
3374
  var userSettingsPath = ({ env, runtime }) => {
2969
3375
  const configDir = env["CLAUDE_CONFIG_DIR"];
2970
3376
  return toAbsolutePath(
2971
- configDir === void 0 ? join9(runtime.homeDir(), ".claude", "settings.json") : join9(configDir, "settings.json")
3377
+ configDir === void 0 ? join11(runtime.homeDir(), ".claude", "settings.json") : join11(configDir, "settings.json")
2972
3378
  );
2973
3379
  };
2974
3380
  var settingsPaths = (env) => {
@@ -2976,13 +3382,13 @@ var settingsPaths = (env) => {
2976
3382
  return [
2977
3383
  ...managed === void 0 ? [] : [{ scope: "managed", path: toAbsolutePath(managed) }],
2978
3384
  { scope: "user", path: userSettingsPath(env) },
2979
- { scope: "project", path: toAbsolutePath(join9(env.root, ".claude", "settings.json")) },
2980
- { scope: "local", path: toAbsolutePath(join9(env.root, ".claude", "settings.local.json")) }
3385
+ { scope: "project", path: toAbsolutePath(join11(env.root, ".claude", "settings.json")) },
3386
+ { scope: "local", path: toAbsolutePath(join11(env.root, ".claude", "settings.local.json")) }
2981
3387
  ];
2982
3388
  };
2983
3389
  var readSettings = (env) => settingsPaths(env).flatMap(({ scope: scope2, path }) => {
2984
- const text8 = env.runtime.readText(path);
2985
- const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
3390
+ const text9 = env.runtime.readText(path);
3391
+ const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
2986
3392
  return parsed?.ok === true ? [{ scope: scope2, path, settings: parsed.value }] : [];
2987
3393
  });
2988
3394
  var scopesWith = (all, test) => all.filter(({ settings }) => test(settings)).map(({ scope: scope2 }) => scope2);
@@ -3121,7 +3527,7 @@ var checkClaudeSettings = (env) => {
3121
3527
  };
3122
3528
 
3123
3529
  // src/doctor/environment.ts
3124
- import { join as join10 } from "node:path";
3530
+ import { join as join12 } from "node:path";
3125
3531
  var AUTH_VARIABLES = [
3126
3532
  "ANTHROPIC_API_KEY",
3127
3533
  "ANTHROPIC_AUTH_TOKEN",
@@ -3159,7 +3565,7 @@ var pathDirs = ({ env, platform }) => {
3159
3565
  var copiesOnPath = (env) => {
3160
3566
  const names = EXECUTABLES[env.platform] ?? ["finitemonkey"];
3161
3567
  const found = pathDirs(env).flatMap(
3162
- (dir) => names.map((name) => toAbsolutePath(join10(dir, name))).filter((path) => env.runtime.stat(path)?.kind === "file").map((path) => env.runtime.realpath(path))
3568
+ (dir) => names.map((name) => toAbsolutePath(join12(dir, name))).filter((path) => env.runtime.stat(path)?.kind === "file").map((path) => env.runtime.realpath(path))
3163
3569
  );
3164
3570
  const byDir = new Map(found.map((path) => [path.replace(/\.(cmd|exe|ps1)$/i, ""), path]));
3165
3571
  return [...byDir.values()];
@@ -3193,7 +3599,7 @@ var checkAuthVariables = ({ env, platform }) => {
3193
3599
  };
3194
3600
 
3195
3601
  // src/doctor/hooks.ts
3196
- import { join as join34 } from "node:path";
3602
+ import { join as join36 } from "node:path";
3197
3603
 
3198
3604
  // src/hooks/events.ts
3199
3605
  var HOOK_EVENTS = [
@@ -3240,14 +3646,14 @@ var deadlineFor = (event, raw) => {
3240
3646
  };
3241
3647
 
3242
3648
  // src/gate/hooks/common.ts
3243
- import { dirname as dirname5, join as join11 } from "node:path";
3649
+ import { dirname as dirname5, join as join13 } from "node:path";
3244
3650
  var DEFAULT_GATE_BUDGET_MS = 3e5;
3245
3651
  var sessionCwd = (input) => input.payload.cwd === void 0 ? input.cwd : toAbsolutePath(input.payload.cwd, input.cwd);
3246
3652
  var MAX_DEPTH = 64;
3247
3653
  var findRepoRoot = (runtime, start) => {
3248
3654
  let current2 = start;
3249
3655
  for (let depth = 0; depth < MAX_DEPTH; depth += 1) {
3250
- if (runtime.stat(toAbsolutePath(join11(current2, ".git"))) !== void 0) return current2;
3656
+ if (runtime.stat(toAbsolutePath(join13(current2, ".git"))) !== void 0) return current2;
3251
3657
  const parent = toAbsolutePath(dirname5(current2));
3252
3658
  if (parent === current2) return void 0;
3253
3659
  current2 = parent;
@@ -3267,7 +3673,7 @@ var contextOutput = (event, additionalContext, systemMessage) => ({
3267
3673
  hookSpecificOutput: { hookEventName: event, additionalContext },
3268
3674
  ...systemMessage === void 0 ? {} : { systemMessage }
3269
3675
  });
3270
- var human = (text8) => `finitemonkey: ${text8}`;
3676
+ var human = (text9) => `finitemonkey: ${text9}`;
3271
3677
 
3272
3678
  // src/hooks/fields.ts
3273
3679
  var textField = (record3, ...names) => {
@@ -3298,7 +3704,7 @@ var editsOf = (input) => {
3298
3704
  });
3299
3705
  };
3300
3706
  var applyEdits = (before, edits) => edits.reduce(
3301
- (text8, edit) => edit.all ? text8.split(edit.oldText).join(edit.newText) : text8.replace(edit.oldText, () => edit.newText),
3707
+ (text9, edit) => edit.all ? text9.split(edit.oldText).join(edit.newText) : text9.replace(edit.oldText, () => edit.newText),
3302
3708
  before
3303
3709
  );
3304
3710
 
@@ -3318,7 +3724,7 @@ var familyOf = (base) => {
3318
3724
  if (found === void 0) throw new Error(`no reason family for ${base}`);
3319
3725
  return found;
3320
3726
  };
3321
- var capReason = (text8) => text8.length <= REASON_LIMIT ? text8 : `${text8.slice(0, REASON_LIMIT - 1)}…`;
3727
+ var capReason = (text9) => text9.length <= REASON_LIMIT ? text9 : `${text9.slice(0, REASON_LIMIT - 1)}…`;
3322
3728
  var modelReason = (t, base, params2 = {}) => {
3323
3729
  const claim = `${base}.claim`;
3324
3730
  const requires = `${base}.requires`;
@@ -3345,9 +3751,9 @@ var textRecord = (value) => isRecord(value) ? Object.fromEntries(
3345
3751
  Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
3346
3752
  ) : {};
3347
3753
  var readJsonFile = (runtime, path) => {
3348
- const text8 = runtime.readText(path);
3349
- if (!text8.ok) return void 0;
3350
- const parsed = parseJsonObject(text8.value);
3754
+ const text9 = runtime.readText(path);
3755
+ if (!text9.ok) return void 0;
3756
+ const parsed = parseJsonObject(text9.value);
3351
3757
  return parsed.ok ? parsed.value : void 0;
3352
3758
  };
3353
3759
  var writeJsonFile = (runtime, path, value) => runtime.writeText(path, `${JSON.stringify({ schemaVersion: 1, ...value }, null, 2)}
@@ -3397,28 +3803,28 @@ var closeTask = (state, id) => assemble({
3397
3803
 
3398
3804
  // src/guards/release/grants.ts
3399
3805
  var GRANT_MINUTES = 30;
3400
- var text2 = (record3, key) => {
3806
+ var text3 = (record3, key) => {
3401
3807
  const value = record3[key];
3402
3808
  return typeof value === "string" ? value : void 0;
3403
3809
  };
3404
3810
  var toGrant = (value) => {
3405
3811
  if (!isRecord(value)) return void 0;
3406
- const guard = text2(value, "guard");
3407
- const reason = text2(value, "reason");
3408
- const grantedAt = text2(value, "grantedAt");
3409
- const expiresAt = text2(value, "expiresAt");
3410
- const sessionId = text2(value, "sessionId");
3812
+ const guard = text3(value, "guard");
3813
+ const reason = text3(value, "reason");
3814
+ const grantedAt = text3(value, "grantedAt");
3815
+ const expiresAt = text3(value, "expiresAt");
3816
+ const sessionId = text3(value, "sessionId");
3411
3817
  if (guard === void 0 || reason === void 0 || grantedAt === void 0 || expiresAt === void 0)
3412
3818
  return void 0;
3413
3819
  return { guard, reason, grantedAt, expiresAt, ...sessionId === void 0 ? {} : { sessionId } };
3414
3820
  };
3415
3821
  var toApproval = (value) => {
3416
3822
  if (!isRecord(value)) return void 0;
3417
- const task = text2(value, "task");
3418
- const check2 = text2(value, "check");
3419
- const at = text2(value, "at");
3823
+ const task = text3(value, "task");
3824
+ const check2 = text3(value, "check");
3825
+ const at = text3(value, "at");
3420
3826
  if (task === void 0 || check2 === void 0 || at === void 0) return void 0;
3421
- return { task, check: check2, at, reason: text2(value, "reason") ?? "" };
3827
+ return { task, check: check2, at, reason: text3(value, "reason") ?? "" };
3422
3828
  };
3423
3829
  var list = (raw, map) => Array.isArray(raw) ? raw.flatMap((item2) => {
3424
3830
  const value = map(item2);
@@ -3460,7 +3866,7 @@ var SHELL_TOOLS = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
3460
3866
  import { posix } from "node:path";
3461
3867
 
3462
3868
  // src/shell/scan.ts
3463
- var cursorOf = (text8) => ({ text: text8, pos: 0, incomplete: false, subs: [] });
3869
+ var cursorOf = (text9) => ({ text: text9, pos: 0, incomplete: false, subs: [] });
3464
3870
  var peek = (cursor, offset = 0) => cursor.text.charAt(cursor.pos + offset);
3465
3871
  var atEnd = (cursor) => cursor.pos >= cursor.text.length;
3466
3872
  var skipQuoted = (cursor, quote) => {
@@ -3675,8 +4081,8 @@ var nextToken = (cursor, tokens, pending) => {
3675
4081
  tokens.push({ kind: "op", text: op });
3676
4082
  if (op === "\n") flushHeredocs(cursor, tokens, pending);
3677
4083
  };
3678
- var lexBash = (text8) => {
3679
- const cursor = cursorOf(text8.replace(/\r\n/g, "\n"));
4084
+ var lexBash = (text9) => {
4085
+ const cursor = cursorOf(text9.replace(/\r\n/g, "\n"));
3680
4086
  const tokens = [];
3681
4087
  const pending = [];
3682
4088
  skipBlank(cursor);
@@ -3824,9 +4230,9 @@ var next = (cursor, tokens, cmd) => {
3824
4230
  }
3825
4231
  const op = OPERATORS2.find((candidate) => cursor.text.startsWith(candidate, cursor.pos));
3826
4232
  if (op !== void 0 || cmd && peek(cursor) === "&") {
3827
- const text8 = op ?? "&";
3828
- cursor.pos += text8.length;
3829
- tokens.push({ kind: "op", text: text8 });
4233
+ const text9 = op ?? "&";
4234
+ cursor.pos += text9.length;
4235
+ tokens.push({ kind: "op", text: text9 });
3830
4236
  return;
3831
4237
  }
3832
4238
  if ((peek(cursor) === "&" || peek(cursor) === ".") && peek(cursor, 1) === " " && commandStart(tokens)) {
@@ -3835,8 +4241,8 @@ var next = (cursor, tokens, cmd) => {
3835
4241
  }
3836
4242
  tokens.push(word(cursor, cmd));
3837
4243
  };
3838
- var lexPowerShell = (text8, cmd = false) => {
3839
- const cursor = cursorOf(text8.replace(/\r\n/g, "\n"));
4244
+ var lexPowerShell = (text9, cmd = false) => {
4245
+ const cursor = cursorOf(text9.replace(/\r\n/g, "\n"));
3840
4246
  const tokens = [];
3841
4247
  const skip = () => {
3842
4248
  while (peek(cursor) === " " || peek(cursor) === " ") cursor.pos += 1;
@@ -4039,7 +4445,7 @@ var MAX_DEPTH2 = 8;
4039
4445
  var ASSIGNMENT2 = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/s;
4040
4446
  var PS_ENV = /^\$env:([A-Za-z_][A-Za-z0-9_]*)=?(.*)$/is;
4041
4447
  var CHANGES_DIR = /* @__PURE__ */ new Set(["cd", "pushd", "set-location", "push-location"]);
4042
- var lex = (text8, language) => language === "bash" ? lexBash(text8) : lexPowerShell(text8, language === "cmd");
4448
+ var lex = (text9, language) => language === "bash" ? lexBash(text9) : lexPowerShell(text9, language === "cmd");
4043
4449
  var isWindowsAbsolute = (path) => /^[A-Za-z]:\//.test(path);
4044
4450
  var joinDir = (current2, target2) => {
4045
4451
  const next2 = toPosixPath(target2);
@@ -4110,21 +4516,21 @@ var visit = (raw, language, context, depth, collector) => {
4110
4516
  emit(result.stage, raw, language, context, collector);
4111
4517
  for (const extra of result.extra) emit(extra, raw, language, context, collector);
4112
4518
  };
4113
- var collect = (text8, language, context, depth, collector) => {
4519
+ var collect = (text9, language, context, depth, collector) => {
4114
4520
  if (depth > MAX_DEPTH2) {
4115
4521
  collector.incomplete = true;
4116
4522
  return;
4117
4523
  }
4118
- const lexed = lex(text8, language);
4524
+ const lexed = lex(text9, language);
4119
4525
  if (lexed.incomplete) collector.incomplete = true;
4120
4526
  for (const substitution of lexed.substitutions) {
4121
4527
  collect(substitution, language, { ...context }, depth + 1, collector);
4122
4528
  }
4123
4529
  for (const raw of rawCommands(lexed.tokens)) visit(raw, language, context, depth, collector);
4124
4530
  };
4125
- var normalizeCommand = (text8, dialect = "bash") => {
4531
+ var normalizeCommand = (text9, dialect = "bash") => {
4126
4532
  const collector = { out: [], incomplete: false };
4127
- collect(text8, dialect, { env: {}, dir: void 0, via: [] }, 0, collector);
4533
+ collect(text9, dialect, { env: {}, dir: void 0, via: [] }, 0, collector);
4128
4534
  return { commands: collector.out, incomplete: collector.incomplete };
4129
4535
  };
4130
4536
  var dialectOf = (toolName3) => toolName3 === "PowerShell" ? "powershell" : "bash";
@@ -4136,7 +4542,7 @@ var segmentsOf = (command) => normalizeCommand(command).commands.map((item2) =>
4136
4542
 
4137
4543
  // src/guards/shell.ts
4138
4544
  var HEREDOC = /^<<[-<]?$/;
4139
- var parseShell = (text8) => normalizeCommand(text8).commands.map((command) => ({
4545
+ var parseShell = (text9) => normalizeCommand(text9).commands.map((command) => ({
4140
4546
  env: command.env,
4141
4547
  argv: command.program === "" ? [] : [command.program, ...command.args],
4142
4548
  redirects: command.redirects.filter((redirect) => !HEREDOC.test(redirect.op)).map((redirect) => redirect.target)
@@ -4298,9 +4704,9 @@ var writtenPaths = (command) => {
4298
4704
  if (name === "git" && GIT_WRITERS.has(operands2[0] ?? "")) return [...command.redirects, ...operands2.slice(1)];
4299
4705
  return command.redirects;
4300
4706
  };
4301
- var mentioned = (text8) => {
4302
- if (/\.finitemonkey[\\/]allow/.test(text8)) return "allow_store";
4303
- if (/\.finitemonkey|\.gitleaksignore/.test(text8)) return "policy";
4707
+ var mentioned = (text9) => {
4708
+ if (/\.finitemonkey[\\/]allow/.test(text9)) return "allow_store";
4709
+ if (/\.finitemonkey|\.gitleaksignore/.test(text9)) return "policy";
4304
4710
  return void 0;
4305
4711
  };
4306
4712
  var bashSelfTargets = (root, commandLine) => parseShell(commandLine).flatMap(
@@ -4340,7 +4746,7 @@ var selfGuard = (input) => {
4340
4746
  };
4341
4747
 
4342
4748
  // src/guards/tests/hook.ts
4343
- import { join as join12, relative as relative3 } from "node:path";
4749
+ import { join as join14, relative as relative3 } from "node:path";
4344
4750
 
4345
4751
  // src/guards/tests/patterns.ts
4346
4752
  var JS_TEST = /(^|\/)(__tests__|__test__)\/|\.(test|spec)\.[cm]?[jt]sx?$|(^|\/)tests?\/.*\.[cm]?[jt]sx?$/;
@@ -4504,8 +4910,8 @@ var compareConfig = (path, before, after, accepted2) => {
4504
4910
  // src/guards/tests/hook.ts
4505
4911
  var localPath = (root, path) => toPosixPath(relative3(root, toAbsolutePath(path, root)));
4506
4912
  var read = (input, path) => {
4507
- const text8 = input.runtime.readText(path);
4508
- return text8.ok ? text8.value : void 0;
4913
+ const text9 = input.runtime.readText(path);
4914
+ return text9.ok ? text9.value : void 0;
4509
4915
  };
4510
4916
  var proposed = (tool, args2, before) => {
4511
4917
  if (tool === "Write") return textField(args2, "content") ?? "";
@@ -4530,7 +4936,7 @@ var shellViolations = (input, root) => {
4530
4936
  return parseShell(command).flatMap(
4531
4937
  (simple) => writtenPaths(simple).flatMap((path) => {
4532
4938
  const local = localPath(root, path);
4533
- const exists = input.runtime.stat(toAbsolutePath(join12(root, local))) !== void 0;
4939
+ const exists = input.runtime.stat(toAbsolutePath(join14(root, local))) !== void 0;
4534
4940
  if (!exists) return [];
4535
4941
  if (isProtectedConfig(local)) return [{ kind: "config_changed", path: local }];
4536
4942
  return isTestFile(local) ? [{ kind: "test_removed", path: local }] : [];
@@ -4610,8 +5016,8 @@ var parseMarkers = (prompt) => {
4610
5016
  // src/policy/pause.ts
4611
5017
  var PAUSE_FILE = "pause.json";
4612
5018
  var readPause = (runtime, paths) => {
4613
- const text8 = runtime.readText(paths.cache(PAUSE_FILE));
4614
- const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
5019
+ const text9 = runtime.readText(paths.cache(PAUSE_FILE));
5020
+ const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
4615
5021
  if (parsed?.ok !== true || !isRecord(parsed.value)) return void 0;
4616
5022
  const { sessionId, since, by } = parsed.value;
4617
5023
  if (typeof sessionId !== "string" || typeof since !== "string") return void 0;
@@ -4658,7 +5064,7 @@ var unlessPaused = (handler) => (input) => {
4658
5064
  };
4659
5065
 
4660
5066
  // src/gate/hooks/config.ts
4661
- import { join as join13 } from "node:path";
5067
+ import { join as join15 } from "node:path";
4662
5068
  var CONFIG_FILE3 = "config-hooks.json";
4663
5069
  var GUARDED_SOURCES = /* @__PURE__ */ new Set(["user_settings", "project_settings", "local_settings"]);
4664
5070
  var countIn = (value) => {
@@ -4666,20 +5072,20 @@ var countIn = (value) => {
4666
5072
  if (Array.isArray(value)) return value.reduce((sum, item2) => sum + countIn(item2), 0);
4667
5073
  return isRecord(value) ? Object.values(value).reduce((sum, item2) => sum + countIn(item2), 0) : 0;
4668
5074
  };
4669
- var censusOf = (text8) => {
4670
- if (text8 === void 0) return { hooks: 0, disabled: false };
4671
- const parsed = parseJsonObject(text8);
5075
+ var censusOf = (text9) => {
5076
+ if (text9 === void 0) return { hooks: 0, disabled: false };
5077
+ const parsed = parseJsonObject(text9);
4672
5078
  if (!parsed.ok) return void 0;
4673
5079
  return { hooks: countIn(parsed.value["hooks"]), disabled: parsed.value["disableAllHooks"] === true };
4674
5080
  };
4675
5081
  var settingsFiles = (paths, env) => [
4676
- toAbsolutePath(join13(paths.root, ".claude", "settings.json")),
4677
- toAbsolutePath(join13(paths.root, ".claude", "settings.local.json")),
4678
- ...env["HOME"] === void 0 ? [] : [toAbsolutePath(join13(env["HOME"], ".claude", "settings.json"))]
5082
+ toAbsolutePath(join15(paths.root, ".claude", "settings.json")),
5083
+ toAbsolutePath(join15(paths.root, ".claude", "settings.local.json")),
5084
+ ...env["HOME"] === void 0 ? [] : [toAbsolutePath(join15(env["HOME"], ".claude", "settings.json"))]
4679
5085
  ];
4680
5086
  var read2 = (runtime, path) => {
4681
- const text8 = runtime.readText(path);
4682
- return text8.ok ? text8.value : void 0;
5087
+ const text9 = runtime.readText(path);
5088
+ return text9.ok ? text9.value : void 0;
4683
5089
  };
4684
5090
  var known = (runtime, paths) => {
4685
5091
  const raw = readJsonFile(runtime, paths.cache(CONFIG_FILE3))?.["hooks"];
@@ -4734,9 +5140,9 @@ var LOCKFILES2 = [
4734
5140
  ];
4735
5141
  var packageManager = (files) => LOCKFILES2.find(([file]) => files.exists(file))?.[1] ?? "npm";
4736
5142
  var scriptChecks = (files) => {
4737
- const text8 = files.read("package.json");
4738
- if (text8 === void 0) return [];
4739
- const parsed = parseJsonObject(text8);
5143
+ const text9 = files.read("package.json");
5144
+ if (text9 === void 0) return [];
5145
+ const parsed = parseJsonObject(text9);
4740
5146
  const scripts = parsed.ok ? parsed.value["scripts"] : void 0;
4741
5147
  if (!isRecord(scripts)) return [];
4742
5148
  const pm = packageManager(files);
@@ -4788,7 +5194,7 @@ var implicitContract = (files) => {
4788
5194
  };
4789
5195
 
4790
5196
  // src/gate/prepare.ts
4791
- import { join as join15 } from "node:path";
5197
+ import { join as join17 } from "node:path";
4792
5198
 
4793
5199
  // src/core/yaml/scalar.ts
4794
5200
  var ESCAPES = {
@@ -4826,16 +5232,16 @@ var unescapeDouble = (body2) => {
4826
5232
  }
4827
5233
  return accept(out);
4828
5234
  };
4829
- var quotedEnd = (text8, start) => {
4830
- const quote = text8[start];
4831
- for (let index = start + 1; index < text8.length; index += 1) {
4832
- const char = text8[index];
5235
+ var quotedEnd = (text9, start) => {
5236
+ const quote = text9[start];
5237
+ for (let index = start + 1; index < text9.length; index += 1) {
5238
+ const char = text9[index];
4833
5239
  if (quote === '"' && char === "\\") {
4834
5240
  index += 1;
4835
5241
  continue;
4836
5242
  }
4837
5243
  if (char !== quote) continue;
4838
- if (quote === "'" && text8[index + 1] === "'") {
5244
+ if (quote === "'" && text9[index + 1] === "'") {
4839
5245
  index += 1;
4840
5246
  continue;
4841
5247
  }
@@ -4843,20 +5249,20 @@ var quotedEnd = (text8, start) => {
4843
5249
  }
4844
5250
  return -1;
4845
5251
  };
4846
- var parseQuoted = (text8) => {
4847
- const end = quotedEnd(text8, 0);
4848
- if (end !== text8.length - 1) return reject(`unterminated or trailing text after quoted string`);
4849
- const body2 = text8.slice(1, -1);
4850
- return text8.startsWith('"') ? unescapeDouble(body2) : accept(body2.replace(/''/g, "'"));
5252
+ var parseQuoted = (text9) => {
5253
+ const end = quotedEnd(text9, 0);
5254
+ if (end !== text9.length - 1) return reject(`unterminated or trailing text after quoted string`);
5255
+ const body2 = text9.slice(1, -1);
5256
+ return text9.startsWith('"') ? unescapeDouble(body2) : accept(body2.replace(/''/g, "'"));
4851
5257
  };
4852
5258
  var NUMBER = /^-?(0|[1-9]\d*)(\.\d+)?([eE][-+]?\d+)?$/;
4853
5259
  var UNSUPPORTED = /^[&*!|>%@`]/;
4854
- var parsePlain = (text8) => {
4855
- if (UNSUPPORTED.test(text8)) return reject(`unsupported YAML syntax "${text8.slice(0, 1)}"`);
4856
- if (text8 === "true" || text8 === "false") return accept(text8 === "true");
4857
- if (text8 === "null" || text8 === "~") return accept(null);
4858
- if (NUMBER.test(text8)) return accept(Number(text8));
4859
- return accept(text8);
5260
+ var parsePlain = (text9) => {
5261
+ if (UNSUPPORTED.test(text9)) return reject(`unsupported YAML syntax "${text9.slice(0, 1)}"`);
5262
+ if (text9 === "true" || text9 === "false") return accept(text9 === "true");
5263
+ if (text9 === "null" || text9 === "~") return accept(null);
5264
+ if (NUMBER.test(text9)) return accept(Number(text9));
5265
+ return accept(text9);
4860
5266
  };
4861
5267
 
4862
5268
  // src/core/yaml/flow.ts
@@ -4883,17 +5289,17 @@ var splitTop = (body2) => {
4883
5289
  if (last.trim() !== "" || parts.length > 0) parts.push(last);
4884
5290
  return accept(parts.map((part2) => part2.trim()));
4885
5291
  };
4886
- var splitKey = (text8) => {
5292
+ var splitKey = (text9) => {
4887
5293
  let index = 0;
4888
- if (text8.startsWith('"') || text8.startsWith("'")) {
4889
- index = quotedEnd(text8, 0) + 1;
5294
+ if (text9.startsWith('"') || text9.startsWith("'")) {
5295
+ index = quotedEnd(text9, 0) + 1;
4890
5296
  if (index <= 0) return void 0;
4891
5297
  }
4892
- for (; index < text8.length; index += 1) {
4893
- const char = text8[index];
4894
- if (char === "#" && text8[index - 1] === " ") return void 0;
4895
- if (char === ":" && (index + 1 === text8.length || text8[index + 1] === " ")) {
4896
- return { key: text8.slice(0, index).trim(), rest: text8.slice(index + 1).trim() };
5298
+ for (; index < text9.length; index += 1) {
5299
+ const char = text9[index];
5300
+ if (char === "#" && text9[index - 1] === " ") return void 0;
5301
+ if (char === ":" && (index + 1 === text9.length || text9[index + 1] === " ")) {
5302
+ return { key: text9.slice(0, index).trim(), rest: text9.slice(index + 1).trim() };
4897
5303
  }
4898
5304
  }
4899
5305
  return void 0;
@@ -4922,39 +5328,39 @@ var flowSeq = (parts) => {
4922
5328
  }
4923
5329
  return accept(out);
4924
5330
  };
4925
- var parseValue = (text8) => {
4926
- if (text8 === "") return accept(null);
4927
- const first = text8[0] ?? "";
4928
- if (first === '"' || first === "'") return parseQuoted(text8);
5331
+ var parseValue = (text9) => {
5332
+ if (text9 === "") return accept(null);
5333
+ const first = text9[0] ?? "";
5334
+ if (first === '"' || first === "'") return parseQuoted(text9);
4929
5335
  const close2 = OPEN[first];
4930
- if (close2 === void 0) return parsePlain(text8);
4931
- if (!text8.endsWith(close2)) return reject(`flow collection must end on the same line with "${close2}"`);
4932
- const parts = splitTop(text8.slice(1, -1));
5336
+ if (close2 === void 0) return parsePlain(text9);
5337
+ if (!text9.endsWith(close2)) return reject(`flow collection must end on the same line with "${close2}"`);
5338
+ const parts = splitTop(text9.slice(1, -1));
4933
5339
  if (!parts.ok) return parts;
4934
5340
  return first === "[" ? flowSeq(parts.value) : flowMap(parts.value);
4935
5341
  };
4936
5342
 
4937
5343
  // src/core/yaml/parse.ts
4938
5344
  var BOM = new RegExp(`^${String.fromCharCode(65279)}`);
4939
- var stripComment2 = (text8) => {
4940
- for (let index = 0; index < text8.length; index += 1) {
4941
- const char = text8[index];
5345
+ var stripComment2 = (text9) => {
5346
+ for (let index = 0; index < text9.length; index += 1) {
5347
+ const char = text9[index];
4942
5348
  if (char === '"' || char === "'") {
4943
- const end = quotedEnd(text8, index);
4944
- if (end < 0) return text8;
5349
+ const end = quotedEnd(text9, index);
5350
+ if (end < 0) return text9;
4945
5351
  index = end;
4946
- } else if (char === "#" && (index === 0 || text8[index - 1] === " ")) return text8.slice(0, index);
5352
+ } else if (char === "#" && (index === 0 || text9[index - 1] === " ")) return text9.slice(0, index);
4947
5353
  }
4948
- return text8;
5354
+ return text9;
4949
5355
  };
4950
5356
  var toLines = (source) => {
4951
5357
  const lines3 = [];
4952
5358
  const raw = source.replace(BOM, "").split(/\r?\n/);
4953
5359
  for (const [index, full] of raw.entries()) {
4954
- const text8 = stripComment2(full).trimEnd();
4955
- const body2 = text8.trimStart();
5360
+ const text9 = stripComment2(full).trimEnd();
5361
+ const body2 = text9.trimStart();
4956
5362
  if (body2 === "" || body2 === "---" && lines3.length === 0) continue;
4957
- const indentText = text8.slice(0, text8.length - body2.length);
5363
+ const indentText = text9.slice(0, text9.length - body2.length);
4958
5364
  if (indentText.includes(" ")) return reject({ line: index + 1, message: "tabs are not allowed for indentation" });
4959
5365
  if (body2 === "---" || body2 === "...")
4960
5366
  return reject({ line: index + 1, message: "only one YAML document is allowed" });
@@ -4963,11 +5369,11 @@ var toLines = (source) => {
4963
5369
  return accept(lines3);
4964
5370
  };
4965
5371
  var fail = (line2, message) => reject({ line: line2?.number ?? 0, message });
4966
- var scalar = (line2, text8) => {
4967
- const value = parseValue(text8);
5372
+ var scalar = (line2, text9) => {
5373
+ const value = parseValue(text9);
4968
5374
  return value.ok ? value : fail(line2, value.error);
4969
5375
  };
4970
- var isItem = (text8) => text8 === "-" || text8.startsWith("- ");
5376
+ var isItem = (text9) => text9 === "-" || text9.startsWith("- ");
4971
5377
  var child = (lines3, index, parent, sameIndentSeq) => {
4972
5378
  const next2 = lines3[index];
4973
5379
  if (next2 !== void 0 && next2.indent > parent.indent) return block(lines3, index, next2.indent);
@@ -5210,8 +5616,8 @@ var validateContract = (value, implicit = false) => {
5210
5616
  // src/task/load.ts
5211
5617
  var contractHash = (contract) => sha256Hex(JSON.stringify(contract.acceptance.map((check2) => [check2.id, check2.kind, check2.run ?? ""])));
5212
5618
  var taskRelative = (id) => `.finitemonkey/tasks/${id}.yml`;
5213
- var parseContract = (id, text8) => {
5214
- const yaml = parseYaml(text8);
5619
+ var parseContract = (id, text9) => {
5620
+ const yaml = parseYaml(text9);
5215
5621
  if (!yaml.ok)
5216
5622
  return reject({ kind: "invalid", id, issues: [`line ${String(yaml.error.line)}: ${yaml.error.message}`] });
5217
5623
  const valid = validateContract(yaml.value);
@@ -5285,7 +5691,7 @@ var readBaseline = (runtime, paths) => {
5285
5691
  var writeBaseline = (runtime, paths, treeHash2, checks) => writeJsonFile(runtime, paths.cache(BASELINE_FILE), { treeHash: treeHash2, checks: { ...checks } }).ok;
5286
5692
 
5287
5693
  // src/gate/diff-shape.ts
5288
- import { join as join14 } from "node:path";
5694
+ import { join as join16 } from "node:path";
5289
5695
 
5290
5696
  // src/gate/git.ts
5291
5697
  var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
@@ -5321,10 +5727,10 @@ var resolveBase = (git2, declared) => {
5321
5727
  const merged2 = remote === void 0 ? void 0 : git2.out(["merge-base", "HEAD", remote])?.trim();
5322
5728
  return nonEmpty(merged2) ?? nonEmpty(headSha(git2)) ?? EMPTY_TREE;
5323
5729
  };
5324
- var nulFields = (text8) => text8.split("\0").filter((part2) => part2 !== "");
5730
+ var nulFields = (text9) => text9.split("\0").filter((part2) => part2 !== "");
5325
5731
  var untracked = (git2) => nulFields(git2.out(["ls-files", "--others", "--exclude-standard", "-z", "--", ".", EXCLUDE_FM]) ?? "");
5326
- var parseNameStatus = (text8) => {
5327
- const fields = nulFields(text8);
5732
+ var parseNameStatus = (text9) => {
5733
+ const fields = nulFields(text9);
5328
5734
  const changes = [];
5329
5735
  for (let index = 0; index < fields.length; index += 1) {
5330
5736
  const code = fields[index] ?? "";
@@ -5392,8 +5798,8 @@ var aheadOfUpstream = (git2) => {
5392
5798
 
5393
5799
  // src/gate/diff-shape.ts
5394
5800
  var untrackedLines = (runtime, root, changes) => changes.filter((change) => change.status === "A").reduce((sum, change) => {
5395
- const text8 = runtime.readText(toAbsolutePath(join14(root, change.path)));
5396
- return sum + (text8.ok ? text8.value.split("\n").length : 0);
5801
+ const text9 = runtime.readText(toAbsolutePath(join16(root, change.path)));
5802
+ return sum + (text9.ok ? text9.value.split("\n").length : 0);
5397
5803
  }, 0);
5398
5804
  var diffShape = (runtime, git2, base, changes) => ({
5399
5805
  files: changes.map((change) => change.path),
@@ -5416,8 +5822,8 @@ var ciChecks = (value) => Array.isArray(value) ? value.flatMap((item2) => {
5416
5822
  const name = optionalString(item2, "name") ?? optionalString(item2, "context") ?? "?";
5417
5823
  return [{ name, state: ciState(item2) }];
5418
5824
  }) : [];
5419
- var parsePrView = (text8) => {
5420
- const parsed = parseJsonObject(text8);
5825
+ var parsePrView = (text9) => {
5826
+ const parsed = parseJsonObject(text9);
5421
5827
  if (!parsed.ok) return void 0;
5422
5828
  const record3 = parsed.value;
5423
5829
  const number2 = record3["number"];
@@ -5598,10 +6004,10 @@ var applySize = (contract, verdict) => {
5598
6004
  // src/gate/prepare.ts
5599
6005
  var diskFiles = (runtime, root) => ({
5600
6006
  read: (relative11) => {
5601
- const text8 = runtime.readText(toAbsolutePath(join15(root, relative11)));
5602
- return text8.ok ? text8.value : void 0;
6007
+ const text9 = runtime.readText(toAbsolutePath(join17(root, relative11)));
6008
+ return text9.ok ? text9.value : void 0;
5603
6009
  },
5604
- exists: (relative11) => runtime.stat(toAbsolutePath(join15(root, relative11))) !== void 0
6010
+ exists: (relative11) => runtime.stat(toAbsolutePath(join17(root, relative11))) !== void 0
5605
6011
  });
5606
6012
  var baseFiles = (git2, base, disk) => ({
5607
6013
  read: (relative11) => base === EMPTY_TREE ? disk.read(relative11) : fileAt(git2, base, relative11),
@@ -5613,7 +6019,7 @@ var loadContract = (options, paths, git2, base) => {
5613
6019
  if (id === void 0) return accept(loadImplicit(options.runtime, paths, baseFiles(git2, base, disk)));
5614
6020
  return loadTask(options.runtime, paths, { disk: disk.read, base: (relative11) => fileAt(git2, base, relative11) }, id);
5615
6021
  };
5616
- var memo = (make) => {
6022
+ var memo2 = (make) => {
5617
6023
  let value;
5618
6024
  return () => {
5619
6025
  value ??= { v: make() };
@@ -5652,7 +6058,7 @@ var prepareGate = (options) => {
5652
6058
  if (tree === void 0) return { kind: "failed", failure: { kind: "no_tree_hash" } };
5653
6059
  const skip = options.noSkip === true ? void 0 : skipReason(options.runtime, paths, loaded.value, changes, tree, options.sessionId);
5654
6060
  if (skip !== void 0) return { kind: "skipped", reason: skip };
5655
- const pr = memo(() => prStatus(options.runtime, git2, paths));
6061
+ const pr = memo2(() => prStatus(options.runtime, git2, paths));
5656
6062
  const size = measureSize(options.runtime, git2, base, changes, loaded.value.contract);
5657
6063
  const ctx = {
5658
6064
  runtime: options.runtime,
@@ -5720,11 +6126,11 @@ var trust = (input, paths, task) => {
5720
6126
  });
5721
6127
  if (ready.kind !== "ready" || ready.prepared.ctx.contract.id !== task) return false;
5722
6128
  const lock = readTrust(runtime, paths);
5723
- const text8 = runtime.readText(paths.task(task));
6129
+ const text9 = runtime.readText(paths.task(task));
5724
6130
  return writeTrust(runtime, paths, {
5725
6131
  ...lock,
5726
6132
  contracts: { ...lock.contracts, [task]: ready.prepared.loaded.hash },
5727
- approved: task === IMPLICIT_ID || !text8.ok ? lock.approved : { ...lock.approved, [task]: text8.value }
6133
+ approved: task === IMPLICIT_ID || !text9.ok ? lock.approved : { ...lock.approved, [task]: text9.value }
5728
6134
  }).ok;
5729
6135
  };
5730
6136
  var apply = (input, paths, markers) => {
@@ -5776,8 +6182,8 @@ var promptRelease = (input) => {
5776
6182
  var PRS_CACHE_MS = 10 * 6e4;
5777
6183
  var PRS_TIMEOUT_MS = 3e3;
5778
6184
  var PRS_AUTH_TIMEOUT_MS = 2e3;
5779
- var parsePrList = (text8) => {
5780
- const parsed = parseJson(text8);
6185
+ var parsePrList = (text9) => {
6186
+ const parsed = parseJson(text9);
5781
6187
  if (!parsed.ok || !Array.isArray(parsed.value)) return void 0;
5782
6188
  return parsed.value.flatMap((item2) => {
5783
6189
  if (!isRecord(item2) || typeof item2["number"] !== "number") return [];
@@ -5863,16 +6269,16 @@ var params = (value) => isRecord(value) ? Object.fromEntries(
5863
6269
  var notes = (value) => Array.isArray(value) ? value.flatMap(
5864
6270
  (item2) => isRecord(item2) && typeof item2["key"] === "string" ? [{ key: item2["key"], params: params(item2["params"]) }] : []
5865
6271
  ) : [];
5866
- var text3 = (record3, key) => {
6272
+ var text4 = (record3, key) => {
5867
6273
  const value = record3[key];
5868
6274
  return typeof value === "string" ? value : void 0;
5869
6275
  };
5870
6276
  var optional = (record3) => {
5871
6277
  const exitCode = record3["exitCode"];
5872
6278
  const durationMs = record3["durationMs"];
5873
- const command = text3(record3, "command");
5874
- const stdoutSha256 = text3(record3, "stdoutSha256");
5875
- const releasedBy = text3(record3, "releasedBy");
6279
+ const command = text4(record3, "command");
6280
+ const stdoutSha256 = text4(record3, "stdoutSha256");
6281
+ const releasedBy = text4(record3, "releasedBy");
5876
6282
  const reason = oneOf2(UNVERIFIED_REASONS, record3["reason"]);
5877
6283
  const failures = record3["failures"];
5878
6284
  const testCount = record3["testCount"];
@@ -5892,11 +6298,11 @@ var parseEvidence = (line2) => {
5892
6298
  if (!parsed.ok) return void 0;
5893
6299
  const record3 = parsed.value;
5894
6300
  const status = oneOf2(EVIDENCE_STATUSES, record3["status"]);
5895
- const task = text3(record3, "task");
5896
- const check2 = text3(record3, "check");
5897
- const kind = text3(record3, "kind");
5898
- const treeHash2 = text3(record3, "treeHash");
5899
- const at = text3(record3, "at");
6301
+ const task = text4(record3, "task");
6302
+ const check2 = text4(record3, "check");
6303
+ const kind = text4(record3, "kind");
6304
+ const treeHash2 = text4(record3, "treeHash");
6305
+ const at = text4(record3, "at");
5900
6306
  if (record3["schema"] !== EVIDENCE_SCHEMA || status === void 0) return void 0;
5901
6307
  if (task === void 0 || check2 === void 0 || kind === void 0 || treeHash2 === void 0 || at === void 0) {
5902
6308
  return void 0;
@@ -5913,7 +6319,7 @@ var parseEvidence = (line2) => {
5913
6319
  ...optional(record3)
5914
6320
  };
5915
6321
  };
5916
- var readLedger = (runtime, path) => {
6322
+ var readLedger2 = (runtime, path) => {
5917
6323
  const content = runtime.readText(path);
5918
6324
  if (!content.ok) return [];
5919
6325
  return content.value.split("\n").flatMap((line2) => {
@@ -6595,7 +7001,26 @@ var en_default = {
6595
7001
  "gain.row.gate_time": "{minutes, plural, one {# minute} other {# minutes}} spent in the gate",
6596
7002
  "gain.up": "up {delta} on the window before",
6597
7003
  "gain.down": "down {delta} on the window before",
6598
- "gain.footer": "Counts come from local files only; a save is a block followed by a passing gate, not proof that the block caused it."
7004
+ "gain.footer": "Counts come from local files only; a save is a block followed by a passing gate, not proof that the block caused it.",
7005
+ "critique.command.verify": "Check a review's must-fix findings: run each reproduction command on base and head in throwaway worktrees and keep only what fails on head alone",
7006
+ "critique.title": "Critique",
7007
+ "critique.group.must": "Must fix",
7008
+ "critique.group.should": "Should fix",
7009
+ "critique.group.nice": "Nice to have",
7010
+ "critique.group.already-there": "Already there (fails on base too)",
7011
+ "critique.group.not-reproduced": "Not reproduced",
7012
+ "critique.finding": "{id}. {what}. Problem: {problem}. Fix: {fix}. If not fixed: {risk}.",
7013
+ "critique.table.command": "reproduction",
7014
+ "critique.table.outcome": "outcome",
7015
+ "critique.outcome.head-only": "fails only on head: kept",
7016
+ "critique.outcome.already-there": "fails on both: already there",
7017
+ "critique.outcome.not-reproduced": "fails on neither: not reproduced",
7018
+ "critique.outcome.not-run": "not run",
7019
+ "critique.demoted": "Finding {id} was marked must fix without a reproduction command and expected result, so it counts as should fix.",
7020
+ "critique.decision": "Decision needed: finding {id} ({what}) already fails on the base branch and is an active risk.",
7021
+ "critique.verdict": "Verdict: {verdict}",
7022
+ "critique.error.invalid": "the findings file is not valid: {detail}",
7023
+ "critique.error.must_fix": "{count, plural, one {# must-fix finding reproduced on head only} other {# must-fix findings reproduced on head only}}"
6599
7024
  };
6600
7025
 
6601
7026
  // src/i18n/es.json
@@ -7265,7 +7690,26 @@ var es_default = {
7265
7690
  "gain.row.gate_time": "{minutes, plural, one {# minuto} other {# minutos}} en la compuerta",
7266
7691
  "gain.up": "+{delta} respecto a la ventana anterior",
7267
7692
  "gain.down": "-{delta} respecto a la ventana anterior",
7268
- "gain.footer": "Los conteos salen solo de archivos locales; un acierto es un bloqueo seguido de una compuerta en verde, no prueba de que el bloqueo lo causara."
7693
+ "gain.footer": "Los conteos salen solo de archivos locales; un acierto es un bloqueo seguido de una compuerta en verde, no prueba de que el bloqueo lo causara.",
7694
+ "critique.command.verify": "Revisa los hallazgos obligatorios de una revisión: ejecuta cada comando de reproducción en base y en head en worktrees desechables y conserva solo lo que falla únicamente en head",
7695
+ "critique.title": "Crítica",
7696
+ "critique.group.must": "Corregir sí o sí",
7697
+ "critique.group.should": "Debería corregirse",
7698
+ "critique.group.nice": "Sería bueno",
7699
+ "critique.group.already-there": "Ya estaba (falla también en base)",
7700
+ "critique.group.not-reproduced": "No reproducido",
7701
+ "critique.finding": "{id}. {what}. Problema: {problem}. Arreglo: {fix}. Si no se corrige: {risk}.",
7702
+ "critique.table.command": "reproducción",
7703
+ "critique.table.outcome": "resultado",
7704
+ "critique.outcome.head-only": "falla solo en head: se mantiene",
7705
+ "critique.outcome.already-there": "falla en ambos: ya estaba",
7706
+ "critique.outcome.not-reproduced": "no falla en ninguno: no reproducido",
7707
+ "critique.outcome.not-run": "no se ejecutó",
7708
+ "critique.demoted": "El hallazgo {id} venía como obligatorio sin comando de reproducción ni resultado esperado, así que cuenta como debería corregirse.",
7709
+ "critique.decision": "Decisión necesaria: el hallazgo {id} ({what}) ya falla en la rama base y es un riesgo activo.",
7710
+ "critique.verdict": "Veredicto: {verdict}",
7711
+ "critique.error.invalid": "el archivo de hallazgos no es válido: {detail}",
7712
+ "critique.error.must_fix": "{count, plural, one {# hallazgo obligatorio reproducido solo en head} other {# hallazgos obligatorios reproducidos solo en head}}"
7269
7713
  };
7270
7714
 
7271
7715
  // src/i18n/pt-BR.json
@@ -7935,7 +8379,26 @@ var pt_BR_default = {
7935
8379
  "gain.row.gate_time": "{minutes, plural, one {# minuto} other {# minutos}} no portão",
7936
8380
  "gain.up": "+{delta} em relação à janela anterior",
7937
8381
  "gain.down": "-{delta} em relação à janela anterior",
7938
- "gain.footer": "As contagens vêm só de arquivos locais; um acerto é um bloqueio seguido de portão verde, não prova de que o bloqueio causou isso."
8382
+ "gain.footer": "As contagens vêm só de arquivos locais; um acerto é um bloqueio seguido de portão verde, não prova de que o bloqueio causou isso.",
8383
+ "critique.command.verify": "Confere os achados obrigatórios de uma revisão: roda cada comando de reprodução na base e no head em worktrees descartáveis e fica só com o que falha apenas no head",
8384
+ "critique.title": "Crítica",
8385
+ "critique.group.must": "Corrigir obrigatoriamente",
8386
+ "critique.group.should": "Deveria corrigir",
8387
+ "critique.group.nice": "Seria bom",
8388
+ "critique.group.already-there": "Já existia (falha na base também)",
8389
+ "critique.group.not-reproduced": "Não reproduzido",
8390
+ "critique.finding": "{id}. {what}. Problema: {problem}. Conserto: {fix}. Se não corrigir: {risk}.",
8391
+ "critique.table.command": "reprodução",
8392
+ "critique.table.outcome": "resultado",
8393
+ "critique.outcome.head-only": "falha só no head: mantido",
8394
+ "critique.outcome.already-there": "falha nos dois: já existia",
8395
+ "critique.outcome.not-reproduced": "não falha em nenhum: não reproduzido",
8396
+ "critique.outcome.not-run": "não rodou",
8397
+ "critique.demoted": "O achado {id} veio como obrigatório sem comando de reprodução e resultado esperado, então conta como deveria corrigir.",
8398
+ "critique.decision": "Decisão necessária: o achado {id} ({what}) já falha na branch base e é um risco ativo.",
8399
+ "critique.verdict": "Veredito: {verdict}",
8400
+ "critique.error.invalid": "o arquivo de achados não é válido: {detail}",
8401
+ "critique.error.must_fix": "{count, plural, one {# achado obrigatório reproduzido só no head} other {# achados obrigatórios reproduzidos só no head}}"
7939
8402
  };
7940
8403
 
7941
8404
  // src/i18n/review.json
@@ -8606,7 +9069,26 @@ var review_default = {
8606
9069
  "gain.row.gate_time": "machine",
8607
9070
  "gain.up": "machine",
8608
9071
  "gain.down": "machine",
8609
- "gain.footer": "machine"
9072
+ "gain.footer": "machine",
9073
+ "critique.command.verify": "machine",
9074
+ "critique.title": "machine",
9075
+ "critique.group.must": "machine",
9076
+ "critique.group.should": "machine",
9077
+ "critique.group.nice": "machine",
9078
+ "critique.group.already-there": "machine",
9079
+ "critique.group.not-reproduced": "machine",
9080
+ "critique.finding": "machine",
9081
+ "critique.table.command": "machine",
9082
+ "critique.table.outcome": "machine",
9083
+ "critique.outcome.head-only": "machine",
9084
+ "critique.outcome.already-there": "machine",
9085
+ "critique.outcome.not-reproduced": "machine",
9086
+ "critique.outcome.not-run": "machine",
9087
+ "critique.demoted": "machine",
9088
+ "critique.decision": "machine",
9089
+ "critique.verdict": "machine",
9090
+ "critique.error.invalid": "machine",
9091
+ "critique.error.must_fix": "machine"
8610
9092
  },
8611
9093
  es: {
8612
9094
  "command.init.summary": "machine",
@@ -9274,7 +9756,26 @@ var review_default = {
9274
9756
  "gain.row.gate_time": "machine",
9275
9757
  "gain.up": "machine",
9276
9758
  "gain.down": "machine",
9277
- "gain.footer": "machine"
9759
+ "gain.footer": "machine",
9760
+ "critique.command.verify": "machine",
9761
+ "critique.title": "machine",
9762
+ "critique.group.must": "machine",
9763
+ "critique.group.should": "machine",
9764
+ "critique.group.nice": "machine",
9765
+ "critique.group.already-there": "machine",
9766
+ "critique.group.not-reproduced": "machine",
9767
+ "critique.finding": "machine",
9768
+ "critique.table.command": "machine",
9769
+ "critique.table.outcome": "machine",
9770
+ "critique.outcome.head-only": "machine",
9771
+ "critique.outcome.already-there": "machine",
9772
+ "critique.outcome.not-reproduced": "machine",
9773
+ "critique.outcome.not-run": "machine",
9774
+ "critique.demoted": "machine",
9775
+ "critique.decision": "machine",
9776
+ "critique.verdict": "machine",
9777
+ "critique.error.invalid": "machine",
9778
+ "critique.error.must_fix": "machine"
9278
9779
  }
9279
9780
  };
9280
9781
 
@@ -9431,7 +9932,7 @@ var currentReport = (runtime, paths, sessionId, taskId) => {
9431
9932
  const { ctx, loaded } = ready.prepared;
9432
9933
  return buildReport({
9433
9934
  contract: ctx.contract,
9434
- ledger: readLedger(runtime, paths.evidence(ctx.contract.id)),
9935
+ ledger: readLedger2(runtime, paths.evidence(ctx.contract.id)),
9435
9936
  treeHash: ctx.treeHash,
9436
9937
  base: ctx.base,
9437
9938
  source: loaded.source,
@@ -9457,22 +9958,22 @@ var saveSnapshot = (runtime, paths, t, sessionId) => {
9457
9958
  const snapshot2 = snapshotOf(t, currentReport(runtime, paths, sessionId), grants, new Date(now).toISOString());
9458
9959
  return writeState(runtime, paths, { ...readState(runtime, paths), snapshot: snapshot2 }).ok;
9459
9960
  };
9460
- var text4 = (snapshot2, key) => {
9961
+ var text5 = (snapshot2, key) => {
9461
9962
  const value = snapshot2[key];
9462
9963
  return typeof value === "string" ? value : "";
9463
9964
  };
9464
9965
  var snapshotFacts = (t, snapshot2) => {
9465
- if (snapshot2 === void 0 || text4(snapshot2, "at") === "") return void 0;
9466
- const task = text4(snapshot2, "task");
9966
+ if (snapshot2 === void 0 || text5(snapshot2, "at") === "") return void 0;
9967
+ const task = text5(snapshot2, "task");
9467
9968
  return t(task === "" || task === "implicit" ? "gate.context.state_implicit" : "gate.context.state", {
9468
- at: text4(snapshot2, "at"),
9969
+ at: text5(snapshot2, "at"),
9469
9970
  task,
9470
- goal: text4(snapshot2, "goal"),
9471
- tree: text4(snapshot2, "tree"),
9472
- passed: text4(snapshot2, "passed"),
9473
- failed: text4(snapshot2, "failed"),
9474
- unverified: text4(snapshot2, "unverified"),
9475
- grants: text4(snapshot2, "grants")
9971
+ goal: text5(snapshot2, "goal"),
9972
+ tree: text5(snapshot2, "tree"),
9973
+ passed: text5(snapshot2, "passed"),
9974
+ failed: text5(snapshot2, "failed"),
9975
+ unverified: text5(snapshot2, "unverified"),
9976
+ grants: text5(snapshot2, "grants")
9476
9977
  });
9477
9978
  };
9478
9979
 
@@ -9564,10 +10065,10 @@ var pairs = (record3) => {
9564
10065
  return names.filter((name) => typeof name === "string").map((name) => `${task}\0${name}`);
9565
10066
  };
9566
10067
  var callersRate = (runtime, paths) => {
9567
- const text8 = runtime.readText(paths.log);
10068
+ const text9 = runtime.readText(paths.log);
9568
10069
  const flagged = /* @__PURE__ */ new Set();
9569
10070
  const released2 = /* @__PURE__ */ new Set();
9570
- for (const line2 of text8.ok ? text8.value.split("\n") : []) {
10071
+ for (const line2 of text9.ok ? text9.value.split("\n") : []) {
9571
10072
  const parsed = line2.trim() === "" ? void 0 : parseJsonObject(line2);
9572
10073
  if (parsed === void 0 || !parsed.ok || !isRecord(parsed.value)) continue;
9573
10074
  const event = parsed.value["event"];
@@ -9585,14 +10086,14 @@ var callersRate = (runtime, paths) => {
9585
10086
  };
9586
10087
 
9587
10088
  // src/gate/callers/scan.ts
9588
- import { join as join16 } from "node:path";
10089
+ import { join as join18 } from "node:path";
9589
10090
 
9590
10091
  // src/gate/callers/imports.ts
9591
10092
  import { posix as posix2 } from "node:path";
9592
10093
  var JS_SPECIFIER = /(?:\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)["']([^"']+)["']/g;
9593
10094
  var JS_EXTENSION = /\.(?:[cm]?[jt]sx?)$/;
9594
10095
  var stripModule = (path) => path.replace(JS_EXTENSION, "").replace(/\/index$/, "");
9595
- var jsSpecifiers = (text8) => [...text8.matchAll(JS_SPECIFIER)].map((match) => match[1] ?? "");
10096
+ var jsSpecifiers = (text9) => [...text9.matchAll(JS_SPECIFIER)].map((match) => match[1] ?? "");
9596
10097
  var jsImports = (callerPath, callerText, origin) => {
9597
10098
  const target2 = stripModule(origin);
9598
10099
  return jsSpecifiers(callerText).some((specifier) => {
@@ -9694,9 +10195,9 @@ var languageOf = (path) => {
9694
10195
  const extension = extname(path).toLowerCase();
9695
10196
  return LANGUAGES2.find((language) => language.extensions.includes(extension));
9696
10197
  };
9697
- var definitionsIn = (language, text8) => {
10198
+ var definitionsIn = (language, text9) => {
9698
10199
  const names = /* @__PURE__ */ new Set();
9699
- for (const line2 of text8.split(/\r?\n/)) {
10200
+ for (const line2 of text9.split(/\r?\n/)) {
9700
10201
  for (const pattern of language.definitions) {
9701
10202
  const name = pattern.exec(line2)?.[1];
9702
10203
  if (name !== void 0 && !KEYWORDS.has(name)) names.add(name);
@@ -9709,8 +10210,8 @@ var definesName = (language, line2, name) => language.definitions.some((pattern)
9709
10210
  // src/gate/callers/scan.ts
9710
10211
  var MAX_SYMBOLS = 200;
9711
10212
  var current = (runtime, root, path) => {
9712
- const text8 = runtime.readText(toAbsolutePath(join16(root, path)));
9713
- return text8.ok ? text8.value : "";
10213
+ const text9 = runtime.readText(toAbsolutePath(join18(root, path)));
10214
+ return text9.ok ? text9.value : "";
9714
10215
  };
9715
10216
  var keyOf = (language, name) => `${language.id}\0${name}`;
9716
10217
  var redefinedNames = (runtime, git2, changes) => {
@@ -9751,7 +10252,7 @@ var grep = (git2, names) => {
9751
10252
  git2.out(["grep", "-n", "-z", "-w", "-I", "--untracked", "--no-color", ...patterns, "--", ".", EXCLUDE_FM]) ?? ""
9752
10253
  );
9753
10254
  };
9754
- var mentions = (text8, name) => new RegExp(`(?<![\\w$])${name.replace(/[$?!]/g, "\\$&")}(?![\\w$])`).test(text8);
10255
+ var mentions = (text9, name) => new RegExp(`(?<![\\w$])${name.replace(/[$?!]/g, "\\$&")}(?![\\w$])`).test(text9);
9755
10256
  var callSites = (runtime, git2, symbols) => {
9756
10257
  const hits = grep(git2, [...new Set(symbols.map((symbol) => symbol.name))]);
9757
10258
  const texts2 = /* @__PURE__ */ new Map();
@@ -9826,31 +10327,31 @@ var ZERO = [
9826
10327
  /^ℹ tests 0$/m,
9827
10328
  /No tests found/
9828
10329
  ];
9829
- var pyTotal = (text8) => {
9830
- const line2 = /^=+ (.*) in [\d.]+s.*=+$/m.exec(text8)?.[1];
10330
+ var pyTotal = (text9) => {
10331
+ const line2 = /^=+ (.*) in [\d.]+s.*=+$/m.exec(text9)?.[1];
9831
10332
  if (line2 === void 0) return void 0;
9832
10333
  return [...line2.matchAll(/(\d+) (passed|failed|error|errors|skipped|xfailed|xpassed)/g)].reduce(
9833
10334
  (sum, match) => sum + Number(match[1] ?? "0"),
9834
10335
  0
9835
10336
  );
9836
10337
  };
9837
- var counted = (text8) => {
10338
+ var counted = (text9) => {
9838
10339
  for (const counter of COUNTERS) {
9839
- const total = counter.total.exec(text8)?.[1];
10340
+ const total = counter.total.exec(text9)?.[1];
9840
10341
  if (total === void 0) continue;
9841
- const failed4 = counter.failed?.exec(text8)?.[1];
10342
+ const failed4 = counter.failed?.exec(text9)?.[1];
9842
10343
  return { total: Number(total), ...failed4 === void 0 ? {} : { failed: Number(failed4) } };
9843
10344
  }
9844
- const py = pyTotal(text8);
10345
+ const py = pyTotal(text9);
9845
10346
  return py === void 0 ? {} : { total: py };
9846
10347
  };
9847
10348
  var parseTestSummary = (output) => {
9848
- const text8 = output.replace(ANSI2, "");
10349
+ const text9 = output.replace(ANSI2, "");
9849
10350
  const failures = [
9850
- ...new Set(FAILURE_LINES.flatMap((pattern) => [...text8.matchAll(pattern)].map((match) => (match[1] ?? "").trim())))
10351
+ ...new Set(FAILURE_LINES.flatMap((pattern) => [...text9.matchAll(pattern)].map((match) => (match[1] ?? "").trim())))
9851
10352
  ].filter((name) => name !== "" && !/^failing tests:?$/i.test(name));
9852
- if (ZERO.some((pattern) => pattern.test(text8))) return { total: 0, failures };
9853
- return { ...counted(text8), failures };
10353
+ if (ZERO.some((pattern) => pattern.test(text9))) return { total: 0, failures };
10354
+ return { ...counted(text9), failures };
9854
10355
  };
9855
10356
 
9856
10357
  // src/gate/checks/command.ts
@@ -9861,8 +10362,8 @@ var REWRITERS = /^\s*(rtk)\b/;
9861
10362
  var TRIVIAL = /* @__PURE__ */ new Set(["echo", "true", ":", "printf", "exit", "sleep", "cat", "yes", "pwd", "ls", "date", "env"]);
9862
10363
  var TAIL = 160;
9863
10364
  var MAX_FAILURES2 = 5;
9864
- var tail = (text8) => {
9865
- const lines3 = text8.split(/\r?\n/).filter((line2) => line2.trim() !== "");
10365
+ var tail = (text9) => {
10366
+ const lines3 = text9.split(/\r?\n/).filter((line2) => line2.trim() !== "");
9866
10367
  const last = lines3[lines3.length - 1]?.trim() ?? "";
9867
10368
  return last.length > TAIL ? `${last.slice(0, TAIL)}…` : last;
9868
10369
  };
@@ -9982,10 +10483,10 @@ var gitCheck = (check2, ctx) => {
9982
10483
  };
9983
10484
 
9984
10485
  // src/gate/checks/look.ts
9985
- import { join as join19 } from "node:path";
10486
+ import { join as join21 } from "node:path";
9986
10487
 
9987
10488
  // src/look/config.ts
9988
- import { join as join17 } from "node:path";
10489
+ import { join as join19 } from "node:path";
9989
10490
 
9990
10491
  // src/look/types.ts
9991
10492
  var LOOK_RULES = [
@@ -10020,7 +10521,7 @@ var DEFAULT_SCREENS = [
10020
10521
  "**/*.sass",
10021
10522
  "**/*.less"
10022
10523
  ];
10023
- var text5 = (value) => typeof value === "string" && value !== "" ? value : void 0;
10524
+ var text6 = (value) => typeof value === "string" && value !== "" ? value : void 0;
10024
10525
  var number = (value) => {
10025
10526
  const parsed = typeof value === "string" ? Number(value) : Number.NaN;
10026
10527
  return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
@@ -10037,8 +10538,8 @@ var parseLookConfig = (source) => {
10037
10538
  const top = configMap(tree);
10038
10539
  return {
10039
10540
  port: number(top["port"]),
10040
- start: text5(top["start"]),
10041
- host: text5(top["host"]) ?? "127.0.0.1",
10541
+ start: text6(top["start"]),
10542
+ host: text6(top["host"]) ?? "127.0.0.1",
10042
10543
  routes: configList(tree, "routes").length === 0 ? ["/"] : configList(tree, "routes"),
10043
10544
  mocks: configList(tree, "mocks"),
10044
10545
  screens: screens === void 0 ? DEFAULT_SCREENS : configList(tree, "screens"),
@@ -10047,18 +10548,18 @@ var parseLookConfig = (source) => {
10047
10548
  };
10048
10549
  };
10049
10550
  var loadLookConfig = (runtime, root) => {
10050
- const source = runtime.readText(toAbsolutePath(join17(root, LOOK_CONFIG)));
10551
+ const source = runtime.readText(toAbsolutePath(join19(root, LOOK_CONFIG)));
10051
10552
  return source.ok ? parseLookConfig(source.value) : void 0;
10052
10553
  };
10053
10554
  var withMock = (route, mock) => `${route}${route.includes("?") ? "&" : "?"}mock=${encodeURIComponent(mock)}`;
10054
10555
  var lookRoutes = (config) => config.routes.flatMap((route) => [route, ...config.mocks.map((mock) => withMock(route, mock))]);
10055
10556
 
10056
10557
  // src/look/paths.ts
10057
- import { join as join18, relative as relative5 } from "node:path";
10558
+ import { join as join20, relative as relative5 } from "node:path";
10058
10559
  var LOOK_DIR = "look";
10059
10560
  var SUMMARY_FILE = "summary.json";
10060
- var lookDir = (paths, treeHash2) => toAbsolutePath(join18(paths.dir, LOOK_DIR, treeHash2));
10061
- var summaryPath = (paths, treeHash2) => toAbsolutePath(join18(lookDir(paths, treeHash2), SUMMARY_FILE));
10561
+ var lookDir = (paths, treeHash2) => toAbsolutePath(join20(paths.dir, LOOK_DIR, treeHash2));
10562
+ var summaryPath = (paths, treeHash2) => toAbsolutePath(join20(lookDir(paths, treeHash2), SUMMARY_FILE));
10062
10563
  var projectRelative = (paths, absolute) => toPosixPath(relative5(paths.root, absolute));
10063
10564
 
10064
10565
  // src/gate/checks/look.ts
@@ -10076,8 +10577,8 @@ var findingNote = (value) => {
10076
10577
  ];
10077
10578
  };
10078
10579
  var readSummary = (ctx) => {
10079
- const text8 = ctx.runtime.readText(summaryPath(ctx.paths, ctx.treeHash));
10080
- const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
10580
+ const text9 = ctx.runtime.readText(summaryPath(ctx.paths, ctx.treeHash));
10581
+ const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
10081
10582
  if (!parsed?.ok) return void 0;
10082
10583
  const { at, captures, findings, ignoredAllows } = parsed.value;
10083
10584
  if (typeof at !== "string" || !Array.isArray(captures) || !Array.isArray(findings)) return void 0;
@@ -10093,7 +10594,7 @@ var readSummary = (ctx) => {
10093
10594
  var lastScreenEdit = (ctx) => {
10094
10595
  const screens = loadLookConfig(ctx.runtime, ctx.paths.root)?.screens ?? DEFAULT_SCREENS;
10095
10596
  return ctx.changes.filter((change) => change.status !== "D" && matchesAny(change.path, screens)).reduce((latest, change) => {
10096
- const stat = ctx.runtime.stat(toAbsolutePath(join19(ctx.paths.root, change.path)));
10597
+ const stat = ctx.runtime.stat(toAbsolutePath(join21(ctx.paths.root, change.path)));
10097
10598
  return Math.max(latest, stat?.mtimeMs ?? 0);
10098
10599
  }, 0);
10099
10600
  };
@@ -10140,14 +10641,14 @@ var manualCheck = (check2, ctx) => {
10140
10641
  };
10141
10642
 
10142
10643
  // src/gate/checks/placeholders.ts
10143
- import { join as join20 } from "node:path";
10644
+ import { join as join22 } from "node:path";
10144
10645
  var MAX_LISTED3 = 10;
10145
10646
  var SKIP_FILES = /\.(md|mdx|txt|rst|lock|snap|svg|png|jpg|gif|json)$|(^|\/)(CHANGELOG|LICENSE)/i;
10146
10647
  var PLACEHOLDER = /\b(TODO|FIXME|XXX)\b|not implemented|NotImplementedError|unimplemented!|todo!\(|lorem ipsum/i;
10147
10648
  var findPlaceholders = (lines3) => lines3.filter((line2) => !isTestFile(line2.path) && !SKIP_FILES.test(line2.path) && PLACEHOLDER.test(line2.text));
10148
10649
  var untrackedAdded = (ctx) => ctx.changes.filter((change) => change.status === "A" && !SKIP_FILES.test(change.path)).flatMap((change) => {
10149
- const text8 = ctx.runtime.readText(toAbsolutePath(join20(ctx.paths.root, change.path)));
10150
- return text8.ok ? text8.value.split("\n").map((line2, index) => ({ path: change.path, line: index + 1, text: line2 })) : [];
10650
+ const text9 = ctx.runtime.readText(toAbsolutePath(join22(ctx.paths.root, change.path)));
10651
+ return text9.ok ? text9.value.split("\n").map((line2, index) => ({ path: change.path, line: index + 1, text: line2 })) : [];
10151
10652
  });
10152
10653
  var placeholdersCheck = (_check, ctx) => {
10153
10654
  const tracked = addedLines(ctx.git, ctx.base);
@@ -10206,9 +10707,9 @@ var prOutcome = (check2, ctx) => {
10206
10707
 
10207
10708
  // src/prose/preset.ts
10208
10709
  var PRESET_LINE = /^preset:\s*([A-Za-z0-9_-]+)\s*$/m;
10209
- var presetFromConfig = (text8) => parsePreset(PRESET_LINE.exec(text8)?.[1]) ?? "neutral";
10710
+ var presetFromConfig = (text9) => parsePreset(PRESET_LINE.exec(text9)?.[1]) ?? "neutral";
10210
10711
  var projectPreset = (runtime, root) => {
10211
- const config = runtime.readText(toAbsolutePath(CONFIG_FILE, root));
10712
+ const config = runtime.readText(toAbsolutePath(CONFIG_FILE2, root));
10212
10713
  return config.ok ? presetFromConfig(config.value) : "neutral";
10213
10714
  };
10214
10715
 
@@ -10383,16 +10884,16 @@ var ANY_MARKER = /<!--\s*finitemonkey:prose-allow(?:-file)?\s*-->/g;
10383
10884
  var INLINE_CODE = /`[^`]*`/g;
10384
10885
  var FENCE2 = /^\s*(```|~~~)/;
10385
10886
  var outsideCode = (line2) => line2.replace(INLINE_CODE, "");
10386
- var unfencedLines = (text8) => {
10887
+ var unfencedLines = (text9) => {
10387
10888
  let fenced = false;
10388
- return text8.split("\n").filter((line2) => {
10889
+ return text9.split("\n").filter((line2) => {
10389
10890
  if (FENCE2.test(line2)) fenced = !fenced;
10390
10891
  return !fenced && !FENCE2.test(line2);
10391
10892
  });
10392
10893
  };
10393
- var allowsWholeFile = (text8) => unfencedLines(text8).some((line2) => FILE_MARKER.test(outsideCode(line2)));
10894
+ var allowsWholeFile = (text9) => unfencedLines(text9).some((line2) => FILE_MARKER.test(outsideCode(line2)));
10394
10895
  var allowsLine = (line2) => LINE_MARKER.test(outsideCode(line2));
10395
- var markerCount = (text8) => text8.match(ANY_MARKER)?.length ?? 0;
10896
+ var markerCount = (text9) => text9.match(ANY_MARKER)?.length ?? 0;
10396
10897
  var isMarkerOnlyLine = (line2) => allowsLine(line2) && outsideCode(line2).replace(LINE_MARKER, "").trim() === "";
10397
10898
 
10398
10899
  // src/prose/extract.ts
@@ -10412,7 +10913,7 @@ var MASKS = [
10412
10913
  [/<[^>]+>/g, ""],
10413
10914
  [/\*\*|__/g, ""]
10414
10915
  ];
10415
- var maskInline = (line2) => MASKS.reduce((text8, [pattern, replacement]) => text8.replace(pattern, replacement), line2);
10916
+ var maskInline = (line2) => MASKS.reduce((text9, [pattern, replacement]) => text9.replace(pattern, replacement), line2);
10416
10917
  var BlockReader = class {
10417
10918
  blocks = [];
10418
10919
  current;
@@ -10503,8 +11004,8 @@ var splitBlock = (block4) => {
10503
11004
  return joined.split(SENTENCE_END).flatMap((sentence) => {
10504
11005
  const at = joined.indexOf(sentence, offset);
10505
11006
  offset = at + sentence.length;
10506
- const text8 = sentence.trim();
10507
- return text8 === "" ? [] : [{ text: text8, line: lineAt(at) }];
11007
+ const text9 = sentence.trim();
11008
+ return text9 === "" ? [] : [{ text: text9, line: lineAt(at) }];
10508
11009
  });
10509
11010
  };
10510
11011
  var unitsOf = (blocks) => {
@@ -10525,9 +11026,9 @@ var unitsOf = (blocks) => {
10525
11026
  }));
10526
11027
  });
10527
11028
  };
10528
- var extractProse = (text8) => {
10529
- if (allowsWholeFile(text8)) return { units: [], allowed: true };
10530
- const lines3 = text8.split("\n").map(withoutCarriageReturn);
11029
+ var extractProse = (text9) => {
11030
+ if (allowsWholeFile(text9)) return { units: [], allowed: true };
11031
+ const lines3 = text9.split("\n").map(withoutCarriageReturn);
10531
11032
  return { units: unitsOf(readBlocks(lines3)), allowed: false };
10532
11033
  };
10533
11034
 
@@ -10652,7 +11153,7 @@ var CONNECTIVES = new Set(
10652
11153
  " "
10653
11154
  )
10654
11155
  );
10655
- var wordsOf = (text8) => text8.split(/\s+/).filter((word2) => WORD.test(word2));
11156
+ var wordsOf = (text9) => text9.split(/\s+/).filter((word2) => WORD.test(word2));
10656
11157
  var hit = (rule, units, index, weight) => {
10657
11158
  const unit = units[index];
10658
11159
  return { rule, unit: index, line: unit?.line ?? 0, weight, excerpt: unit?.text ?? "" };
@@ -10780,8 +11281,8 @@ var mergeSpans = (hits, windows) => {
10780
11281
  return ranges.map(([from, to]) => spanOf2(hits, from, to)).filter((span) => span.hits.length > 0);
10781
11282
  };
10782
11283
  var byPosition = (left, right) => left.unit - right.unit || left.line - right.line;
10783
- var scoreProse = (text8, options = {}) => {
10784
- const { units, allowed: allowed2 } = extractProse(text8);
11284
+ var scoreProse = (text9, options = {}) => {
11285
+ const { units, allowed: allowed2 } = extractProse(text9);
10785
11286
  const size = options.window ?? DENSITY_WINDOW;
10786
11287
  const threshold2 = options.threshold ?? DENSITY_THRESHOLD;
10787
11288
  const catalog = options.context === "record" ? [...phraseRules(), ...recordRules()] : phraseRules();
@@ -10813,9 +11314,9 @@ var changedMarkdown = (runtime, root) => {
10813
11314
  var scoreFiles = (runtime, root, files, forced) => {
10814
11315
  const reports = [];
10815
11316
  for (const file of files) {
10816
- const text8 = runtime.readText(toAbsolutePath(file, root));
10817
- if (!text8.ok) return reject(`${file}: ${text8.error}`);
10818
- reports.push({ file: toPosixPath(file), report: scoreProse(text8.value, { context: forced ?? contextOf(file) }) });
11317
+ const text9 = runtime.readText(toAbsolutePath(file, root));
11318
+ if (!text9.ok) return reject(`${file}: ${text9.error}`);
11319
+ reports.push({ file: toPosixPath(file), report: scoreProse(text9.value, { context: forced ?? contextOf(file) }) });
10819
11320
  }
10820
11321
  return accept(reports);
10821
11322
  };
@@ -10914,8 +11415,8 @@ var parseAppCheck = (stdout) => {
10914
11415
  var PROBE_MARGIN_MS = 15e3;
10915
11416
  var MIN_REMAINING_MS2 = 5e3;
10916
11417
  var TAIL2 = 160;
10917
- var lastLine = (text8) => {
10918
- const rows = text8.split(/\r?\n/).filter((row) => row.trim() !== "" && !/^\s*at\s|^Node\.js v\d/.test(row));
11418
+ var lastLine = (text9) => {
11419
+ const rows = text9.split(/\r?\n/).filter((row) => row.trim() !== "" && !/^\s*at\s|^Node\.js v\d/.test(row));
10919
11420
  const line2 = (rows.findLast((row) => /error/i.test(row)) ?? rows.at(-1))?.trim() ?? "";
10920
11421
  return line2.length > TAIL2 ? `${line2.slice(0, TAIL2)}…` : line2;
10921
11422
  };
@@ -10993,14 +11494,14 @@ ${run4.stderr}`)) })]);
10993
11494
  };
10994
11495
 
10995
11496
  // src/secrets/baseline.ts
10996
- import { join as join21 } from "node:path";
11497
+ import { join as join23 } from "node:path";
10997
11498
  var BASELINE_FILE2 = ".finitemonkey/findings/baseline.json";
10998
11499
  var BASELINE_SCHEMA = "finitemonkey/secrets-baseline@1";
10999
11500
  var BASELINE_STATES = ["aceito", "falso-positivo"];
11000
11501
  var EXCEPTION_MAX_DAYS = 90;
11001
11502
  var EMPTY_BASELINE = { schema: BASELINE_SCHEMA, entries: [] };
11002
- var baselinePath = (root) => toAbsolutePath(join21(root, BASELINE_FILE2));
11003
- var text6 = (record3, key) => {
11503
+ var baselinePath = (root) => toAbsolutePath(join23(root, BASELINE_FILE2));
11504
+ var text7 = (record3, key) => {
11004
11505
  const value = record3[key];
11005
11506
  return typeof value === "string" && value !== "" ? value : void 0;
11006
11507
  };
@@ -11008,7 +11509,7 @@ var stateOf = (value) => BASELINE_STATES.find((state) => state === value);
11008
11509
  var parseEntry2 = (value) => {
11009
11510
  if (!isRecord(value)) return void 0;
11010
11511
  const fields = ["id", "rule", "file", "fingerprint", "owner", "reason", "acceptedAt", "expiresAt"];
11011
- const [id, rule, file, fingerprint, owner, reason, acceptedAt, expiresAt] = fields.map((key) => text6(value, key));
11512
+ const [id, rule, file, fingerprint, owner, reason, acceptedAt, expiresAt] = fields.map((key) => text7(value, key));
11012
11513
  const state = stateOf(value["state"]);
11013
11514
  const line2 = value["line"];
11014
11515
  if (id === void 0 || rule === void 0 || file === void 0 || fingerprint === void 0) return void 0;
@@ -11059,14 +11560,14 @@ var withoutFixed = (baseline, fixed) => {
11059
11560
  var acceptedFingerprints = (baseline, now) => new Set(baseline.entries.filter((entry2) => entry2.expiresAt >= now).map((entry2) => entry2.fingerprint));
11060
11561
 
11061
11562
  // src/secrets/incidents.ts
11062
- import { join as join22 } from "node:path";
11563
+ import { join as join24 } from "node:path";
11063
11564
  var INCIDENTS_DIR = ".finitemonkey/incidents";
11064
11565
  var INCIDENT_STATES = ["aberto", "exposicao-local", "rotacionado", "fechado"];
11065
11566
  var INCIDENT_SOURCES = ["tool-output", "prompt", "history", "scan"];
11066
11567
  var ID = /^inc-[0-9a-f]{12}$/;
11067
11568
  var incidentId = (rule, fingerprint) => `inc-${sha256Hex(`${rule}\0${fingerprint}`).slice(0, 12)}`;
11068
- var incidentsDir = (root) => toAbsolutePath(join22(root, INCIDENTS_DIR));
11069
- var incidentFile = (root, id) => toAbsolutePath(join22(incidentsDir(root), `${id}.json`));
11569
+ var incidentsDir = (root) => toAbsolutePath(join24(root, INCIDENTS_DIR));
11570
+ var incidentFile = (root, id) => toAbsolutePath(join24(incidentsDir(root), `${id}.json`));
11070
11571
  var pick3 = (options, value) => options.find((option) => option === value);
11071
11572
  var str2 = (record3, key) => {
11072
11573
  const value = record3[key];
@@ -11121,7 +11622,7 @@ var listIncidents = (runtime, root) => {
11121
11622
  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));
11122
11623
  };
11123
11624
  var saveIncident = (runtime, root, incident) => {
11124
- const ignore = runtime.writeText(toAbsolutePath(join22(incidentsDir(root), ".gitignore")), "*\n");
11625
+ const ignore = runtime.writeText(toAbsolutePath(join24(incidentsDir(root), ".gitignore")), "*\n");
11125
11626
  if (!ignore.ok) return ignore;
11126
11627
  return runtime.writeText(incidentFile(root, incident.id), `${JSON.stringify(incident, null, 2)}
11127
11628
  `);
@@ -11168,7 +11669,7 @@ var moveIncident = (incident, to, now, by) => ({
11168
11669
  var isRotated = (incident) => incident.state === "rotacionado" || incident.state === "fechado";
11169
11670
 
11170
11671
  // src/secrets/scan.ts
11171
- import { join as join23, relative as relative6 } from "node:path";
11672
+ import { join as join25, relative as relative6 } from "node:path";
11172
11673
 
11173
11674
  // src/secrets/located.ts
11174
11675
  var findingId = (ruleId, file, fingerprint) => `fm-${sha256Hex(`${ruleId}\0${file}\0${fingerprint}`).slice(0, 16)}`;
@@ -11258,7 +11759,7 @@ var diffScanner = () => {
11258
11759
  var MAX_FILE_BYTES = 2 * 1024 * 1024;
11259
11760
  var SKIPPED = /(?:^|\/)(?:\.git|node_modules|\.finitemonkey)\//;
11260
11761
  var DIFF_FLAGS = ["-U0", "--no-color", "--no-ext-diff", "--no-renames"];
11261
- var binary = (text8) => text8.slice(0, 8e3).includes("\0");
11762
+ var binary = (text9) => text9.slice(0, 8e3).includes("\0");
11262
11763
  var listed2 = (runtime, root, target2) => {
11263
11764
  const git2 = runtime.git(["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", target2], root);
11264
11765
  if (git2.ok)
@@ -11268,12 +11769,12 @@ var listed2 = (runtime, root, target2) => {
11268
11769
  const base = toAbsolutePath(target2, root);
11269
11770
  const walked = runtime.walkFiles(base);
11270
11771
  if (!walked.ok) return walked;
11271
- return accept(walked.value.map((name) => toPosixPath(relative6(root, join23(base, toPosixPath(name))))));
11772
+ return accept(walked.value.map((name) => toPosixPath(relative6(root, join25(base, toPosixPath(name))))));
11272
11773
  };
11273
11774
  var scanFile = (runtime, root, file) => {
11274
- const text8 = runtime.readText(toAbsolutePath(file, root));
11275
- if (!text8.ok || text8.value.length > MAX_FILE_BYTES || binary(text8.value)) return [];
11276
- return scanText(text8.value, { path: file }).map((finding4) => locate(finding4, file, finding4.line));
11775
+ const text9 = runtime.readText(toAbsolutePath(file, root));
11776
+ if (!text9.ok || text9.value.length > MAX_FILE_BYTES || binary(text9.value)) return [];
11777
+ return scanText(text9.value, { path: file }).map((finding4) => locate(finding4, file, finding4.line));
11277
11778
  };
11278
11779
  var scanPath = (runtime, root, target2) => {
11279
11780
  const files = listed2(runtime, root, target2);
@@ -11338,7 +11839,7 @@ var secretsCheck = (_check, ctx) => {
11338
11839
  };
11339
11840
 
11340
11841
  // src/gate/checks/tests.ts
11341
- import { join as join24 } from "node:path";
11842
+ import { join as join26 } from "node:path";
11342
11843
 
11343
11844
  // src/guards/tests/tree.ts
11344
11845
  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 }] : [];
@@ -11370,8 +11871,8 @@ var treeViolations = (ctx) => {
11370
11871
  changes: relevant,
11371
11872
  before: (path) => fileAt(ctx.git, ctx.base, path),
11372
11873
  after: (path) => {
11373
- const text8 = ctx.runtime.readText(toAbsolutePath(join24(ctx.paths.root, path)));
11374
- return text8.ok ? text8.value : void 0;
11874
+ const text9 = ctx.runtime.readText(toAbsolutePath(join26(ctx.paths.root, path)));
11875
+ return text9.ok ? text9.value : void 0;
11375
11876
  },
11376
11877
  acceptedConfigs: ctx.trust.configs,
11377
11878
  scopePaths: ctx.contract.scope.paths,
@@ -11391,7 +11892,7 @@ var testsCheck = (check2, ctx) => {
11391
11892
  };
11392
11893
 
11393
11894
  // src/git/tree.ts
11394
- import { join as join25 } from "node:path";
11895
+ import { join as join27 } from "node:path";
11395
11896
 
11396
11897
  // src/secrets/guards/paths.ts
11397
11898
  var SAFE_ENV = /^\.env\.(?:example|sample|template|dist|defaults|schema)$/i;
@@ -11430,7 +11931,7 @@ var dropSensitive = (runtime, repo, env, options) => {
11430
11931
  return removed.ok ? accept(void 0) : removed;
11431
11932
  };
11432
11933
  var withTempIndex = (runtime, repo, options, work) => {
11433
- const path = toAbsolutePath(join25(repo.gitDir, `finitemonkey-${options.stamp}.index`));
11934
+ const path = toAbsolutePath(join27(repo.gitDir, `finitemonkey-${options.stamp}.index`));
11434
11935
  const env = { GIT_INDEX_FILE: path, GIT_OPTIONAL_LOCKS: "0" };
11435
11936
  try {
11436
11937
  return work(env);
@@ -11535,10 +12036,10 @@ var bodyLine = (draft, line2) => {
11535
12036
  else if (line2.startsWith("+")) draft.current.added.push(line2.slice(1));
11536
12037
  };
11537
12038
  var DIFF_GIT = /^diff --git a\/(.+) b\/(.+)$/;
11538
- var parseUnifiedDiff = (text8) => {
12039
+ var parseUnifiedDiff = (text9) => {
11539
12040
  const out = [];
11540
12041
  let draft;
11541
- for (const raw of text8.split("\n")) {
12042
+ for (const raw of text9.split("\n")) {
11542
12043
  const line2 = withoutCarriageReturn(raw);
11543
12044
  const start = DIFF_GIT.exec(line2);
11544
12045
  if (start !== null) {
@@ -11637,59 +12138,15 @@ var mergeCommitsSince = (runtime, repo, since) => {
11637
12138
  };
11638
12139
  var mergeInProgress = (runtime, repo) => revParse(runtime, repo, "MERGE_HEAD") !== void 0;
11639
12140
 
11640
- // src/git/facts.ts
11641
- var memo2 = (compute) => {
11642
- let cached3;
11643
- return () => {
11644
- cached3 ??= { value: compute() };
11645
- return cached3.value;
11646
- };
11647
- };
11648
- var memoBy = (compute) => {
11649
- const cache = /* @__PURE__ */ new Map();
11650
- return (key) => {
11651
- const hit3 = cache.get(key);
11652
- if (hit3 !== void 0) return hit3.value;
11653
- const value = compute(key);
11654
- cache.set(key, { value });
11655
- return value;
11656
- };
11657
- };
11658
- var STATE_PREFIX = ".finitemonkey/";
11659
- var PORCELAIN_FIELDS = { ordinary: 8, renamed: 9, unmerged: 10 };
11660
- var entryOf2 = (kind, xy, path) => ({
11661
- path,
11662
- kind,
11663
- staged: kind !== "untracked" && !xy.startsWith("."),
11664
- unstaged: kind === "untracked" || xy.charAt(1) !== "."
11665
- });
11666
- var afterFields = (record3, count4) => record3.split(" ").slice(count4).join(" ");
11667
- var parsePorcelainV2 = (stdout) => {
11668
- const records = stdout.split("\0");
11669
- const out = [];
11670
- for (let index = 0; index < records.length; index += 1) {
11671
- const record3 = records[index] ?? "";
11672
- const xy = record3.split(" ")[1] ?? "..";
11673
- if (record3.startsWith("1 ")) out.push(entryOf2("changed", xy, afterFields(record3, PORCELAIN_FIELDS.ordinary)));
11674
- else if (record3.startsWith("2 ")) {
11675
- out.push(entryOf2("changed", xy, afterFields(record3, PORCELAIN_FIELDS.renamed)));
11676
- index += 1;
11677
- } else if (record3.startsWith("u ")) {
11678
- out.push(entryOf2("unmerged", xy, afterFields(record3, PORCELAIN_FIELDS.unmerged)));
11679
- } else if (record3.startsWith("? ")) out.push(entryOf2("untracked", "??", record3.slice(2)));
11680
- }
11681
- return out.filter((entry2) => entry2.path !== "" && !entry2.path.startsWith(STATE_PREFIX));
11682
- };
11683
-
11684
12141
  // src/git/merge/dropped.ts
11685
- import { join as join26 } from "node:path";
12142
+ import { join as join28 } from "node:path";
11686
12143
 
11687
12144
  // src/git/merge/lines.ts
11688
12145
  var normalizeLine = (line2) => line2.replace(/\r$/, "").trim().replace(/\s*,$/, "");
11689
12146
  var WEAK = /^[\p{P}\p{S}\s]+$/u;
11690
12147
  var isWeak = (line2) => WEAK.test(line2);
11691
12148
  var meaningful = (lines3) => lines3.map(normalizeLine).filter((line2) => line2 !== "");
11692
- var contentLines = (text8) => text8 === void 0 ? [] : meaningful(text8.split("\n"));
12149
+ var contentLines = (text9) => text9 === void 0 ? [] : meaningful(text9.split("\n"));
11693
12150
  var countLines = (lines3) => {
11694
12151
  const counts = /* @__PURE__ */ new Map();
11695
12152
  for (const line2 of lines3) counts.set(line2, (counts.get(line2) ?? 0) + 1);
@@ -11738,16 +12195,16 @@ var itemOf = (value) => {
11738
12195
  approvedBy: optionalString(value, "approved_by")
11739
12196
  };
11740
12197
  };
11741
- var invalidFinding = (file, text8) => ({
12198
+ var invalidFinding = (file, text9) => ({
11742
12199
  kind: "invalid-dropped",
11743
12200
  file,
11744
12201
  side: void 0,
11745
12202
  line: void 0,
11746
- text: text8,
12203
+ text: text9,
11747
12204
  weak: false
11748
12205
  });
11749
- var parseDropped = (text8) => {
11750
- const parsed = parseJson(text8);
12206
+ var parseDropped = (text9) => {
12207
+ const parsed = parseJson(text9);
11751
12208
  if (!parsed.ok) return parsed;
11752
12209
  const list4 = isRecord(parsed.value) ? parsed.value["dropped"] : parsed.value;
11753
12210
  if (!Array.isArray(list4)) return reject("expected a list of dropped items");
@@ -11762,17 +12219,17 @@ var parseDropped = (text8) => {
11762
12219
  return accept({ items, invalid });
11763
12220
  };
11764
12221
  var loadDropped = (runtime, root, file) => {
11765
- const path = toAbsolutePath(file ?? join26(root, DROPPED_FILE), root);
11766
- const text8 = runtime.readText(path);
11767
- if (!text8.ok) return file === void 0 ? accept({ items: [], invalid: [] }) : reject(`${file}: ${text8.error}`);
11768
- return parseDropped(text8.value);
12222
+ const path = toAbsolutePath(file ?? join28(root, DROPPED_FILE), root);
12223
+ const text9 = runtime.readText(path);
12224
+ if (!text9.ok) return file === void 0 ? accept({ items: [], invalid: [] }) : reject(`${file}: ${text9.error}`);
12225
+ return parseDropped(text9.value);
11769
12226
  };
11770
12227
  var isDropped = (finding4, items) => items.some(
11771
12228
  (item2) => item2.file === finding4.file && (item2.side === void 0 || finding4.side === void 0 || item2.side === finding4.side) && (item2.text === void 0 || normalizeLine(item2.text) === finding4.text)
11772
12229
  );
11773
12230
 
11774
12231
  // src/git/merge/generated.ts
11775
- import { join as join27 } from "node:path";
12232
+ import { join as join29 } from "node:path";
11776
12233
  var LOCKFILES3 = /* @__PURE__ */ new Set([
11777
12234
  "package-lock.json",
11778
12235
  "npm-shrinkwrap.json",
@@ -11790,11 +12247,11 @@ var LOCKFILES3 = /* @__PURE__ */ new Set([
11790
12247
  ]);
11791
12248
  var GENERATOR_TIMEOUT_MS = 3e5;
11792
12249
  var DOUBLE_STAR = "";
11793
- var globRegex = (pattern) => new RegExp(
12250
+ var globRegex2 = (pattern) => new RegExp(
11794
12251
  `^${pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").split("**").join(DOUBLE_STAR).replace(/\*/g, "[^/]*").split(DOUBLE_STAR).join(".*")}$`
11795
12252
  );
11796
12253
  var generatorFor = (path, generators) => Object.entries(generators).find(
11797
- ([pattern]) => globRegex(pattern).test(path) || globRegex(pattern).test(path.split("/").pop() ?? "")
12254
+ ([pattern]) => globRegex2(pattern).test(path) || globRegex2(pattern).test(path.split("/").pop() ?? "")
11798
12255
  )?.[1];
11799
12256
  var generatedPaths = (runtime, repo, paths, generators) => {
11800
12257
  const out = new Set(
@@ -11835,7 +12292,7 @@ var verifyGenerated = (runtime, repo, inputs, paths, generators) => {
11835
12292
  if (declared.length === 0) return accept([]);
11836
12293
  const commit2 = resultCommit(runtime, repo, inputs);
11837
12294
  if (!commit2.ok) return commit2;
11838
- const dir = join27(repo.gitDir, `finitemonkey-generate-${runtime.now().replace(/[:.]/g, "-")}`);
12295
+ const dir = join29(repo.gitDir, `finitemonkey-generate-${runtime.now().replace(/[:.]/g, "-")}`);
11839
12296
  if (!runtime.git(["worktree", "add", "--detach", dir, commit2.value], repo.root).ok)
11840
12297
  return reject("could not check out the merge result");
11841
12298
  try {
@@ -11845,7 +12302,7 @@ var verifyGenerated = (runtime, repo, inputs, paths, generators) => {
11845
12302
  }
11846
12303
  return accept(
11847
12304
  declared.flatMap(({ path }) => {
11848
- const fresh3 = runtime.readText(toAbsolutePath(join27(dir, path)));
12305
+ const fresh3 = runtime.readText(toAbsolutePath(join29(dir, path)));
11849
12306
  const merged2 = blobText(runtime, repo, commit2.value, path);
11850
12307
  if (fresh3.ok && fresh3.value === merged2) return [];
11851
12308
  return [{ kind: "generated-differs", file: path, side: void 0, line: void 0, text: path, weak: false }];
@@ -11888,13 +12345,13 @@ var removalOf = (ours, theirs, paired) => {
11888
12345
  var missingAdditions = (hunks, file, elsewhere) => {
11889
12346
  const present2 = new Set(file.result);
11890
12347
  return hunks.flatMap(({ side, hunk, added }) => {
11891
- const text8 = added.find((line2) => !isWeak(line2) && !present2.has(line2));
11892
- if (text8 === void 0 || containsSequence(file.result, added) || elsewhere(added)) return [];
12348
+ const text9 = added.find((line2) => !isWeak(line2) && !present2.has(line2));
12349
+ if (text9 === void 0 || containsSequence(file.result, added) || elsewhere(added)) return [];
11893
12350
  const offset = Math.max(
11894
12351
  0,
11895
- hunk.added.findIndex((line2) => normalizeLine(line2) === text8)
12352
+ hunk.added.findIndex((line2) => normalizeLine(line2) === text9)
11896
12353
  );
11897
- return [{ kind: "missing-addition", file: file.path, side, line: hunk.newStart + offset, text: text8, weak: false }];
12354
+ return [{ kind: "missing-addition", file: file.path, side, line: hunk.newStart + offset, text: text9, weak: false }];
11898
12355
  });
11899
12356
  };
11900
12357
  var expectations = (file, hunks, removal) => {
@@ -11967,8 +12424,8 @@ var symbolsOf = (line2) => {
11967
12424
  ...title === void 0 ? [] : [{ name: title, test: true }]
11968
12425
  ];
11969
12426
  };
11970
- var escapeRegex = (text8) => text8.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
11971
- var containsSymbol = (text8, symbol) => symbol.test ? text8.includes(symbol.name) : new RegExp(`(?<![\\w$])${escapeRegex(symbol.name)}(?![\\w$])`).test(text8);
12427
+ var escapeRegex = (text9) => text9.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12428
+ var containsSymbol = (text9, symbol) => symbol.test ? text9.includes(symbol.name) : new RegExp(`(?<![\\w$])${escapeRegex(symbol.name)}(?![\\w$])`).test(text9);
11972
12429
  var missingSymbols = (file, side, hunks, present2) => {
11973
12430
  const seen = /* @__PURE__ */ new Set();
11974
12431
  return hunks.flatMap(
@@ -11987,12 +12444,12 @@ var created2 = (diff) => diff?.status === "added" || diff?.status === "copied";
11987
12444
  var resultPathOf = (context, pair) => [pair.ours?.newPath, pair.theirs?.newPath, pair.key].find(
11988
12445
  (path) => path !== void 0 && context.resultText(path) !== void 0
11989
12446
  );
11990
- var finding2 = (kind, file, side, text8) => ({
12447
+ var finding2 = (kind, file, side, text9) => ({
11991
12448
  kind,
11992
12449
  file,
11993
12450
  side,
11994
12451
  line: void 0,
11995
- text: text8,
12452
+ text: text9,
11996
12453
  weak: false
11997
12454
  });
11998
12455
  var binaryFindings = (context, pair, path) => {
@@ -12011,8 +12468,8 @@ var binaryFindings = (context, pair, path) => {
12011
12468
  return result === expected ? [] : [finding2("binary", file, side, file)];
12012
12469
  };
12013
12470
  var elsewhereOf = (context) => (lines3) => context.resultFiles().some((path) => containsSequence(contentLines(context.resultText(path)), lines3));
12014
- var symbolFindings = (context, pair, path, text8) => {
12015
- const present2 = (symbol) => text8 !== void 0 && containsSymbol(text8, symbol) || context.resultFiles().some((other) => containsSymbol(context.resultText(other) ?? "", symbol));
12471
+ var symbolFindings = (context, pair, path, text9) => {
12472
+ const present2 = (symbol) => text9 !== void 0 && containsSymbol(text9, symbol) || context.resultFiles().some((other) => containsSymbol(context.resultText(other) ?? "", symbol));
12016
12473
  return [
12017
12474
  ...missingSymbols(path, "ours", pair.ours?.hunks ?? [], present2),
12018
12475
  ...missingSymbols(path, "theirs", pair.theirs?.hunks ?? [], present2)
@@ -12028,19 +12485,19 @@ var verifyFile = (context, pair) => {
12028
12485
  const copied = [pair.ours, pair.theirs].find((diff) => diff?.status === "copied");
12029
12486
  const baseSource = copied?.oldPath ?? (created2(pair.ours) || created2(pair.theirs) ? void 0 : pair.key);
12030
12487
  const baseText = baseSource === void 0 ? void 0 : blobText(context.runtime, context.repo, context.inputs.base, baseSource);
12031
- const text8 = path === void 0 ? void 0 : context.resultText(path);
12488
+ const text9 = path === void 0 ? void 0 : context.resultText(path);
12032
12489
  const file = path ?? pair.key;
12033
12490
  const lines3 = checkFile(
12034
12491
  {
12035
12492
  path: file,
12036
12493
  base: contentLines(baseText),
12037
- result: contentLines(text8),
12494
+ result: contentLines(text9),
12038
12495
  ours: pair.ours?.hunks ?? [],
12039
12496
  theirs: pair.theirs?.hunks ?? []
12040
12497
  },
12041
12498
  elsewhereOf(context)
12042
12499
  );
12043
- return [...lines3, ...symbolFindings(context, pair, file, text8)];
12500
+ return [...lines3, ...symbolFindings(context, pair, file, text9)];
12044
12501
  };
12045
12502
 
12046
12503
  // src/git/merge/verify.ts
@@ -12069,13 +12526,13 @@ var pairsOf = (ours, theirs) => {
12069
12526
  }));
12070
12527
  };
12071
12528
  var contextOf2 = (runtime, repo, inputs, pairs2) => {
12072
- const resultText = memoBy((path) => blobText(runtime, repo, inputs.result, path));
12529
+ const resultText2 = memoBy((path) => blobText(runtime, repo, inputs.result, path));
12073
12530
  const context = {
12074
12531
  runtime,
12075
12532
  repo,
12076
12533
  inputs,
12077
- resultText,
12078
- resultFiles: memo2(
12534
+ resultText: resultText2,
12535
+ resultFiles: memo(
12079
12536
  () => pairs2.flatMap((pair) => {
12080
12537
  const path = resultPathOf(context, pair);
12081
12538
  return path === void 0 ? [] : [path];
@@ -12131,19 +12588,19 @@ var verifyMerge = (runtime, repo, options = {}) => {
12131
12588
  };
12132
12589
 
12133
12590
  // src/git/ownership.ts
12134
- import { join as join28 } from "node:path";
12591
+ import { join as join30 } from "node:path";
12135
12592
  var OWNERSHIP_DIR = ".finitemonkey/git";
12136
12593
  var ownerKey = (raw) => {
12137
12594
  const cleaned = (raw ?? "").replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 80);
12138
12595
  return cleaned === "" ? "unknown" : cleaned;
12139
12596
  };
12140
- var fileOf = (repo, kind, owner) => toAbsolutePath(join28(repo.root, OWNERSHIP_DIR, kind, `${ownerKey(owner)}.json`));
12597
+ var fileOf = (repo, kind, owner) => toAbsolutePath(join30(repo.root, OWNERSHIP_DIR, kind, `${ownerKey(owner)}.json`));
12141
12598
  var readDirty = (runtime, root) => {
12142
12599
  const status = runtime.git(["status", "--porcelain=v2", "-z", "--untracked-files=all"], root);
12143
12600
  return status.ok ? parsePorcelainV2(status.stdout) : void 0;
12144
12601
  };
12145
12602
  var hashDirty = (runtime, repo, entries) => {
12146
- const present2 = entries.filter((entry2) => runtime.stat(toAbsolutePath(join28(repo.root, entry2.path)))?.kind === "file");
12603
+ const present2 = entries.filter((entry2) => runtime.stat(toAbsolutePath(join30(repo.root, entry2.path)))?.kind === "file");
12147
12604
  const hashed = present2.length === 0 ? void 0 : runtime.gitWith(["hash-object", "--stdin-paths"], repo.root, {
12148
12605
  input: `${present2.map((entry2) => entry2.path).join("\n")}
12149
12606
  `
@@ -12160,8 +12617,8 @@ var hashesOf = (value) => isRecord(value) ? Object.fromEntries(
12160
12617
  Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
12161
12618
  ) : {};
12162
12619
  var readObject = (runtime, path) => {
12163
- const text8 = runtime.readText(path);
12164
- return text8.ok ? parseJsonObject(text8.value) : text8;
12620
+ const text9 = runtime.readText(path);
12621
+ return text9.ok ? parseJsonObject(text9.value) : text9;
12165
12622
  };
12166
12623
  var readBaseline2 = (runtime, repo, owner) => {
12167
12624
  const parsed = readObject(runtime, fileOf(repo, "baseline", owner));
@@ -12227,7 +12684,7 @@ var foreignFiles = (runtime, repo, owner, targets2) => {
12227
12684
  };
12228
12685
 
12229
12686
  // src/git/gate-status.ts
12230
- var lines = (text8) => text8.split("\n").filter((line2) => line2.trim() !== "");
12687
+ var lines = (text9) => text9.split("\n").filter((line2) => line2.trim() !== "");
12231
12688
  var unpushedSince = (runtime, repo, since) => {
12232
12689
  if (lines(runtime.git(["remote"], repo.root).stdout).length === 0) return [];
12233
12690
  const result = runtime.git(["rev-list", `${since}..HEAD`, "--not", "--remotes"], repo.root);
@@ -12255,97 +12712,17 @@ var findingLabel = (finding4) => {
12255
12712
  return `${where} ${finding4.text === "" ? finding4.kind : `"${finding4.text}"`}`;
12256
12713
  };
12257
12714
 
12258
- // src/git/repo.ts
12259
- var DEFAULT_PROTECTED = ["main", "master", "release/*", "prod", "production", "rc-*"];
12260
- var openRepo = (runtime, dir) => {
12261
- const result = runtime.git(["rev-parse", "--show-toplevel", "--absolute-git-dir", "--show-prefix"], dir);
12262
- if (!result.ok) return void 0;
12263
- const [root, gitDir, prefix] = result.stdout.split("\n");
12264
- if (root === void 0 || root === "" || gitDir === void 0 || gitDir === "") return void 0;
12265
- return {
12266
- root: toAbsolutePath(root),
12267
- gitDir: toAbsolutePath(gitDir),
12268
- prefix: (prefix ?? "").replace(/\/$/, "")
12269
- };
12270
- };
12271
- var globRegex2 = (pattern) => new RegExp(`^${pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`);
12272
- var matchesBranch = (branch, patterns) => patterns.some((pattern) => globRegex2(pattern).test(branch));
12273
- var line = (runtime, root, args2) => {
12274
- const result = runtime.git(args2, root);
12275
- const value = result.stdout.trim();
12276
- return result.ok && value !== "" ? value : void 0;
12277
- };
12278
- var refExists = (runtime, root, ref) => runtime.git(["show-ref", "--verify", "--quiet", ref], root).ok;
12279
- var defaultBranchOf = (runtime, root) => {
12280
- const remote = line(runtime, root, ["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"]);
12281
- if (remote !== void 0) return remote.replace(/^[^/]+\//, "");
12282
- const configured = line(runtime, root, ["config", "--get", "init.defaultBranch"]);
12283
- if (configured !== void 0 && refExists(runtime, root, `refs/heads/${configured}`)) return configured;
12284
- return refExists(runtime, root, "refs/heads/master") && !refExists(runtime, root, "refs/heads/main") ? "master" : "main";
12285
- };
12286
- var upstreamOf = (runtime, root, branch) => {
12287
- const remote = line(runtime, root, ["config", "--get", `branch.${branch}.remote`]);
12288
- const merge = line(runtime, root, ["config", "--get", `branch.${branch}.merge`]);
12289
- if (remote === void 0 || merge === void 0 || remote === ".") return void 0;
12290
- const name = merge.replace(/^refs\/heads\//, "");
12291
- return { branch: name, remoteRef: `refs/remotes/${remote}/${name}` };
12292
- };
12293
- var integrationRefs = (runtime, root, defaultBranch) => [`refs/heads/${defaultBranch}`, `refs/remotes/origin/${defaultBranch}`].filter(
12294
- (ref) => refExists(runtime, root, ref)
12295
- );
12296
- var contained = (runtime, root, branch, targets2) => {
12297
- const tip = line(runtime, root, ["rev-parse", "--verify", "--quiet", `refs/heads/${branch}`]);
12298
- if (tip === void 0) return true;
12299
- return targets2.filter((target2) => target2 !== `refs/heads/${branch}`).some((target2) => runtime.git(["merge-base", "--is-ancestor", tip, target2], root).ok);
12300
- };
12301
- var shared = (runtime, root, upstream, base) => {
12302
- if (upstream === void 0 || !refExists(runtime, root, upstream.remoteRef)) return false;
12303
- const own = /<([^>]*)>/.exec(line(runtime, root, ["var", "GIT_AUTHOR_IDENT"]) ?? "")?.[1] ?? "";
12304
- const log = runtime.git(["log", "--format=%ae", upstream.remoteRef, ...base.map((ref) => `^${ref}`)], root);
12305
- return log.ok && log.stdout.split("\n").some((email) => email.trim() !== "" && email.trim() !== own);
12306
- };
12307
- var protectedPatterns = (config, defaultBranch) => [
12308
- defaultBranch,
12309
- ...DEFAULT_PROTECTED,
12310
- ...configList(config, "git", "protected")
12311
- ];
12312
- var repoFacts = (runtime, repo, config = loadProjectConfig(runtime, repo.root)) => {
12313
- const { root } = repo;
12314
- const defaultBranch = memo2(() => defaultBranchOf(runtime, root));
12315
- const patterns = memo2(() => protectedPatterns(config, defaultBranch()));
12316
- const upstream = memoBy((branch) => upstreamOf(runtime, root, branch));
12317
- const integration = memo2(() => integrationRefs(runtime, root, defaultBranch()));
12318
- return {
12319
- prefix: repo.prefix,
12320
- currentBranch: memo2(() => line(runtime, root, ["symbolic-ref", "--quiet", "--short", "HEAD"])),
12321
- defaultBranch,
12322
- isProtected: (branch) => matchesBranch(branch, patterns()),
12323
- dirty: memo2(() => {
12324
- const status = runtime.git(["status", "--porcelain=v2", "-z", "--untracked-files=all"], root);
12325
- return status.ok ? parsePorcelainV2(status.stdout) : void 0;
12326
- }),
12327
- isRef: memoBy((name) => runtime.git(["rev-parse", "--verify", "--quiet", `${name}^{commit}`], root).ok),
12328
- branchExists: memoBy((name) => refExists(runtime, root, `refs/heads/${name}`)),
12329
- branchContained: memoBy((branch) => {
12330
- const up = upstream(branch);
12331
- return contained(runtime, root, branch, [...up === void 0 ? [] : [up.remoteRef], ...integration()]);
12332
- }),
12333
- upstream,
12334
- sharedBranch: memoBy((branch) => shared(runtime, root, upstream(branch), integration()))
12335
- };
12336
- };
12337
-
12338
12715
  // src/gate/checks/worktree.ts
12339
12716
  var MAX_LISTED7 = 8;
12340
12717
  var listed3 = (items) => {
12341
12718
  const head = items.slice(0, MAX_LISTED7).join(", ");
12342
12719
  return items.length > MAX_LISTED7 ? `${head} (+${String(items.length - MAX_LISTED7)})` : head;
12343
12720
  };
12344
- var repoOf = (ctx) => openRepo(ctx.runtime, ctx.paths.root);
12721
+ var repoOf2 = (ctx) => openRepo(ctx.runtime, ctx.paths.root);
12345
12722
  var ownerOf = (ctx) => ownerKey(ctx.sessionId);
12346
12723
  var declaresDestination = (ctx) => ctx.contract.acceptance.some((check2) => check2.kind === "git" && (check2.require ?? []).includes("pushed"));
12347
12724
  var worktreeCheck = (_check, ctx) => {
12348
- const repo = repoOf(ctx);
12725
+ const repo = repoOf2(ctx);
12349
12726
  if (repo === void 0) return unverified("tool_missing", [note("gate.worktree.unavailable")]);
12350
12727
  const status = worktreeGateStatus(ctx.runtime, repo, ownerOf(ctx));
12351
12728
  const unpushed = declaresDestination(ctx) ? [] : status.unpushed;
@@ -12356,7 +12733,7 @@ var worktreeCheck = (_check, ctx) => {
12356
12733
  return notes2.length > 0 ? failed(notes2) : passed([note("gate.worktree.clean")]);
12357
12734
  };
12358
12735
  var mergeCheck = (_check, ctx) => {
12359
- const repo = repoOf(ctx);
12736
+ const repo = repoOf2(ctx);
12360
12737
  if (repo === void 0) return unverified("tool_missing", [note("gate.merge.unavailable")]);
12361
12738
  const items = mergeGateStatus(ctx.runtime, repo, ownerOf(ctx));
12362
12739
  const short3 = (commit2) => commit2 === "MERGE_HEAD" ? commit2 : commit2.slice(0, 12);
@@ -12467,15 +12844,15 @@ var runGate = (options, registry = DEFAULT_CHECKS) => {
12467
12844
  if (ready.kind !== "ready") return ready;
12468
12845
  const { prepared } = ready;
12469
12846
  const path = prepared.ctx.paths.evidence(prepared.ctx.contract.id);
12470
- const before = readLedger(options.runtime, path);
12847
+ const before = readLedger2(options.runtime, path);
12471
12848
  if (options.fast && fastPathHit(prepared, before)) return { kind: "done", report: report(prepared, before, true) };
12472
12849
  const fresh3 = runChecks(prepared.ctx, before, registry);
12473
12850
  if (!appendEvidence(options.runtime, path, fresh3)) return { kind: "failed", failure: { kind: "ledger_unwritable" } };
12474
- const withChecks = readLedger(options.runtime, path);
12851
+ const withChecks = readLedger2(options.runtime, path);
12475
12852
  if (!appendEvidence(options.runtime, path, [summaryEntry(prepared, withChecks)])) {
12476
12853
  return { kind: "failed", failure: { kind: "ledger_unwritable" } };
12477
12854
  }
12478
- return { kind: "done", report: report(prepared, readLedger(options.runtime, path), false) };
12855
+ return { kind: "done", report: report(prepared, readLedger2(options.runtime, path), false) };
12479
12856
  };
12480
12857
 
12481
12858
  // src/gate/hooks/stop.ts
@@ -12593,7 +12970,7 @@ var subagentStopGate = (input) => {
12593
12970
  };
12594
12971
 
12595
12972
  // src/gate/hooks/subagent.ts
12596
- import { join as join29 } from "node:path";
12973
+ import { join as join31 } from "node:path";
12597
12974
  var NO_CONTRACT_AGENTS = /(^|:)(reviewer|verifier|explore|plan|claude-code-guide|statusline-setup)$/i;
12598
12975
  var KNOWN_KINDS = /* @__PURE__ */ new Set([
12599
12976
  "command",
@@ -12631,12 +13008,12 @@ var subagentStart = (input) => {
12631
13008
  const active = readState(runtime, paths).active;
12632
13009
  if (active === void 0) return { kind: "allow" };
12633
13010
  const disk = (relative11) => {
12634
- const text8 = runtime.readText(toAbsolutePath(join29(paths.root, relative11)));
12635
- return text8.ok ? text8.value : void 0;
13011
+ const text9 = runtime.readText(toAbsolutePath(join31(paths.root, relative11)));
13012
+ return text9.ok ? text9.value : void 0;
12636
13013
  };
12637
13014
  const loaded = loadTask(runtime, paths, { disk, base: () => void 0 }, active);
12638
13015
  if (!loaded.ok) return { kind: "allow" };
12639
- const briefing = runtime.stat(toAbsolutePath(join29(paths.dir, "briefing.md"))) !== void 0;
13016
+ const briefing = runtime.stat(toAbsolutePath(join31(paths.dir, "briefing.md"))) !== void 0;
12640
13017
  return { kind: "allow", output: contextOutput("SubagentStart", contractFacts(t, loaded.value.contract, briefing)) };
12641
13018
  };
12642
13019
 
@@ -12654,12 +13031,12 @@ var PORTAO_HANDLERS = {
12654
13031
  };
12655
13032
 
12656
13033
  // src/prod/guard.ts
12657
- import { join as join31 } from "node:path";
13034
+ import { join as join33 } from "node:path";
12658
13035
 
12659
13036
  // src/git/command-text.ts
12660
13037
  var MAX_SHOWN = 160;
12661
- var shown = (text8) => {
12662
- const flat = text8.replace(/\s+/g, " ").trim();
13038
+ var shown = (text9) => {
13039
+ const flat = text9.replace(/\s+/g, " ").trim();
12663
13040
  return flat.length > MAX_SHOWN ? `${flat.slice(0, MAX_SHOWN - 3)}...` : flat;
12664
13041
  };
12665
13042
  var commandText = (command) => shown([command.program, ...command.args].join(" "));
@@ -12996,7 +13373,7 @@ var remoteHostsOf = (command) => {
12996
13373
  };
12997
13374
 
12998
13375
  // src/prod/targets.ts
12999
- import { join as join30 } from "node:path";
13376
+ import { join as join32 } from "node:path";
13000
13377
  var DEPLOY_FILES = [
13001
13378
  "deploy.sh",
13002
13379
  "scripts/deploy.sh",
@@ -13009,9 +13386,9 @@ var SSH_URL = /ssh:\/\/(?:[^@\s/]+@)?([A-Za-z0-9.-]+)/g;
13009
13386
  var HOST_KEY = /^\s*-?\s*(?:host|hostname|server|ssh_host|deploy_host|remote_host)\s*:\s*["']?([A-Za-z0-9][A-Za-z0-9.-]*)["']?\s*$/i;
13010
13387
  var hostKey = (raw) => raw.trim().toLowerCase().replace(/^[^@]*@/, "").replace(/^\[([^\]]+)\].*$/, "$1").replace(/:\d+$/, "").replace(/\.$/, "");
13011
13388
  var shellLine = (line2) => line2.replace(/^\s*(?:-\s*)?(?:run:\s*\|?)?/, "");
13012
- var hostsInText = (text8) => {
13013
- const urls = [...text8.matchAll(SSH_URL)].map((match) => match[1] ?? "");
13014
- const lines3 = text8.split(/\r?\n/);
13389
+ var hostsInText = (text9) => {
13390
+ const urls = [...text9.matchAll(SSH_URL)].map((match) => match[1] ?? "");
13391
+ const lines3 = text9.split(/\r?\n/);
13015
13392
  const keyed = lines3.flatMap((line2) => {
13016
13393
  const match = HOST_KEY.exec(line2);
13017
13394
  return match?.[1] === void 0 ? [] : [match[1]];
@@ -13020,13 +13397,13 @@ var hostsInText = (text8) => {
13020
13397
  return [...urls, ...keyed, ...commands].filter((host) => host !== "" && !host.includes("$"));
13021
13398
  };
13022
13399
  var workflowFiles = (runtime, root) => {
13023
- const listed4 = runtime.listDir(toAbsolutePath(join30(root, ".github", "workflows")));
13400
+ const listed4 = runtime.listDir(toAbsolutePath(join32(root, ".github", "workflows")));
13024
13401
  if (!listed4.ok) return [];
13025
13402
  return listed4.value.filter((name) => /deploy/i.test(name) && /\.ya?ml$/i.test(name)).map((name) => `.github/workflows/${name}`);
13026
13403
  };
13027
13404
  var detectedHosts = (runtime, root, files) => files.flatMap((file) => {
13028
- const text8 = runtime.readText(toAbsolutePath(join30(root, file)));
13029
- return text8.ok ? hostsInText(text8.value) : [];
13405
+ const text9 = runtime.readText(toAbsolutePath(join32(root, file)));
13406
+ return text9.ok ? hostsInText(text9.value) : [];
13030
13407
  });
13031
13408
  var loadProdTargets = (runtime, root, config) => {
13032
13409
  const workflows = workflowFiles(runtime, root);
@@ -13037,7 +13414,7 @@ var loadProdTargets = (runtime, root, config) => {
13037
13414
  ...detectedHosts(runtime, root, deployFiles)
13038
13415
  ];
13039
13416
  const declaredDeploy = configAt(config, "prod", "deploy");
13040
- const existing = deployFiles.find((file) => runtime.stat(toAbsolutePath(join30(root, file))) !== void 0);
13417
+ const existing = deployFiles.find((file) => runtime.stat(toAbsolutePath(join32(root, file))) !== void 0);
13041
13418
  return {
13042
13419
  hosts: new Set(hosts.map(hostKey).filter((host) => host !== "")),
13043
13420
  kubeContexts: configList(config, "prod", "kube_contexts"),
@@ -13193,15 +13570,15 @@ var blockOf = (t, hit3, command, deploy) => ({
13193
13570
  })
13194
13571
  });
13195
13572
  var prodGuard = (runtime, root, t) => {
13196
- const targets2 = memo2(() => loadProdTargets(runtime, root, loadProjectConfig(runtime, root)));
13573
+ const targets2 = memo(() => loadProdTargets(runtime, root, loadProjectConfig(runtime, root)));
13197
13574
  const isProd = isProdHostOf(runtime, root, targets2);
13198
13575
  return {
13199
13576
  decide: (command) => {
13200
13577
  if (!PROD_PROGRAMS.has(command.program)) return void 0;
13201
13578
  const hit3 = prodHit(command, targets2(), isProd);
13202
13579
  if (hit3 === void 0) return void 0;
13203
- const text8 = commandText(command);
13204
- return hit3.effect === "read" ? { kind: "read", hit: hit3, command: text8 } : blockOf(t, hit3, text8, targets2().deployPath);
13580
+ const text9 = commandText(command);
13581
+ return hit3.effect === "read" ? { kind: "read", hit: hit3, command: text9 } : blockOf(t, hit3, text9, targets2().deployPath);
13205
13582
  }
13206
13583
  };
13207
13584
  };
@@ -13218,11 +13595,11 @@ var recordEvidence = (runtime, root, owner, reads) => {
13218
13595
  ] : []
13219
13596
  );
13220
13597
  if (lines3.length === 0) return;
13221
- const path = toAbsolutePath(join31(root, EVIDENCE_FILE));
13222
- runtime.mkdir(toAbsolutePath(join31(root, ".finitemonkey")));
13598
+ const path = toAbsolutePath(join33(root, EVIDENCE_FILE));
13599
+ runtime.mkdir(toAbsolutePath(join33(root, ".finitemonkey")));
13223
13600
  const before = runtime.readText(path);
13224
- const text8 = before.ok ? before.value : "";
13225
- runtime.writeAtomic(path, `${text8}${text8 === "" || text8.endsWith("\n") ? "" : "\n"}${lines3.join("\n")}
13601
+ const text9 = before.ok ? before.value : "";
13602
+ runtime.writeAtomic(path, `${text9}${text9 === "" || text9.endsWith("\n") ? "" : "\n"}${lines3.join("\n")}
13226
13603
  `);
13227
13604
  };
13228
13605
 
@@ -13237,15 +13614,15 @@ var editTexts = (input, file) => {
13237
13614
  if (!Array.isArray(edits)) return [];
13238
13615
  return edits.flatMap((edit) => {
13239
13616
  if (!isRecord(edit)) return [];
13240
- const text8 = optionalString(edit, "new_string");
13241
- return text8 === void 0 ? [] : [{ file, text: text8, whole: false }];
13617
+ const text9 = optionalString(edit, "new_string");
13618
+ return text9 === void 0 ? [] : [{ file, text: text9, whole: false }];
13242
13619
  });
13243
13620
  };
13244
13621
  var writtenTexts = (tool, input) => {
13245
13622
  const file = optionalString(input, "file_path") ?? optionalString(input, "notebook_path") ?? "";
13246
13623
  const single2 = (key, whole) => {
13247
- const text8 = optionalString(input, key);
13248
- return text8 === void 0 ? [] : [{ file, text: text8, whole }];
13624
+ const text9 = optionalString(input, key);
13625
+ return text9 === void 0 ? [] : [{ file, text: text9, whole }];
13249
13626
  };
13250
13627
  switch (tool) {
13251
13628
  case "Write":
@@ -13927,24 +14304,24 @@ var withChain = (facts, chain) => {
13927
14304
  const branch = chain?.branch;
13928
14305
  return branch === void 0 ? facts : { ...facts, currentBranch: () => branch };
13929
14306
  };
13930
- var decide = (context, repo, verdict, facts, text8) => {
14307
+ var decide = (context, repo, verdict, facts, text9) => {
13931
14308
  switch (verdict.kind) {
13932
14309
  case "pass":
13933
14310
  return { kind: "pass" };
13934
14311
  case "block":
13935
- return { kind: "block", rule: verdict.rule, subject: verdict.subject, command: text8 };
14312
+ return { kind: "block", rule: verdict.rule, subject: verdict.subject, command: text9 };
13936
14313
  case "snapshot":
13937
- return { kind: "snapshot", repo, op: verdict.op, command: text8 };
14314
+ return { kind: "snapshot", repo, op: verdict.op, command: text9 };
13938
14315
  case "discard": {
13939
14316
  const entries = facts.dirty();
13940
- if (entries === void 0) return { kind: "unknown", command: text8 };
14317
+ if (entries === void 0) return { kind: "unknown", command: text9 };
13941
14318
  const foreign = foreignFiles(
13942
14319
  context.runtime,
13943
14320
  repo,
13944
14321
  context.owner,
13945
14322
  targetsOf(entries, verdict.discard, facts.prefix)
13946
14323
  );
13947
- return foreign.length > 0 ? { kind: "foreign", files: foreign, command: text8 } : { kind: "snapshot", repo, op: verdict.op, command: text8 };
14324
+ return foreign.length > 0 ? { kind: "foreign", files: foreign, command: text9 } : { kind: "snapshot", repo, op: verdict.op, command: text9 };
13948
14325
  }
13949
14326
  }
13950
14327
  };
@@ -14031,10 +14408,10 @@ var listWorktrees = (runtime, cwd) => {
14031
14408
  };
14032
14409
 
14033
14410
  // src/git/journal.ts
14034
- import { join as join32 } from "node:path";
14411
+ import { join as join34 } from "node:path";
14035
14412
  var JOURNAL_FILE = ".finitemonkey/git-journal.jsonl";
14036
14413
  var JOURNAL_SCHEMA = "finitemonkey/git-journal@1";
14037
- var journalPath = (root) => toAbsolutePath(join32(root, JOURNAL_FILE));
14414
+ var journalPath = (root) => toAbsolutePath(join34(root, JOURNAL_FILE));
14038
14415
  var headsOf = (value) => isRecord(value) ? Object.fromEntries(
14039
14416
  Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
14040
14417
  ) : {};
@@ -14061,16 +14438,16 @@ var parseJournalLine = (line2) => {
14061
14438
  };
14062
14439
  };
14063
14440
  var readJournal = (runtime, root) => {
14064
- const text8 = runtime.readText(journalPath(root));
14065
- if (!text8.ok) return [];
14066
- return text8.value.split("\n").flatMap((line2) => {
14441
+ const text9 = runtime.readText(journalPath(root));
14442
+ if (!text9.ok) return [];
14443
+ return text9.value.split("\n").flatMap((line2) => {
14067
14444
  const entry2 = line2.trim() === "" ? void 0 : parseJournalLine(line2);
14068
14445
  return entry2 === void 0 ? [] : [entry2];
14069
14446
  });
14070
14447
  };
14071
14448
  var appendJournal = (runtime, root, entry2) => {
14072
14449
  const path = journalPath(root);
14073
- const made = runtime.mkdir(toAbsolutePath(join32(root, ".finitemonkey")));
14450
+ const made = runtime.mkdir(toAbsolutePath(join34(root, ".finitemonkey")));
14074
14451
  if (!made.ok) return made;
14075
14452
  const previous = runtime.readText(path);
14076
14453
  const before = previous.ok ? previous.value : "";
@@ -14234,9 +14611,9 @@ var snapshotsOf = (input, owner, pending) => {
14234
14611
  return { kind: "allow", output: { hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: context } } };
14235
14612
  };
14236
14613
  var review = (input, commands, owner) => {
14237
- const worktrees = memo2(() => listWorktrees(input.runtime, input.cwd));
14238
- const repo = memo2(() => openRepo(input.runtime, input.cwd));
14239
- const prod = memo2(() => prodGuard(input.runtime, repo()?.root ?? input.cwd, input.t));
14614
+ const worktrees = memo(() => listWorktrees(input.runtime, input.cwd));
14615
+ const repo = memo(() => openRepo(input.runtime, input.cwd));
14616
+ const prod = memo(() => prodGuard(input.runtime, repo()?.root ?? input.cwd, input.t));
14240
14617
  const snapshots = [];
14241
14618
  const reads = [];
14242
14619
  const chain = { branch: void 0 };
@@ -14255,9 +14632,9 @@ var review = (input, commands, owner) => {
14255
14632
  return { blocked: void 0, snapshots, reads };
14256
14633
  };
14257
14634
  var gitPreToolUse = (input) => {
14258
- const text8 = shellText(input.payload);
14259
- if (text8 === void 0) return ALLOW;
14260
- const commands = normalizeCommand(text8, dialectOf(toolName2(input.payload))).commands;
14635
+ const text9 = shellText(input.payload);
14636
+ if (text9 === void 0) return ALLOW;
14637
+ const commands = normalizeCommand(text9, dialectOf(toolName2(input.payload))).commands;
14261
14638
  if (commands.length === 0) return ALLOW;
14262
14639
  const owner = ownerOf2(input.payload);
14263
14640
  const result = review(input, commands, owner);
@@ -14319,8 +14696,8 @@ var GIT_HANDLERS = {
14319
14696
  var SHADOW_FILE = "shadow.json";
14320
14697
  var numberOf = (value) => typeof value === "number" && Number.isInteger(value) ? value : 0;
14321
14698
  var readShadow = (runtime, paths) => {
14322
- const text8 = runtime.readText(paths.cache(SHADOW_FILE));
14323
- const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
14699
+ const text9 = runtime.readText(paths.cache(SHADOW_FILE));
14700
+ const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
14324
14701
  if (parsed?.ok !== true) return {};
14325
14702
  return Object.fromEntries(
14326
14703
  Object.entries(parsed.value).flatMap(
@@ -14404,11 +14781,11 @@ var ASSIGNMENT3 = /^[A-Za-z_][A-Za-z0-9_]*=/;
14404
14781
  var WRAPPERS2 = /* @__PURE__ */ new Set(["sudo", "command", "exec", "time", "nice", "nohup", "builtin", "env"]);
14405
14782
  var liftHeredocs = (command) => {
14406
14783
  const heredocs = [];
14407
- const text8 = command.replace(HEREDOC2, (_match, _quote, _tag, body2) => {
14784
+ const text9 = command.replace(HEREDOC2, (_match, _quote, _tag, body2) => {
14408
14785
  heredocs.push(body2);
14409
14786
  return `<<__FM_HEREDOC_${String(heredocs.length - 1)}__`;
14410
14787
  });
14411
- return { text: text8, heredocs };
14788
+ return { text: text9, heredocs };
14412
14789
  };
14413
14790
  var endWord = (scan) => {
14414
14791
  if (scan.word !== void 0) scan.words.push(scan.word);
@@ -14419,23 +14796,23 @@ var endSegment = (scan) => {
14419
14796
  if (scan.words.length > 0) scan.segments.push(scan.words);
14420
14797
  scan.words = [];
14421
14798
  };
14422
- var quotedEnd2 = (text8, from, quote) => {
14799
+ var quotedEnd2 = (text9, from, quote) => {
14423
14800
  let index = from;
14424
- while (index < text8.length && text8[index] !== quote) index += quote === '"' && text8[index] === "\\" ? 2 : 1;
14801
+ while (index < text9.length && text9[index] !== quote) index += quote === '"' && text9[index] === "\\" ? 2 : 1;
14425
14802
  return index;
14426
14803
  };
14427
14804
  var unescape = (value) => value.replace(/\\(["\\$`])/g, "$1");
14428
- var splitWords = (text8) => {
14805
+ var splitWords = (text9) => {
14429
14806
  const scan = { words: [], segments: [], word: void 0 };
14430
- for (let index = 0; index < text8.length; index += 1) {
14431
- const char = text8[index] ?? "";
14807
+ for (let index = 0; index < text9.length; index += 1) {
14808
+ const char = text9[index] ?? "";
14432
14809
  if (char === '"' || char === "'") {
14433
- const end = quotedEnd2(text8, index + 1, char);
14434
- const inner = text8.slice(index + 1, end);
14810
+ const end = quotedEnd2(text9, index + 1, char);
14811
+ const inner = text9.slice(index + 1, end);
14435
14812
  scan.word = (scan.word ?? "") + (char === '"' ? unescape(inner) : inner);
14436
14813
  index = end;
14437
- } else if (char === "\\" && index + 1 < text8.length) {
14438
- scan.word = (scan.word ?? "") + (text8[index + 1] === "\n" ? "" : text8[index + 1] ?? "");
14814
+ } else if (char === "\\" && index + 1 < text9.length) {
14815
+ scan.word = (scan.word ?? "") + (text9[index + 1] === "\n" ? "" : text9[index + 1] ?? "");
14439
14816
  index += 1;
14440
14817
  } else if (SEPARATOR.test(char)) {
14441
14818
  endSegment(scan);
@@ -14456,8 +14833,8 @@ var segmentOf = (words) => {
14456
14833
  return { program: program.split(/[\\/]/).pop() ?? program, args: words.slice(index + 1) };
14457
14834
  };
14458
14835
  var shellWords = (command) => {
14459
- const { text: text8, heredocs } = liftHeredocs(command);
14460
- const segments = splitWords(text8).flatMap((words) => {
14836
+ const { text: text9, heredocs } = liftHeredocs(command);
14837
+ const segments = splitWords(text9).flatMap((words) => {
14461
14838
  const segment = segmentOf(words);
14462
14839
  return segment === void 0 ? [] : [segment];
14463
14840
  });
@@ -14562,7 +14939,7 @@ var sourceOf = (value, isFile, segment, heredocs) => {
14562
14939
  if (inline3 !== void 0) return { kind: "inline", text: inline3 };
14563
14940
  if (!isFile) return { kind: "inline", text: value };
14564
14941
  if (value !== "-") return { kind: "file", path: value };
14565
- const piped = segment.args.map((arg) => heredocIn(arg, heredocs)).find((text8) => text8 !== void 0);
14942
+ const piped = segment.args.map((arg) => heredocIn(arg, heredocs)).find((text9) => text9 !== void 0);
14566
14943
  return { kind: "inline", text: piped ?? "" };
14567
14944
  };
14568
14945
  var collect3 = (target2, segment, args2, spec2, heredocs) => {
@@ -14659,15 +15036,15 @@ var textOf2 = (input, found) => {
14659
15036
  return accept(parts.join("\n\n"));
14660
15037
  };
14661
15038
  var decisionFor = (input, found, blocking2) => {
14662
- const text8 = textOf2(input, found);
14663
- if (!text8.ok) {
15039
+ const text9 = textOf2(input, found);
15040
+ if (!text9.ok) {
14664
15041
  return blocking2 ? {
14665
15042
  kind: "block",
14666
- reason: modelReason(input.t, "prose.block.unreadable", { ...text8.error }),
15043
+ reason: modelReason(input.t, "prose.block.unreadable", { ...text9.error }),
14667
15044
  human: input.t("prose.block.human")
14668
15045
  } : void 0;
14669
15046
  }
14670
- const report2 = scoreProse(text8.value, { context: "record" });
15047
+ const report2 = scoreProse(text9.value, { context: "record" });
14671
15048
  if (report2.allowed || report2.spans.length === 0) return void 0;
14672
15049
  const params2 = {
14673
15050
  target: input.t(TARGET_KEYS[found.target]),
@@ -14714,7 +15091,7 @@ var HOOK_ACTOR = "finitemonkey";
14714
15091
  var liveFindings = (input, texts2) => {
14715
15092
  const baseline = loadBaseline(input.runtime, input.cwd);
14716
15093
  const accepted2 = acceptedFingerprints(baseline.ok ? baseline.value : EMPTY_BASELINE, input.runtime.now());
14717
- return texts2.flatMap((text8) => scanText(text8, { rules: CREDENTIAL_RULES })).filter((finding4) => !accepted2.has(finding4.fingerprint));
15094
+ return texts2.flatMap((text9) => scanText(text9, { rules: CREDENTIAL_RULES })).filter((finding4) => !accepted2.has(finding4.fingerprint));
14718
15095
  };
14719
15096
  var record2 = (input, findings, draft, where) => {
14720
15097
  const ids2 = findings.map((finding4) => {
@@ -14975,12 +15352,12 @@ var secretsSessionStart = (input) => {
14975
15352
  const remotes = remotesWithCredentials(input.runtime, input.cwd);
14976
15353
  if (remotes.length === 0) return { kind: "allow" };
14977
15354
  const params2 = { remotes: remotes.join(", "), remote: remotes[0] ?? "origin" };
14978
- const text8 = input.t("secrets.session.remote_credential", params2);
15355
+ const text9 = input.t("secrets.session.remote_credential", params2);
14979
15356
  return {
14980
15357
  kind: "allow",
14981
15358
  output: {
14982
- systemMessage: `finitemonkey: ${text8}`,
14983
- hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: text8 }
15359
+ systemMessage: `finitemonkey: ${text9}`,
15360
+ hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: text9 }
14984
15361
  }
14985
15362
  };
14986
15363
  };
@@ -15080,9 +15457,9 @@ var EMPTY_PAYLOAD = {
15080
15457
  stopHookActive: false,
15081
15458
  raw: {}
15082
15459
  };
15083
- var parseHookPayload = (event, text8, interactive) => {
15084
- if (interactive && text8.trim() === "") return accept(EMPTY_PAYLOAD);
15085
- const parsed = parseJsonObject(text8);
15460
+ var parseHookPayload = (event, text9, interactive) => {
15461
+ if (interactive && text9.trim() === "") return accept(EMPTY_PAYLOAD);
15462
+ const parsed = parseJsonObject(text9);
15086
15463
  if (!parsed.ok) return reject(`stdin: ${parsed.error}`);
15087
15464
  const payload = {
15088
15465
  hookEventName: optionalString(parsed.value, "hook_event_name"),
@@ -15097,53 +15474,13 @@ var parseHookPayload = (event, text8, interactive) => {
15097
15474
  return accept(payload);
15098
15475
  };
15099
15476
 
15100
- // src/hooks/stdin.ts
15101
- var readAll = (source) => new Promise((resolve2, reject2) => {
15102
- if (source.isTTY === true) {
15103
- resolve2("");
15104
- return;
15105
- }
15106
- const chunks = [];
15107
- source.setEncoding?.("utf8");
15108
- source.on("data", (chunk) => {
15109
- chunks.push(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
15110
- });
15111
- source.on("end", () => {
15112
- resolve2(chunks.join(""));
15113
- });
15114
- source.on("error", reject2);
15115
- });
15116
- var DeadlineError = class extends Error {
15117
- ms;
15118
- constructor(ms) {
15119
- super(`deadline of ${String(ms)} ms exceeded`);
15120
- this.name = "DeadlineError";
15121
- this.ms = ms;
15122
- }
15123
- };
15124
- var withDeadline = (work, ms, startTimer) => new Promise((resolve2, reject2) => {
15125
- const cancel = startTimer(ms, () => {
15126
- reject2(new DeadlineError(ms));
15127
- });
15128
- work.then(
15129
- (value) => {
15130
- cancel();
15131
- resolve2(value);
15132
- },
15133
- (error) => {
15134
- cancel();
15135
- reject2(error instanceof Error ? error : new Error(String(error)));
15136
- }
15137
- );
15138
- });
15139
-
15140
15477
  // src/hooks/runner.ts
15141
15478
  var HOOK_PROBE_ENV = "FINITEMONKEY_HOOK_PROBE";
15142
15479
  var messageOf2 = (error) => error instanceof Error ? error.message : String(error);
15143
15480
  var decide2 = (input, handlers) => composeDecisions(input, handlers[input.event] ?? [allow]);
15144
15481
  var evaluate = async (event, options) => {
15145
- const text8 = await readAll(options.stdin);
15146
- const parsed = parseHookPayload(event, text8, options.stdin.isTTY === true);
15482
+ const text9 = await readAll(options.stdin);
15483
+ const parsed = parseHookPayload(event, text9, options.stdin.isTTY === true);
15147
15484
  if (!parsed.ok) return { kind: "failed", payload: void 0, detail: parsed.error };
15148
15485
  const payload = parsed.value;
15149
15486
  if (options.env?.[HOOK_PROBE_ENV] === "1") return { kind: "decided", payload, decision: { kind: "allow" } };
@@ -15220,7 +15557,7 @@ var runHook = async (event, options) => {
15220
15557
  var PROBE_TIMEOUT_MS2 = 1e4;
15221
15558
  var SLOW_HOOK_MS = 1500;
15222
15559
  var DEFAULT_TIMEOUT_S = 60;
15223
- var expand = (text8, vars) => text8.replace(/\$\{(\w+)\}|\$(\w+)/g, (match, braced, bare) => {
15560
+ var expand = (text9, vars) => text9.replace(/\$\{(\w+)\}|\$(\w+)/g, (match, braced, bare) => {
15224
15561
  const name = braced ?? bare ?? "";
15225
15562
  return vars[name] ?? match;
15226
15563
  });
@@ -15269,7 +15606,7 @@ var run2 = (exec, invocation, cwd, env, input) => exec({
15269
15606
  env: { ...env, [HOOK_PROBE_ENV]: "1" },
15270
15607
  ...input === void 0 ? {} : { input }
15271
15608
  });
15272
- var firstLine = (text8) => maskText(text8.trim().split("\n")[0] ?? "").slice(0, 160);
15609
+ var firstLine = (text9) => maskText(text9.trim().split("\n")[0] ?? "").slice(0, 160);
15273
15610
  var classify2 = (result) => {
15274
15611
  if (!result.found) return { kind: "failed", problem: "missing", detail: "" };
15275
15612
  if (result.timedOut) return { kind: "failed", problem: "timeout", detail: "" };
@@ -15481,7 +15818,7 @@ var hookSchemaProblems = (hooks, claude) => {
15481
15818
  };
15482
15819
 
15483
15820
  // src/doctor/hook-sources.ts
15484
- import { join as join33 } from "node:path";
15821
+ import { join as join35 } from "node:path";
15485
15822
  var entriesOf = (source, hooks, own) => Object.entries(asRecord(hooks)).flatMap(
15486
15823
  ([event, groups]) => asList(groups).flatMap((group) => {
15487
15824
  const record3 = asRecord(group);
@@ -15494,31 +15831,31 @@ var projectHookFiles = (env) => readSettings(env).flatMap(({ scope: scope2, path
15494
15831
  const source = PROJECT_SCOPES[scope2];
15495
15832
  return source === void 0 || !("hooks" in settings) ? [] : [{ source, path, hooks: settings["hooks"] }];
15496
15833
  });
15497
- var pluginsDir = ({ env, runtime }) => join33(env["CLAUDE_CONFIG_DIR"] ?? join33(runtime.homeDir(), ".claude"), "plugins");
15834
+ var pluginsDir = ({ env, runtime }) => join35(env["CLAUDE_CONFIG_DIR"] ?? join35(runtime.homeDir(), ".claude"), "plugins");
15498
15835
  var installRecord = (value, root) => {
15499
15836
  const records = Array.isArray(value) ? value.filter(isRecord) : isRecord(value) ? [value] : [];
15500
15837
  return records.find((record3) => record3["projectPath"] === root) ?? records[0];
15501
15838
  };
15502
15839
  var readJson = (env, path) => {
15503
- const text8 = env.runtime.readText(path);
15504
- const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
15840
+ const text9 = env.runtime.readText(path);
15841
+ const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
15505
15842
  return parsed?.ok === true ? parsed.value : void 0;
15506
15843
  };
15507
15844
  var installedPlugin = (env) => {
15508
- const index = readJson(env, toAbsolutePath(join33(pluginsDir(env), "installed_plugins.json")));
15845
+ const index = readJson(env, toAbsolutePath(join35(pluginsDir(env), "installed_plugins.json")));
15509
15846
  const record3 = installRecord(asRecord(index?.["plugins"])[PLUGIN_ID], env.root);
15510
15847
  const installPath = record3 === void 0 ? void 0 : optionalString(record3, "installPath");
15511
15848
  if (record3 === void 0 || installPath === void 0) return void 0;
15512
15849
  const root = toAbsolutePath(installPath);
15513
- const manifest = readJson(env, toAbsolutePath(join33(root, ".claude-plugin", "plugin.json")));
15514
- const hooksPath = toAbsolutePath(join33(root, "hooks", "hooks.json"));
15515
- const text8 = env.runtime.readText(hooksPath);
15516
- const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
15850
+ const manifest = readJson(env, toAbsolutePath(join35(root, ".claude-plugin", "plugin.json")));
15851
+ const hooksPath = toAbsolutePath(join35(root, "hooks", "hooks.json"));
15852
+ const text9 = env.runtime.readText(hooksPath);
15853
+ const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
15517
15854
  return {
15518
15855
  root,
15519
15856
  version: (manifest === void 0 ? void 0 : optionalString(manifest, "version")) ?? optionalString(record3, "version"),
15520
15857
  hooksFile: parsed?.ok === true ? { source: "plugin", path: hooksPath, hooks: parsed.value["hooks"] } : void 0,
15521
- hooksText: text8.ok ? text8.value : void 0
15858
+ hooksText: text9.ok ? text9.value : void 0
15522
15859
  };
15523
15860
  };
15524
15861
  var PRECEDENCE2 = ["managed", "local", "project", "user"];
@@ -15548,15 +15885,15 @@ var schemaFinding = (file, problems, claude) => problems.length === 0 ? [] : [
15548
15885
  )
15549
15886
  ];
15550
15887
  var unreadableProjectFiles = (env) => settingsPaths(env).filter(({ scope: scope2 }) => scope2 === "project" || scope2 === "local").flatMap(({ path }) => {
15551
- const text8 = env.runtime.readText(path);
15552
- return text8.ok && text8.value.trim() !== "" && !parseJsonObject(text8.value).ok ? [path] : [];
15888
+ const text9 = env.runtime.readText(path);
15889
+ return text9.ok && text9.value.trim() !== "" && !parseJsonObject(text9.value).ok ? [path] : [];
15553
15890
  });
15554
15891
  var schemaFindings = (env, plugin, claude) => {
15555
15892
  const label2 = claude === void 0 ? "?" : claude.join(".");
15556
15893
  const files = [...projectHookFiles(env), ...plugin?.hooksFile === void 0 ? [] : [plugin.hooksFile]];
15557
15894
  const invalidJson = [
15558
15895
  ...unreadableProjectFiles(env),
15559
- ...plugin?.hooksText !== void 0 && plugin.hooksFile === void 0 ? [toAbsolutePath(join34(plugin.root, "hooks", "hooks.json"))] : []
15896
+ ...plugin?.hooksText !== void 0 && plugin.hooksFile === void 0 ? [toAbsolutePath(join36(plugin.root, "hooks", "hooks.json"))] : []
15560
15897
  ];
15561
15898
  return [
15562
15899
  ...invalidJson.flatMap((file) => schemaFinding(file, ["not valid JSON"], label2)),
@@ -15577,7 +15914,7 @@ var pluginFindings = (env, projectOwn) => {
15577
15914
  ];
15578
15915
  };
15579
15916
  var coverageFindings = (env, entries) => {
15580
- const installed = env.runtime.stat(toAbsolutePath(join34(env.root, ...LOCK_FILE.split("/")))) !== void 0;
15917
+ const installed = env.runtime.stat(toAbsolutePath(join36(env.root, ...LOCK_FILE.split("/")))) !== void 0;
15581
15918
  if (entries.length === 0 && !installed) {
15582
15919
  return [finding("hooks.registered", "warn", "doctor.hooks.none", {}, "doctor.hooks.fix_none")];
15583
15920
  }
@@ -15629,21 +15966,21 @@ var checkHooks = (env) => {
15629
15966
  };
15630
15967
 
15631
15968
  // src/doctor/instructions.ts
15632
- import { join as join35 } from "node:path";
15969
+ import { join as join37 } from "node:path";
15633
15970
  var INSTRUCTION_LINE_CEILING = 200;
15634
15971
  var TOP_INSTRUCTIONS = ["AGENTS.md", "CLAUDE.md", ".claude/CLAUDE.md", "CLAUDE.local.md"];
15635
15972
  var RULES_DIR = ".claude/rules";
15636
15973
  var SHOWN = 3;
15637
15974
  var readProjectFile = (env, relative11) => {
15638
- const text8 = env.runtime.readText(toAbsolutePath(join35(env.root, ...relative11.split("/"))));
15639
- return text8.ok ? text8.value : void 0;
15975
+ const text9 = env.runtime.readText(toAbsolutePath(join37(env.root, ...relative11.split("/"))));
15976
+ return text9.ok ? text9.value : void 0;
15640
15977
  };
15641
15978
  var instructionFiles = (env) => {
15642
- const listed4 = env.runtime.listDir(toAbsolutePath(join35(env.root, ...RULES_DIR.split("/"))));
15979
+ const listed4 = env.runtime.listDir(toAbsolutePath(join37(env.root, ...RULES_DIR.split("/"))));
15643
15980
  const rules = listed4.ok ? listed4.value.filter((name) => name.endsWith(".md")).map((name) => `${RULES_DIR}/${name}`) : [];
15644
15981
  return [...TOP_INSTRUCTIONS, ...rules].flatMap((path) => {
15645
- const text8 = readProjectFile(env, path);
15646
- return text8 === void 0 ? [] : [{ path, text: text8 }];
15982
+ const text9 = readProjectFile(env, path);
15983
+ return text9 === void 0 ? [] : [{ path, text: text9 }];
15647
15984
  });
15648
15985
  };
15649
15986
  var shortList = (items) => items.length <= SHOWN ? items.join(", ") : `${items.slice(0, SHOWN).join(", ")} (+${String(items.length - SHOWN)})`;
@@ -15691,10 +16028,10 @@ var hiddenInstructions = (env, deny2) => {
15691
16028
  ];
15692
16029
  };
15693
16030
  var localFile = (env) => readProjectFile(env, "CLAUDE.local.md") === void 0 ? [] : [finding("instructions.local", "warn", "doctor.instructions.local", {}, "doctor.instructions.fix_local")];
15694
- var lineCount = (text8) => text8.split("\n").length - (text8.endsWith("\n") ? 1 : 0);
16031
+ var lineCount = (text9) => text9.split("\n").length - (text9.endsWith("\n") ? 1 : 0);
15695
16032
  var oversized = (env) => ["AGENTS.md", "CLAUDE.md"].flatMap((file) => {
15696
- const text8 = readProjectFile(env, file);
15697
- const lines3 = text8 === void 0 ? 0 : lineCount(text8);
16033
+ const text9 = readProjectFile(env, file);
16034
+ const lines3 = text9 === void 0 ? 0 : lineCount(text9);
15698
16035
  if (lines3 <= INSTRUCTION_LINE_CEILING) return [];
15699
16036
  return [
15700
16037
  finding(
@@ -15707,13 +16044,13 @@ var oversized = (env) => ["AGENTS.md", "CLAUDE.md"].flatMap((file) => {
15707
16044
  ];
15708
16045
  });
15709
16046
  var MIN_RULE_WORDS = 6;
15710
- var ruleLines = (text8) => text8.split("\n").map((raw) => withoutCarriageReturn(raw).trim()).filter((line2) => !/^(#|\||```|@|>)/.test(line2)).map(
16047
+ var ruleLines = (text9) => text9.split("\n").map((raw) => withoutCarriageReturn(raw).trim()).filter((line2) => !/^(#|\||```|@|>)/.test(line2)).map(
15711
16048
  (line2) => line2.replace(/^([-*+]|\d+[.)])\s+/, "").replace(/\s+/g, " ").toLowerCase()
15712
16049
  ).filter((line2) => line2.split(" ").length >= MIN_RULE_WORDS);
15713
16050
  var duplicatedRules = (files) => {
15714
16051
  const seen = /* @__PURE__ */ new Map();
15715
- for (const { path, text: text8 } of files) {
15716
- for (const line2 of ruleLines(text8)) seen.set(line2, (seen.get(line2) ?? /* @__PURE__ */ new Set()).add(path));
16052
+ for (const { path, text: text9 } of files) {
16053
+ for (const line2 of ruleLines(text9)) seen.set(line2, (seen.get(line2) ?? /* @__PURE__ */ new Set()).add(path));
15717
16054
  }
15718
16055
  return [...seen].filter(([, paths]) => paths.size > 1).map(([line2]) => line2);
15719
16056
  };
@@ -15744,12 +16081,12 @@ var checkInstructions = (env) => {
15744
16081
  };
15745
16082
 
15746
16083
  // src/doctor/line-endings.ts
15747
- import { join as join36 } from "node:path";
16084
+ import { join as join38 } from "node:path";
15748
16085
  var LF_RULE = /^\*\s.*\beol=lf\b/m;
15749
16086
  var checkLineEndings = ({ root, runtime }) => {
15750
16087
  const autocrlf = runtime.git(["config", "--get", "core.autocrlf"], root);
15751
16088
  if (!autocrlf.ok || autocrlf.stdout.trim().toLowerCase() !== "true") return [];
15752
- const attributes = runtime.readText(toAbsolutePath(join36(root, ".gitattributes")));
16089
+ const attributes = runtime.readText(toAbsolutePath(join38(root, ".gitattributes")));
15753
16090
  if (attributes.ok && LF_RULE.test(attributes.value)) return [];
15754
16091
  return [
15755
16092
  finding("git.autocrlf", "warn", "doctor.git.autocrlf", { file: ".gitattributes" }, "doctor.git.fix_autocrlf")
@@ -15757,25 +16094,25 @@ var checkLineEndings = ({ root, runtime }) => {
15757
16094
  };
15758
16095
 
15759
16096
  // src/doctor/memory.ts
15760
- import { join as join37 } from "node:path";
16097
+ import { join as join39 } from "node:path";
15761
16098
  var ceilingOf = (config) => {
15762
16099
  const value = configAt(parseConfigYaml(config ?? ""), "memory", "maxLines");
15763
16100
  const parsed = typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
15764
16101
  return Number.isInteger(parsed) && parsed > 0 ? parsed : AGENTS_MAX_LINES;
15765
16102
  };
15766
- var lineCount2 = (text8) => text8.replace(/\r?\n$/, "").split(/\r?\n/).length;
16103
+ var lineCount2 = (text9) => text9.replace(/\r?\n$/, "").split(/\r?\n/).length;
15767
16104
  var checkAgentsFile = ({ root, runtime }) => {
15768
- const text8 = runtime.readText(toAbsolutePath(join37(root, AGENTS_FILE)));
15769
- if (!text8.ok) return [];
15770
- const config = runtime.readText(toAbsolutePath(join37(root, CONFIG_FILE)));
16105
+ const text9 = runtime.readText(toAbsolutePath(join39(root, AGENTS_FILE)));
16106
+ if (!text9.ok) return [];
16107
+ const config = runtime.readText(toAbsolutePath(join39(root, CONFIG_FILE2)));
15771
16108
  const max = ceilingOf(config.ok ? config.value : void 0);
15772
- const lines3 = lineCount2(text8.value);
16109
+ const lines3 = lineCount2(text9.value);
15773
16110
  const params2 = { file: AGENTS_FILE, lines: lines3, max };
15774
16111
  return lines3 > max ? [finding("memory.agents_lines", "fail", "doctor.memory.too_long", params2, "doctor.memory.fix_too_long")] : [finding("memory.agents_lines", "ok", "doctor.memory.lines", params2)];
15775
16112
  };
15776
16113
 
15777
16114
  // src/doctor/project-files.ts
15778
- import { join as join38 } from "node:path";
16115
+ import { join as join40 } from "node:path";
15779
16116
  var variants = (base, extensions) => extensions.map((extension) => `${base}${extension}`);
15780
16117
  var SCRIPT = [".js", ".cjs", ".mjs", ".ts", ".cts", ".mts"];
15781
16118
  var DATA = ["", ".json", ".yml", ".yaml"];
@@ -15806,7 +16143,7 @@ var CONFIG_GROUPS = [
15806
16143
  files: [...variants("commitlint.config", SCRIPT), ...variants(".commitlintrc", [...DATA, ".js"])]
15807
16144
  }
15808
16145
  ];
15809
- var present = (env, files) => files.filter((file) => env.runtime.stat(toAbsolutePath(join38(env.root, file)))?.kind === "file");
16146
+ var present = (env, files) => files.filter((file) => env.runtime.stat(toAbsolutePath(join40(env.root, file)))?.kind === "file");
15810
16147
  var crowded = (env, groups, build) => groups.flatMap(({ name, files }) => {
15811
16148
  const found = present(env, files);
15812
16149
  return found.length > 1 ? [build(name, found.join(", "))] : [];
@@ -15950,9 +16287,9 @@ var manualLines = (ctx, report2) => report2.checks.some((item2) => item2.status
15950
16287
  var writeReport = (ctx, data, result) => {
15951
16288
  const env = doctorEnvFrom(ctx);
15952
16289
  const path = reportPath(env);
15953
- const text8 = reportMarkdown(env, ctx, toEnvelope("doctor", result));
16290
+ const text9 = reportMarkdown(env, ctx, toEnvelope("doctor", result));
15954
16291
  const dir = ctx.runtime.mkdir(toAbsolutePath(dirname7(path)));
15955
- const written = dir.ok ? ctx.runtime.writeAtomic(path, text8) : dir;
16292
+ const written = dir.ok ? ctx.runtime.writeAtomic(path, text9) : dir;
15956
16293
  if (!written.ok) return failureFrom(ctx.t, applyProblem({ path, detail: written.error }));
15957
16294
  const human2 = [...result.human ?? [], ctx.t("doctor.report.written", { path })];
15958
16295
  return { ...result, data: { ...data, reportPath: path }, human: human2 };
@@ -15984,14 +16321,14 @@ var doctorCommand = {
15984
16321
  };
15985
16322
 
15986
16323
  // src/gain/gain.ts
15987
- import { join as join39 } from "node:path";
16324
+ import { join as join41 } from "node:path";
15988
16325
  var GAIN_SCHEMA = "finitemonkey/gain@1";
15989
16326
  var DAY_MS2 = 864e5;
15990
16327
  var SAVE_WINDOW_MS = DAY_MS2;
15991
16328
  var readLog = (runtime, paths) => {
15992
- const text8 = runtime.readText(paths.log);
15993
- if (!text8.ok) return [];
15994
- return text8.value.split("\n").flatMap((line2) => {
16329
+ const text9 = runtime.readText(paths.log);
16330
+ if (!text9.ok) return [];
16331
+ return text9.value.split("\n").flatMap((line2) => {
15995
16332
  const parsed = parseJsonObject(line2);
15996
16333
  if (!parsed.ok || !isRecord(parsed.value)) return [];
15997
16334
  const { at, event } = parsed.value;
@@ -16000,9 +16337,9 @@ var readLog = (runtime, paths) => {
16000
16337
  });
16001
16338
  };
16002
16339
  var readGates = (runtime, paths) => {
16003
- const listed4 = runtime.listDir(toAbsolutePath(join39(paths.dir, "evidence")));
16340
+ const listed4 = runtime.listDir(toAbsolutePath(join41(paths.dir, "evidence")));
16004
16341
  const files = listed4.ok ? listed4.value.filter((name) => name.endsWith(".jsonl")) : [];
16005
- return files.flatMap((name) => readLedger(runtime, paths.evidence(name.replace(/\.jsonl$/, "")))).filter((entry2) => entry2.check === GATE_CHECK);
16342
+ return files.flatMap((name) => readLedger2(runtime, paths.evidence(name.replace(/\.jsonl$/, "")))).filter((entry2) => entry2.check === GATE_CHECK);
16006
16343
  };
16007
16344
  var countsIn = (log, gates, from, to) => {
16008
16345
  const inWindow = (at) => at >= from && at < to;
@@ -16341,17 +16678,6 @@ var checksResult = (t, schema, code, checks) => {
16341
16678
  };
16342
16679
  };
16343
16680
 
16344
- // src/commands/git-problems.ts
16345
- var notRepository = (dir) => ({
16346
- code: "not_a_git_repo",
16347
- messageKey: "error.not_a_git_repo",
16348
- params: { dir },
16349
- fix: "git init",
16350
- exitCode: ExitCode.NotConfigured
16351
- });
16352
- var gitProblem = (code, messageKey, detail, fix, exitCode = ExitCode.CheckFailed) => ({ code, messageKey, params: { detail }, fix, exitCode });
16353
- var repoOf2 = (ctx) => openRepo(ctx.runtime, ctx.cwd);
16354
-
16355
16681
  // src/commands/git-checks.ts
16356
16682
  var COMMIT_MSG_SCHEMA = "finitemonkey/commit-msg@1";
16357
16683
  var PUSH_CHECK_SCHEMA = "finitemonkey/push-check@1";
@@ -16372,15 +16698,15 @@ var commitMsgCommand = {
16372
16698
  usageProblem("invalid_arguments", "error.invalid_arguments", COMMIT_MSG_USAGE, { detail: "<message-file>" })
16373
16699
  )
16374
16700
  );
16375
- const text8 = ctx.runtime.readText(toAbsolutePath(file, ctx.cwd));
16376
- if (!text8.ok)
16701
+ const text9 = ctx.runtime.readText(toAbsolutePath(file, ctx.cwd));
16702
+ if (!text9.ok)
16377
16703
  return Promise.resolve(
16378
16704
  failureFrom(
16379
16705
  ctx.t,
16380
16706
  usageProblem("invalid_arguments", "error.invalid_arguments", COMMIT_MSG_USAGE, { detail: file })
16381
16707
  )
16382
16708
  );
16383
- const failures = commitMessageProblems(text8.value);
16709
+ const failures = commitMessageProblems(text9.value);
16384
16710
  const fix = "git commit --amend, using type(scope): subject and no AI attribution lines";
16385
16711
  return Promise.resolve(
16386
16712
  checksResult(ctx.t, COMMIT_MSG_SCHEMA, "githook_failed", [{ id: "commit-messages", failures, fix }])
@@ -16394,7 +16720,7 @@ var pushCheckCommand = {
16394
16720
  status: "ready",
16395
16721
  maxPositionals: 2,
16396
16722
  run: async (ctx, args2) => {
16397
- const repo = repoOf2(ctx);
16723
+ const repo = repoOf(ctx);
16398
16724
  if (repo === void 0) return failureFrom(ctx.t, notRepository(ctx.cwd));
16399
16725
  const piped = parsePushUpdates(ctx.io.stdin.isTTY === true ? "" : await readAll(ctx.io.stdin));
16400
16726
  const updates = piped.length > 0 ? piped : updatesFromEnv(ctx.env);
@@ -16409,7 +16735,7 @@ var ciGateCommand = {
16409
16735
  status: "ready",
16410
16736
  options: { base: { type: "string" } },
16411
16737
  run: (ctx, args2) => {
16412
- const repo = repoOf2(ctx);
16738
+ const repo = repoOf(ctx);
16413
16739
  if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
16414
16740
  const base = ciBase(ctx.runtime, repo, ctx.env, stringOption(args2.values, "base"));
16415
16741
  if (!baseExists(ctx.runtime, repo, base))
@@ -16495,7 +16821,7 @@ var mergeVerifyCommand = {
16495
16821
  options: { since: { type: "string" }, dropped: { type: "string" } },
16496
16822
  maxPositionals: 1,
16497
16823
  run: (ctx, args2) => {
16498
- const repo = repoOf2(ctx);
16824
+ const repo = repoOf(ctx);
16499
16825
  if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
16500
16826
  const dropped = stringOption(args2.values, "dropped");
16501
16827
  const targets2 = targetsOf2(ctx, repo, args2.positionals[0], stringOption(args2.values, "since"));
@@ -16518,7 +16844,7 @@ var mergeVerifyCommand = {
16518
16844
  var REFLOG_LIMIT = 300;
16519
16845
  var LIST_LIMIT = 50;
16520
16846
  var SHA = /^[0-9a-f]{40,64}$/;
16521
- var linesOf2 = (text8) => text8.split("\n").filter((line2) => line2.trim() !== "");
16847
+ var linesOf2 = (text9) => text9.split("\n").filter((line2) => line2.trim() !== "");
16522
16848
  var reflogLines = (runtime, repo) => {
16523
16849
  const result = runtime.git(["reflog", "show", "--all", `-n${String(REFLOG_LIMIT)}`, "--format=%H %gD"], repo.root);
16524
16850
  if (!result.ok) return [];
@@ -16607,7 +16933,7 @@ var rescueCommand = {
16607
16933
  status: "ready",
16608
16934
  options: { restore: { type: "string" }, branch: { type: "string" } },
16609
16935
  run: (ctx, args2) => {
16610
- const repo = repoOf2(ctx);
16936
+ const repo = repoOf(ctx);
16611
16937
  if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
16612
16938
  const sha = stringOption(args2.values, "restore");
16613
16939
  const branch = stringOption(args2.values, "branch");
@@ -16639,7 +16965,7 @@ var rescueCommand = {
16639
16965
  };
16640
16966
 
16641
16967
  // src/git/undo.ts
16642
- import { basename as basename3, dirname as dirname8, join as join40 } from "node:path";
16968
+ import { basename as basename3, dirname as dirname8, join as join42 } from "node:path";
16643
16969
  var INDEX_SUBJECT = "finitemonkey index:";
16644
16970
  var gitLine = (runtime, repo, args2) => {
16645
16971
  const result = runtime.git(args2, repo.root);
@@ -16682,7 +17008,7 @@ var findSnapshot = (views, wanted) => {
16682
17008
  };
16683
17009
  var stampOf2 = (iso) => iso.replace(/[:.]/g, "-");
16684
17010
  var worktreePath = (runtime, repo) => {
16685
- const base = join40(dirname8(repo.root), `${basename3(repo.root)}-undo-${stampOf2(runtime.now())}`);
17011
+ const base = join42(dirname8(repo.root), `${basename3(repo.root)}-undo-${stampOf2(runtime.now())}`);
16686
17012
  return toAbsolutePath(runtime.stat(toAbsolutePath(base)) === void 0 ? base : `${base}-2`);
16687
17013
  };
16688
17014
  var intoWorktree = (runtime, repo, view2) => {
@@ -16726,7 +17052,7 @@ var snapshotCommand = {
16726
17052
  status: "ready",
16727
17053
  options: { message: { type: "string", short: "m" } },
16728
17054
  run: (ctx, args2) => {
16729
- const repo = repoOf2(ctx);
17055
+ const repo = repoOf(ctx);
16730
17056
  if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
16731
17057
  const message = args2.values["message"];
16732
17058
  const op = typeof message === "string" && message.trim() !== "" ? message.trim() : "manual";
@@ -16759,7 +17085,7 @@ var undoCommand = {
16759
17085
  options: { list: { type: "boolean" }, "in-place": { type: "boolean" } },
16760
17086
  maxPositionals: 1,
16761
17087
  run: (ctx, args2) => {
16762
- const repo = repoOf2(ctx);
17088
+ const repo = repoOf(ctx);
16763
17089
  if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
16764
17090
  const views = snapshotViews(ctx.runtime, repo);
16765
17091
  if (args2.values["list"] === true) return Promise.resolve(listResult(ctx, views));
@@ -16807,7 +17133,7 @@ var GLOBAL_FLAGS = [
16807
17133
  { flag: "--help, -h", key: "flag.help" }
16808
17134
  ];
16809
17135
  var exitCodeHelp = (ctx) => EXIT_CODE_NAMES.map((name) => ({ code: ExitCode[name], name, summary: ctx.t(exitCodeDocs[ExitCode[name]]) }));
16810
- var pad = (text8, width) => text8.padEnd(Math.max(width, text8.length + 1));
17136
+ var pad = (text9, width) => text9.padEnd(Math.max(width, text9.length + 1));
16811
17137
  var describe2 = (ctx, spec2) => ({
16812
17138
  name: commandName(spec2),
16813
17139
  usage: spec2.usage,
@@ -16917,7 +17243,7 @@ var hookCommand = {
16917
17243
  };
16918
17244
 
16919
17245
  // src/commands/i18n-check.ts
16920
- import { join as join41 } from "node:path";
17246
+ import { join as join43 } from "node:path";
16921
17247
 
16922
17248
  // src/i18n/message.ts
16923
17249
  var ARG = /^\s*([a-zA-Z][a-zA-Z0-9_]*)\s*(?:,\s*(plural|select)\s*,)?/;
@@ -16949,10 +17275,10 @@ var parseBranches = (body2, inPlural) => {
16949
17275
  };
16950
17276
  var parseNodes = (source, inPlural) => {
16951
17277
  const nodes = [];
16952
- let text8 = "";
17278
+ let text9 = "";
16953
17279
  const flush2 = () => {
16954
- if (text8 !== "") nodes.push({ kind: "text", text: text8 });
16955
- text8 = "";
17280
+ if (text9 !== "") nodes.push({ kind: "text", text: text9 });
17281
+ text9 = "";
16956
17282
  };
16957
17283
  for (let index = 0; index < source.length; index += 1) {
16958
17284
  const char = source.charAt(index);
@@ -16962,7 +17288,7 @@ var parseNodes = (source, inPlural) => {
16962
17288
  continue;
16963
17289
  }
16964
17290
  if (char !== "{") {
16965
- text8 += char;
17291
+ text9 += char;
16966
17292
  continue;
16967
17293
  }
16968
17294
  const cursor = { source, index: index + 1 };
@@ -16970,18 +17296,18 @@ var parseNodes = (source, inPlural) => {
16970
17296
  const inner = end < 0 ? void 0 : source.slice(index + 1, end);
16971
17297
  const match = inner === void 0 ? null : ARG.exec(inner);
16972
17298
  if (inner === void 0 || match === null) {
16973
- text8 += char;
17299
+ text9 += char;
16974
17300
  continue;
16975
17301
  }
16976
17302
  const name = match[1] ?? "";
16977
17303
  const type = match[2] === "plural" || match[2] === "select" ? match[2] : void 0;
16978
17304
  if (type === void 0 && match[0].trim().length !== inner.trim().length) {
16979
- text8 += char;
17305
+ text9 += char;
16980
17306
  continue;
16981
17307
  }
16982
17308
  const branches = type === void 0 ? void 0 : parseBranches(inner.slice(match[0].length), type === "plural");
16983
17309
  if (type !== void 0 && branches === void 0) {
16984
- text8 += char;
17310
+ text9 += char;
16985
17311
  continue;
16986
17312
  }
16987
17313
  flush2();
@@ -17104,8 +17430,8 @@ var RELEASE_HINTS = [
17104
17430
  /\bFINITEMONKEY_[A-Z_]+/,
17105
17431
  /\b[A-Z][A-Z0-9_]{2,}=/
17106
17432
  ];
17107
- var releaseHints = (text8) => RELEASE_HINTS.flatMap((pattern) => {
17108
- const match = pattern.exec(text8);
17433
+ var releaseHints = (text9) => RELEASE_HINTS.flatMap((pattern) => {
17434
+ const match = pattern.exec(text9);
17109
17435
  return match === null ? [] : [match[0]];
17110
17436
  });
17111
17437
  var hintProblems = (lang, catalog) => Object.keys(catalog).filter((key) => MODEL_FACING.test(key)).sort().flatMap((key) => {
@@ -17143,10 +17469,10 @@ var unreadable = (dir, file, detail) => ({
17143
17469
  exitCode: ExitCode.NotConfigured
17144
17470
  });
17145
17471
  var readCatalog = (ctx, dir, lang) => {
17146
- const file = toAbsolutePath(join41(dir, `${lang}.json`));
17147
- const text8 = ctx.runtime.readText(file);
17148
- if (!text8.ok) return reject(unreadable(dir, file, text8.error));
17149
- const parsed = parseJsonObject(text8.value);
17472
+ const file = toAbsolutePath(join43(dir, `${lang}.json`));
17473
+ const text9 = ctx.runtime.readText(file);
17474
+ if (!text9.ok) return reject(unreadable(dir, file, text9.error));
17475
+ const parsed = parseJsonObject(text9.value);
17150
17476
  return parsed.ok ? parsed : reject(unreadable(dir, file, parsed.error));
17151
17477
  };
17152
17478
  var loadCatalogs = (ctx, dir) => {
@@ -17166,8 +17492,8 @@ var describeProblem = (ctx, problem4) => {
17166
17492
  return ` ${problem4.lang} ${problem4.key} ${kind}`;
17167
17493
  };
17168
17494
  var loadReview = (ctx, dir) => {
17169
- const text8 = ctx.runtime.readText(toAbsolutePath(join41(dir, "review.json")));
17170
- const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
17495
+ const text9 = ctx.runtime.readText(toAbsolutePath(join43(dir, "review.json")));
17496
+ const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
17171
17497
  if (parsed?.ok !== true) return void 0;
17172
17498
  const statuses = (lang) => {
17173
17499
  const value = parsed.value[lang];
@@ -17207,7 +17533,7 @@ var i18nCheckCommand = {
17207
17533
  };
17208
17534
 
17209
17535
  // src/history/look-back.ts
17210
- import { join as join42 } from "node:path";
17536
+ import { join as join44 } from "node:path";
17211
17537
 
17212
17538
  // src/history/patterns.ts
17213
17539
  import { posix as posix5 } from "node:path";
@@ -17312,8 +17638,8 @@ var addResults = (draft, blocks) => {
17312
17638
  let any = false;
17313
17639
  for (const block4 of blocks) {
17314
17640
  if (!isRecord(block4) || block4["type"] !== "tool_result" || typeof block4["tool_use_id"] !== "string") continue;
17315
- const text8 = textOf3(block4["content"]);
17316
- draft.results.set(block4["tool_use_id"], { failed: block4["is_error"] === true || FAILED_EXIT.test(text8), text: text8 });
17641
+ const text9 = textOf3(block4["content"]);
17642
+ draft.results.set(block4["tool_use_id"], { failed: block4["is_error"] === true || FAILED_EXIT.test(text9), text: text9 });
17317
17643
  any = true;
17318
17644
  }
17319
17645
  return any;
@@ -17343,7 +17669,7 @@ var parseTranscript = (jsonl) => {
17343
17669
  // src/history/look-back.ts
17344
17670
  var SAMPLES = 3;
17345
17671
  var projectLogKey = (root) => root.replace(/[^A-Za-z0-9-]/g, "-");
17346
- var projectLogDir = (runtime, root, env) => join42(env["CLAUDE_CONFIG_DIR"] ?? join42(runtime.homeDir(), ".claude"), "projects", projectLogKey(root));
17672
+ var projectLogDir = (runtime, root, env) => join44(env["CLAUDE_CONFIG_DIR"] ?? join44(runtime.homeDir(), ".claude"), "projects", projectLogKey(root));
17347
17673
  var lookBack = (runtime, root, env) => {
17348
17674
  const dir = projectLogDir(runtime, root, env);
17349
17675
  const listed4 = runtime.listDir(toAbsolutePath(dir));
@@ -17364,9 +17690,9 @@ var lookBack = (runtime, root, env) => {
17364
17690
  };
17365
17691
  let turns = 0;
17366
17692
  for (const name of files) {
17367
- const text8 = runtime.readText(toAbsolutePath(join42(dir, name)));
17368
- if (!text8.ok) continue;
17369
- for (const turn of parseTranscript(text8.value)) {
17693
+ const text9 = runtime.readText(toAbsolutePath(join44(dir, name)));
17694
+ if (!text9.ok) continue;
17695
+ for (const turn of parseTranscript(text9.value)) {
17370
17696
  turns += 1;
17371
17697
  for (const hit3 of hitsIn(turn)) {
17372
17698
  counts[hit3.pattern] += 1;
@@ -17414,7 +17740,7 @@ var walk2 = (a, b) => {
17414
17740
  }
17415
17741
  return ops;
17416
17742
  };
17417
- var splitLines = (text8) => text8 === "" ? [] : text8.replace(/\r?\n$/, "").split(/\r?\n/);
17743
+ var splitLines = (text9) => text9 === "" ? [] : text9.replace(/\r?\n$/, "").split(/\r?\n/);
17418
17744
  var sharedPrefix = (a, b) => {
17419
17745
  let count4 = 0;
17420
17746
  while (count4 < a.length && count4 < b.length && a[count4] === b[count4]) count4 += 1;
@@ -17491,7 +17817,7 @@ var planData = (t, plan, mode, applied, backup) => ({
17491
17817
  diff: planDiff(plan),
17492
17818
  ...backup === void 0 ? {} : { backup }
17493
17819
  });
17494
- var pad2 = (text8, width) => text8.padEnd(width);
17820
+ var pad2 = (text9, width) => text9.padEnd(width);
17495
17821
  var planLines = (t, data, withDiff) => [
17496
17822
  t("install.plan.title", { root: data.root, preset: data.preset }),
17497
17823
  ...data.actions.map(({ path, action }) => ` ${pad2(action, 13)}${path}`),
@@ -17616,7 +17942,7 @@ var applyAllows = (findings, allows, decided) => {
17616
17942
 
17617
17943
  // src/look/node-capture.ts
17618
17944
  import { mkdirSync as mkdirSync3 } from "node:fs";
17619
- import { join as join43 } from "node:path";
17945
+ import { join as join45 } from "node:path";
17620
17946
 
17621
17947
  // src/look/measure.ts
17622
17948
  var measurePage = (doc) => {
@@ -17627,8 +17953,8 @@ var measurePage = (doc) => {
17627
17953
  const walker = doc.createTreeWalker(doc.body, SHOW_TEXT);
17628
17954
  const keys = /* @__PURE__ */ new Set();
17629
17955
  for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
17630
- const text8 = (node.textContent ?? "").trim();
17631
- if (KEY2.test(text8) && !NOT_KEY.test(text8) || PLACEHOLDER2.test(text8)) keys.add(text8.slice(0, 80));
17956
+ const text9 = (node.textContent ?? "").trim();
17957
+ if (KEY2.test(text9) && !NOT_KEY.test(text9) || PLACEHOLDER2.test(text9)) keys.add(text9.slice(0, 80));
17632
17958
  }
17633
17959
  const small = [...doc.querySelectorAll("a, button, input, select, textarea, [role=button], [role=link]")].filter((element) => {
17634
17960
  const box = element.getBoundingClientRect();
@@ -17661,8 +17987,8 @@ var slugOf = (route) => {
17661
17987
  var watch = (page) => {
17662
17988
  const errors = [];
17663
17989
  const failed4 = [];
17664
- const push = (list4, text8) => {
17665
- if (list4.length < MAX_EVENTS) list4.push(text8.slice(0, 200));
17990
+ const push = (list4, text9) => {
17991
+ if (list4.length < MAX_EVENTS) list4.push(text9.slice(0, 200));
17666
17992
  };
17667
17993
  page.on("console", (message) => {
17668
17994
  if (message.type() === "error" && !RESOURCE_ERROR.test(message.text())) push(errors, message.text());
@@ -17692,7 +18018,7 @@ var captureOne = async (browser, request, route, viewport) => {
17692
18018
  const context = await browser.newContext({ viewport: { width: viewport, height: HEIGHT }, reducedMotion: "reduce" });
17693
18019
  const page = await context.newPage();
17694
18020
  const events = watch(page);
17695
- const path = join43(request.dir, `${slugOf(route)}-${String(viewport)}.png`);
18021
+ const path = join45(request.dir, `${slugOf(route)}-${String(viewport)}.png`);
17696
18022
  const loadError = await loadAndShoot(page, `${request.baseUrl}${route}`, path, request.timeoutMs);
17697
18023
  const measure = loadError === void 0 ? parseMeasure(await page.evaluate(MEASURE_SCRIPT)) : void 0;
17698
18024
  await context.close();
@@ -17709,7 +18035,7 @@ var captureAll = async (browser, request) => {
17709
18035
 
17710
18036
  // src/look/node-playwright.ts
17711
18037
  import { createRequire } from "node:module";
17712
- import { join as join44 } from "node:path";
18038
+ import { join as join46 } from "node:path";
17713
18039
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
17714
18040
  var NAMES = ["playwright", "playwright-core"];
17715
18041
  var isPlaywright = (value) => isRecord(value) && isRecord(value["chromium"]) && typeof value["chromium"]["launch"] === "function";
@@ -17724,7 +18050,7 @@ var tryLoad = async (base, name) => {
17724
18050
  }
17725
18051
  };
17726
18052
  var loadPlaywright = async (root) => {
17727
- for (const base of [join44(root, "package.json"), fileURLToPath3(import.meta.url)]) {
18053
+ for (const base of [join46(root, "package.json"), fileURLToPath3(import.meta.url)]) {
17728
18054
  for (const name of NAMES) {
17729
18055
  const found = await tryLoad(base, name);
17730
18056
  if (found !== void 0) return found;
@@ -17745,8 +18071,8 @@ var launchBrowser = async (playwright) => {
17745
18071
 
17746
18072
  // src/look/node-sheet.ts
17747
18073
  import { readFileSync as readFileSync2 } from "node:fs";
17748
- import { join as join45 } from "node:path";
17749
- var escapeHtml = (text8) => text8.replace(/[&<>"]/g, (char) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[char] ?? char);
18074
+ import { join as join47 } from "node:path";
18075
+ var escapeHtml = (text9) => text9.replace(/[&<>"]/g, (char) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;" })[char] ?? char);
17750
18076
  var card = (result) => {
17751
18077
  const image = (() => {
17752
18078
  try {
@@ -17768,7 +18094,7 @@ var contactSheet = async (browser, results, dir) => {
17768
18094
  if (shown3.length === 0) return void 0;
17769
18095
  const page = await browser.newPage({ viewport: { width: 1600, height: 900 } });
17770
18096
  await page.setContent(`<!doctype html><style>${STYLE}</style>${shown3.map(card).join("")}`);
17771
- const path = join45(dir, "contact-sheet.png");
18097
+ const path = join47(dir, "contact-sheet.png");
17772
18098
  await page.screenshot({ path, fullPage: true });
17773
18099
  await page.close();
17774
18100
  return path;
@@ -18108,12 +18434,12 @@ var proseCheckCommand = {
18108
18434
  };
18109
18435
 
18110
18436
  // src/review/packet.ts
18111
- import { join as join46 } from "node:path";
18437
+ import { join as join48 } from "node:path";
18112
18438
  var REVIEW_STATE_FILE = ".finitemonkey/review/state.json";
18113
18439
  var FULL_ROUNDS = 3;
18114
18440
  var REVIEW_FILE_MAX_LINES = 200;
18115
18441
  var LEDGER_MAX_LINES = 100;
18116
- var statePath = (root) => toAbsolutePath(join46(root, REVIEW_STATE_FILE));
18442
+ var statePath = (root) => toAbsolutePath(join48(root, REVIEW_STATE_FILE));
18117
18443
  var readReviewState = (runtime, root) => {
18118
18444
  const raw = runtime.readText(statePath(root));
18119
18445
  const parsed = raw.ok ? parseJsonObject(raw.value) : void 0;
@@ -18125,7 +18451,7 @@ var git = (runtime, root, args2) => {
18125
18451
  const result = runtime.git(args2, root);
18126
18452
  return result.ok ? result.stdout : void 0;
18127
18453
  };
18128
- var lastLines = (text8, max) => text8.split("\n").slice(-max).join("\n");
18454
+ var lastLines = (text9, max) => text9.split("\n").slice(-max).join("\n");
18129
18455
  var section = (title, body2, empty) => `## ${title}
18130
18456
 
18131
18457
  ${body2 === void 0 || body2.trim() === "" ? empty : body2.trimEnd()}
@@ -18135,7 +18461,7 @@ var taskSections = (runtime, root) => {
18135
18461
  const active = readState(runtime, paths).active;
18136
18462
  if (active === void 0)
18137
18463
  return [section("Contract", void 0, "(no active task)"), section("Evidence ledger", void 0, "(none)")];
18138
- const contract = runtime.readText(toAbsolutePath(join46(root, taskRelative(active))));
18464
+ const contract = runtime.readText(toAbsolutePath(join48(root, taskRelative(active))));
18139
18465
  const ledger = runtime.readText(paths.evidence(active));
18140
18466
  return [
18141
18467
  section("Contract", contract.ok ? contract.value : void 0, "(contract file missing)"),
@@ -18143,8 +18469,8 @@ var taskSections = (runtime, root) => {
18143
18469
  ];
18144
18470
  };
18145
18471
  var reviewFile = (runtime, root, base) => {
18146
- const text8 = git(runtime, root, ["show", `${base}:REVIEW.md`]);
18147
- return text8 === void 0 ? void 0 : text8.split("\n").slice(0, REVIEW_FILE_MAX_LINES).join("\n");
18472
+ const text9 = git(runtime, root, ["show", `${base}:REVIEW.md`]);
18473
+ return text9 === void 0 ? void 0 : text9.split("\n").slice(0, REVIEW_FILE_MAX_LINES).join("\n");
18148
18474
  };
18149
18475
  var nextState = (previous, base, head) => {
18150
18476
  if (previous?.base !== base) return { base, rounds: 1, lastHead: head };
@@ -18157,7 +18483,7 @@ var buildPacket = (runtime, root, base, head) => {
18157
18483
  const range = `${from}...${head}`;
18158
18484
  const diff = git(runtime, root, ["diff", "--no-color", "--no-ext-diff", range, "--", ".", ":(exclude).finitemonkey"]);
18159
18485
  const owners = reviewFile(runtime, root, base);
18160
- const text8 = [
18486
+ const text9 = [
18161
18487
  `# Review packet (round ${String(state.rounds)}, ${range})`,
18162
18488
  "",
18163
18489
  ...state.rounds > FULL_ROUNDS ? ["Only the diff since the last review round is included.", ""] : [],
@@ -18165,10 +18491,10 @@ var buildPacket = (runtime, root, base, head) => {
18165
18491
  ...taskSections(runtime, root),
18166
18492
  ...owners === void 0 ? [] : [section("REVIEW.md (base branch)", owners, "")]
18167
18493
  ].join("\n");
18168
- runtime.mkdir(toAbsolutePath(join46(root, ".finitemonkey", "review")));
18494
+ runtime.mkdir(toAbsolutePath(join48(root, ".finitemonkey", "review")));
18169
18495
  runtime.writeAtomic(statePath(root), `${JSON.stringify(state, null, 2)}
18170
18496
  `);
18171
- return { round: state.rounds, base, head, range, text: text8 };
18497
+ return { round: state.rounds, base, head, range, text: text9 };
18172
18498
  };
18173
18499
 
18174
18500
  // src/commands/review.ts
@@ -18180,7 +18506,7 @@ var reviewPacketCommand = {
18180
18506
  status: "ready",
18181
18507
  options: { base: { type: "string" } },
18182
18508
  run: (ctx, args2) => {
18183
- const repo = repoOf2(ctx);
18509
+ const repo = repoOf(ctx);
18184
18510
  if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
18185
18511
  const base = stringOption(args2.values, "base") ?? repoFacts(ctx.runtime, repo).defaultBranch();
18186
18512
  const resolved = ctx.runtime.git(["rev-parse", "--verify", "--quiet", `${base}^{commit}`], repo.root);
@@ -18320,7 +18646,7 @@ var secretsAcceptCommand = {
18320
18646
  };
18321
18647
 
18322
18648
  // src/secrets/runbooks.ts
18323
- import { dirname as dirname9, join as join47 } from "node:path";
18649
+ import { dirname as dirname9, join as join49 } from "node:path";
18324
18650
  var PROVIDER_OF_RULE = {
18325
18651
  "aws-access-token": "aws",
18326
18652
  "aws-secret-access-key": "aws",
@@ -18342,13 +18668,13 @@ var RUNBOOKS_URL = "https://github.com/gabrielvieiraf/finitemonkey/blob/main/run
18342
18668
  var runbookProvider = (rule) => PROVIDER_OF_RULE[rule] ?? "generic";
18343
18669
  var runbookLink = (runtime, rule) => {
18344
18670
  const file = `${runbookProvider(rule)}.md`;
18345
- const local = toAbsolutePath(join47(dirname9(dirname9(runtime.selfPath)), "runbooks", file));
18671
+ const local = toAbsolutePath(join49(dirname9(dirname9(runtime.selfPath)), "runbooks", file));
18346
18672
  return runtime.stat(local)?.kind === "file" ? local : `${RUNBOOKS_URL}/${file}`;
18347
18673
  };
18348
18674
 
18349
18675
  // src/commands/secrets-incident.ts
18350
18676
  var INCIDENT_ACTIONS = ["list", "rotated", "close", "purge"];
18351
- var USAGE = "finitemonkey secrets incident [list | rotated <id> | close <id> | purge <id>]";
18677
+ var USAGE2 = "finitemonkey secrets incident [list | rotated <id> | close <id> | purge <id>]";
18352
18678
  var TARGET = { rotated: "rotacionado", close: "fechado" };
18353
18679
  var PURGE_STEPS = [
18354
18680
  "secrets.purge.step1",
@@ -18406,7 +18732,7 @@ var move = (ctx, root, incident, action) => {
18406
18732
  if (!canMove(incident.state, to))
18407
18733
  return action === "close" ? notRotated(ctx, incident) : fail2(
18408
18734
  ctx,
18409
- usageProblem("invalid_arguments", "secrets.error.bad_transition", USAGE, {
18735
+ usageProblem("invalid_arguments", "secrets.error.bad_transition", USAGE2, {
18410
18736
  id: incident.id,
18411
18737
  from: incident.state,
18412
18738
  to
@@ -18429,7 +18755,7 @@ var actionOf = (raw) => INCIDENT_ACTIONS.find((action) => action === (raw ?? "li
18429
18755
  var secretsIncidentCommand = {
18430
18756
  path: ["secrets", "incident"],
18431
18757
  summaryKey: "secrets.command.incident",
18432
- usage: USAGE,
18758
+ usage: USAGE2,
18433
18759
  status: "ready",
18434
18760
  maxPositionals: 2,
18435
18761
  run: (ctx, args2) => {
@@ -18439,7 +18765,7 @@ var secretsIncidentCommand = {
18439
18765
  return Promise.resolve(
18440
18766
  fail2(
18441
18767
  ctx,
18442
- usageProblem("invalid_arguments", "error.invalid_arguments", USAGE, { detail: args2.positionals[0] ?? "" })
18768
+ usageProblem("invalid_arguments", "error.invalid_arguments", USAGE2, { detail: args2.positionals[0] ?? "" })
18443
18769
  )
18444
18770
  );
18445
18771
  if (action === "list") return Promise.resolve(list2(ctx, root));
@@ -18511,7 +18837,7 @@ var liveOutcome = (result) => {
18511
18837
 
18512
18838
  // src/commands/secrets-live.ts
18513
18839
  var SECRETS_LIVE_SCHEMA = "finitemonkey/secrets-live@1";
18514
- var USAGE2 = `finitemonkey secrets live <${LIVE_PROVIDERS.join("|")}> --env <VARIABLE> [--send]`;
18840
+ var USAGE3 = `finitemonkey secrets live <${LIVE_PROVIDERS.join("|")}> --env <VARIABLE> [--send]`;
18515
18841
  var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
18516
18842
  var target = (provider) => {
18517
18843
  const probe = LIVE_PROBES[provider];
@@ -18542,7 +18868,7 @@ var send = (ctx, provider, variable, key) => {
18542
18868
  var secretsLiveCommand = {
18543
18869
  path: ["secrets", "live"],
18544
18870
  summaryKey: "secrets.command.live",
18545
- usage: USAGE2,
18871
+ usage: USAGE3,
18546
18872
  status: "ready",
18547
18873
  maxPositionals: 1,
18548
18874
  options: { env: { type: "string" }, send: { type: "boolean" } },
@@ -18553,7 +18879,7 @@ var secretsLiveCommand = {
18553
18879
  return Promise.resolve(
18554
18880
  failureFrom(
18555
18881
  ctx.t,
18556
- usageProblem("invalid_arguments", "error.invalid_arguments", USAGE2, {
18882
+ usageProblem("invalid_arguments", "error.invalid_arguments", USAGE3, {
18557
18883
  detail: provider === void 0 ? args2.positionals[0] ?? "<provider>" : "--env <VARIABLE>"
18558
18884
  })
18559
18885
  )
@@ -18563,7 +18889,7 @@ var secretsLiveCommand = {
18563
18889
  return Promise.resolve(
18564
18890
  failureFrom(
18565
18891
  ctx.t,
18566
- needsPersonProblem("secrets.error.person_only", `${USAGE2.replace(" [--send]", "")} --send`, {
18892
+ needsPersonProblem("secrets.error.person_only", `${USAGE3.replace(" [--send]", "")} --send`, {
18567
18893
  action: ctx.t("secrets.action.live")
18568
18894
  })
18569
18895
  )
@@ -18571,7 +18897,7 @@ var secretsLiveCommand = {
18571
18897
  const key = ctx.env[variable] ?? "";
18572
18898
  if (key === "")
18573
18899
  return Promise.resolve(
18574
- failureFrom(ctx.t, usageProblem("invalid_arguments", "secrets.live.no_value", USAGE2, { variable }))
18900
+ failureFrom(ctx.t, usageProblem("invalid_arguments", "secrets.live.no_value", USAGE3, { variable }))
18575
18901
  );
18576
18902
  return Promise.resolve(send(ctx, provider, variable, key));
18577
18903
  }
@@ -18740,7 +19066,7 @@ var gateLight = (runtime, paths) => {
18740
19066
  };
18741
19067
 
18742
19068
  // src/statusline/input.ts
18743
- var text7 = (value) => typeof value === "string" && value !== "" ? value : void 0;
19069
+ var text8 = (value) => typeof value === "string" && value !== "" ? value : void 0;
18744
19070
  var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
18745
19071
  var usedTokens = (usage) => {
18746
19072
  if (!isRecord(usage)) return void 0;
@@ -18760,8 +19086,8 @@ var parseStatusInput = (raw) => {
18760
19086
  const record3 = parsed.ok ? parsed.value : {};
18761
19087
  const workspace = isRecord(record3["workspace"]) ? record3["workspace"] : {};
18762
19088
  return {
18763
- sessionId: text7(record3["session_id"]),
18764
- dir: text7(workspace["project_dir"]) ?? text7(workspace["current_dir"]) ?? text7(record3["cwd"]),
19089
+ sessionId: text8(record3["session_id"]),
19090
+ dir: text8(workspace["project_dir"]) ?? text8(workspace["current_dir"]) ?? text8(record3["cwd"]),
18765
19091
  contextPercent: contextPercent(record3),
18766
19092
  raw
18767
19093
  };
@@ -18776,7 +19102,7 @@ var segmentText = (t, segment) => [
18776
19102
  ...segment.contextPercent === void 0 ? [] : [t("statusline.context", { percent: segment.contextPercent })]
18777
19103
  ].join(" · ");
18778
19104
  var ANSI3 = new RegExp(`${String.fromCharCode(27)}\\[[0-9;?]*[A-Za-z]`, "g");
18779
- var graphemes = (text8) => Array.from(new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(text8), (part2) => part2.segment);
19105
+ var graphemes = (text9) => Array.from(new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(text9), (part2) => part2.segment);
18780
19106
  var fit = (line2, columns) => {
18781
19107
  if (columns === void 0) return line2;
18782
19108
  const visible = graphemes(line2.replace(ANSI3, ""));
@@ -18837,16 +19163,16 @@ var cached2 = (runtime, paths) => {
18837
19163
  const raw = readJsonFile(runtime, paths.cache(WRAP_CACHE))?.["sessions"];
18838
19164
  return isRecord(raw) ? Object.fromEntries(Object.entries(raw).filter((entry2) => typeof entry2[1] === "string")) : {};
18839
19165
  };
18840
- var remember = (runtime, paths, session, text8) => {
18841
- const sessions = Object.entries({ ...cached2(runtime, paths), [session]: text8 }).slice(-KEEP3);
19166
+ var remember = (runtime, paths, session, text9) => {
19167
+ const sessions = Object.entries({ ...cached2(runtime, paths), [session]: text9 }).slice(-KEEP3);
18842
19168
  writeJsonFile(runtime, paths.cache(WRAP_CACHE), { sessions: Object.fromEntries(sessions) });
18843
19169
  };
18844
19170
  var runWrapped = async (runtime, paths, request, shell = nodeShell) => {
18845
19171
  const result = await shell(request.command, request.input, request.cwd, request.timeoutMs);
18846
19172
  if (result.text !== void 0) {
18847
- const text8 = result.text.replace(/\s+$/, "");
18848
- if (paths !== void 0) remember(runtime, paths, request.session, text8);
18849
- return text8;
19173
+ const text9 = result.text.replace(/\s+$/, "");
19174
+ if (paths !== void 0) remember(runtime, paths, request.session, text9);
19175
+ return text9;
18850
19176
  }
18851
19177
  return paths === void 0 ? "" : cached2(runtime, paths)[request.session] ?? "";
18852
19178
  };
@@ -18855,8 +19181,8 @@ var runWrapped = async (runtime, paths, request, shell = nodeShell) => {
18855
19181
  var STDIN_MS = 300;
18856
19182
  var WRAP_MS = 700;
18857
19183
  var wrapOf = (ctx, root) => {
18858
- const text8 = ctx.runtime.readText(toAbsolutePath(`${root}/${LOCK_FILE}`));
18859
- const lock = text8.ok ? parseLock(text8.value) : void 0;
19184
+ const text9 = ctx.runtime.readText(toAbsolutePath(`${root}/${LOCK_FILE}`));
19185
+ const lock = text9.ok ? parseLock(text9.value) : void 0;
18860
19186
  if (!lock?.ok) return void 0;
18861
19187
  return lock.value.entries.find((entry2) => entry2.added?.statusLine !== void 0)?.added?.statusLine?.wrap ?? void 0;
18862
19188
  };
@@ -18902,7 +19228,7 @@ var statuslineCommand = {
18902
19228
  };
18903
19229
 
18904
19230
  // src/task/create.ts
18905
- import { join as join48 } from "node:path";
19231
+ import { join as join50 } from "node:path";
18906
19232
 
18907
19233
  // src/core/yaml/write.ts
18908
19234
  var PLAIN_SAFE = /^[A-Za-z_./][A-Za-z0-9_./ ()-]*$/;
@@ -18991,10 +19317,10 @@ var explicitChecks = (draft) => [
18991
19317
  var draftChecks = (runtime, paths, draft) => {
18992
19318
  const disk = {
18993
19319
  read: (relative11) => {
18994
- const text8 = runtime.readText(toAbsolutePath(join48(paths.root, relative11)));
18995
- return text8.ok ? text8.value : void 0;
19320
+ const text9 = runtime.readText(toAbsolutePath(join50(paths.root, relative11)));
19321
+ return text9.ok ? text9.value : void 0;
18996
19322
  },
18997
- exists: (relative11) => runtime.stat(toAbsolutePath(join48(paths.root, relative11))) !== void 0
19323
+ exists: (relative11) => runtime.stat(toAbsolutePath(join50(paths.root, relative11))) !== void 0
18998
19324
  };
18999
19325
  const detected = detectChecks(disk);
19000
19326
  const base = draft.runs.length > 0 || draft.tests !== void 0 ? explicitChecks(draft) : detected;
@@ -19009,7 +19335,7 @@ var draftChecks = (runtime, paths, draft) => {
19009
19335
  { id: "merge", kind: "merge" },
19010
19336
  { id: "callers", kind: "callers" },
19011
19337
  ...draft.paths.length > 0 || draft.budget !== void 0 ? [{ id: "diff", kind: "diff" }] : [],
19012
- ...runtime.stat(toAbsolutePath(join48(paths.root, LOOK_CONFIG))) === void 0 ? [] : [{ id: "look", kind: "look" }],
19338
+ ...runtime.stat(toAbsolutePath(join50(paths.root, LOOK_CONFIG))) === void 0 ? [] : [{ id: "look", kind: "look" }],
19013
19339
  ...draft.git.length > 0 ? [{ id: "git", kind: "git", require: draft.git }] : [],
19014
19340
  { id: "pr", kind: "pr", requireOpen: draft.git.includes("pr_open") },
19015
19341
  ...draft.manual.map((description, index) => ({
@@ -19035,19 +19361,19 @@ var createTask = (runtime, paths, draft) => {
19035
19361
  decisionsNeeded: [],
19036
19362
  implicit: false
19037
19363
  };
19038
- const text8 = stringifyYaml(contractYaml(shape));
19364
+ const text9 = stringifyYaml(contractYaml(shape));
19039
19365
  const valid = validateContract(contractYaml(shape));
19040
19366
  if (!valid.ok) return reject({ kind: "invalid", issues: valid.error });
19041
- const wrote = runtime.writeText(paths.task(draft.id), text8);
19367
+ const wrote = runtime.writeText(paths.task(draft.id), text9);
19042
19368
  if (!wrote.ok) return reject({ kind: "write", detail: wrote.error });
19043
19369
  const lock = readTrust(runtime, paths);
19044
19370
  writeTrust(runtime, paths, {
19045
19371
  ...lock,
19046
19372
  contracts: { ...lock.contracts, [draft.id]: contractHash(valid.value) },
19047
- approved: { ...lock.approved, [draft.id]: text8 }
19373
+ approved: { ...lock.approved, [draft.id]: text9 }
19048
19374
  });
19049
19375
  writeState(runtime, paths, { ...readState(runtime, paths), active: draft.id });
19050
- return accept({ contract: valid.value, text: text8 });
19376
+ return accept({ contract: valid.value, text: text9 });
19051
19377
  };
19052
19378
 
19053
19379
  // src/commands/task.ts
@@ -19198,7 +19524,7 @@ var taskDoneCommand = {
19198
19524
  };
19199
19525
 
19200
19526
  // src/install/githook-block.ts
19201
- var withoutHookBlock = (text8) => withoutManagedBlock(text8, { begin: BLOCK_BEGIN, end: BLOCK_END });
19527
+ var withoutHookBlock = (text9) => withoutManagedBlock(text9, { begin: BLOCK_BEGIN, end: BLOCK_END });
19202
19528
 
19203
19529
  // src/install/uninstall.ts
19204
19530
  var blockless = (kind, current2) => {
@@ -19207,8 +19533,8 @@ var blockless = (kind, current2) => {
19207
19533
  };
19208
19534
  var structural = (entry2, current2) => {
19209
19535
  if (entry2.kind === "gitignore" || entry2.kind === "markdown" || entry2.kind === "githook") {
19210
- const text8 = blockless(entry2.kind, current2);
19211
- return text8.trim() === "" && !entry2.existed ? void 0 : text8;
19536
+ const text9 = blockless(entry2.kind, current2);
19537
+ return text9.trim() === "" && !entry2.existed ? void 0 : text9;
19212
19538
  }
19213
19539
  const parsed = parseSettings(current2);
19214
19540
  if (!parsed.ok || entry2.added === void 0) return current2;
@@ -19414,34 +19740,35 @@ var REGISTRY = [
19414
19740
  lookCommand,
19415
19741
  reviewPacketCommand,
19416
19742
  statuslineCommand,
19743
+ critiqueVerifyCommand,
19417
19744
  versionCommand,
19418
19745
  helpCommand
19419
19746
  ];
19420
19747
 
19421
19748
  // src/i18n/lang-settings.ts
19422
- import { join as join49 } from "node:path";
19749
+ import { join as join51 } from "node:path";
19423
19750
  var read3 = (runtime, path) => {
19424
- const text8 = runtime.readText(toAbsolutePath(path));
19425
- return text8.ok ? text8.value : void 0;
19751
+ const text9 = runtime.readText(toAbsolutePath(path));
19752
+ return text9.ok ? text9.value : void 0;
19426
19753
  };
19427
19754
  var configText = (tree, ...path) => {
19428
19755
  const value = configAt(tree, ...path);
19429
19756
  return typeof value === "string" && value !== "" ? value : void 0;
19430
19757
  };
19431
- var languageIn = (text8) => {
19432
- if (text8 === void 0) return void 0;
19433
- const parsed = parseJsonObject(text8);
19758
+ var languageIn = (text9) => {
19759
+ if (text9 === void 0) return void 0;
19760
+ const parsed = parseJsonObject(text9);
19434
19761
  const value = parsed.ok && isRecord(parsed.value) ? parsed.value["language"] : void 0;
19435
19762
  return typeof value === "string" && value.trim() !== "" ? value : void 0;
19436
19763
  };
19437
- var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join49(runtime.homeDir(), ".claude");
19764
+ var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join51(runtime.homeDir(), ".claude");
19438
19765
  var langSettings = (runtime, cwd, env, platform) => {
19439
19766
  const root = findGitRoot(cwd, runtime) ?? cwd;
19440
- const config = parseConfigYaml(read3(runtime, join49(root, CONFIG_FILE)) ?? "");
19767
+ const config = parseConfigYaml(read3(runtime, join51(root, CONFIG_FILE2)) ?? "");
19441
19768
  const claudeFiles = [
19442
- join49(root, ".claude", "settings.local.json"),
19443
- join49(root, ".claude", "settings.json"),
19444
- join49(claudeDir(runtime, env), "settings.json")
19769
+ join51(root, ".claude", "settings.local.json"),
19770
+ join51(root, ".claude", "settings.json"),
19771
+ join51(claudeDir(runtime, env), "settings.json")
19445
19772
  ];
19446
19773
  return {
19447
19774
  config: configText(config, "lang"),
@@ -19528,8 +19855,8 @@ var resultFromThrown = (thrown, t) => {
19528
19855
  };
19529
19856
  var block3 = (items) => items.length > 0 ? `${items.join("\n")}
19530
19857
  ` : "";
19531
- var write2 = (sink, text8) => {
19532
- if (text8 !== "") sink.write(maskText(text8));
19858
+ var write2 = (sink, text9) => {
19859
+ if (text9 !== "") sink.write(maskText(text9));
19533
19860
  };
19534
19861
  var render2 = (ctx, command, result) => {
19535
19862
  if (result.raw !== void 0) {
@@ -19598,9 +19925,9 @@ import { fileURLToPath as fileURLToPath4 } from "node:url";
19598
19925
  var UNKNOWN_VERSION = "0.0.0-unknown";
19599
19926
  var VERSION_SOURCES = ["../package.json", "../.claude-plugin/plugin.json"];
19600
19927
  var versionAt = (readText, moduleUrl, relative11) => {
19601
- const text8 = readText(toAbsolutePath(fileURLToPath4(new URL(relative11, moduleUrl))));
19602
- if (!text8.ok) return void 0;
19603
- const parsed = parseJson(text8.value);
19928
+ const text9 = readText(toAbsolutePath(fileURLToPath4(new URL(relative11, moduleUrl))));
19929
+ if (!text9.ok) return void 0;
19930
+ const parsed = parseJson(text9.value);
19604
19931
  if (!parsed.ok || !isRecord(parsed.value)) return void 0;
19605
19932
  const version = parsed.value["version"];
19606
19933
  return typeof version === "string" ? version : void 0;