finitemonkey 0.1.0-next.23 → 0.1.0-next.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/finitemonkey.mjs +992 -533
- package/package.json +1 -1
package/dist/finitemonkey.mjs
CHANGED
|
@@ -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 = (
|
|
31
|
+
var parseJson = (text8) => {
|
|
32
32
|
try {
|
|
33
|
-
return accept(JSON.parse(
|
|
33
|
+
return accept(JSON.parse(text8));
|
|
34
34
|
} catch (error) {
|
|
35
35
|
return reject(error instanceof Error ? error.message : String(error));
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
-
var parseJsonObject = (
|
|
39
|
-
const parsed = parseJson(
|
|
38
|
+
var parseJsonObject = (text8) => {
|
|
39
|
+
const parsed = parseJson(text8);
|
|
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,
|
|
163
|
+
var nodeWriteText = (path, text8) => {
|
|
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,
|
|
169
|
+
writeSync(fd, text8);
|
|
170
170
|
fsyncSync(fd);
|
|
171
171
|
} finally {
|
|
172
172
|
closeSync(fd);
|
|
@@ -778,7 +778,7 @@ import { dirname as dirname3 } from "node:path";
|
|
|
778
778
|
|
|
779
779
|
// src/install/hash.ts
|
|
780
780
|
import { createHash } from "node:crypto";
|
|
781
|
-
var hashText = (
|
|
781
|
+
var hashText = (text8) => `sha256:${createHash("sha256").update(text8, "utf8").digest("hex")}`;
|
|
782
782
|
|
|
783
783
|
// src/install/layout.ts
|
|
784
784
|
var STATE_DIR = ".finitemonkey";
|
|
@@ -809,6 +809,76 @@ var PRESETS = ["neutral", "gabriel"];
|
|
|
809
809
|
var parsePreset = (raw) => raw === void 0 ? "neutral" : PRESETS.find((preset) => preset === raw);
|
|
810
810
|
var COMMITTED_STATE = ["config.yml", "runtime/", "tasks/", "rules.yml", "policy.yml", "findings/"];
|
|
811
811
|
|
|
812
|
+
// src/install/hook-entries.ts
|
|
813
|
+
var INSTALLED_HOOKS = [
|
|
814
|
+
{ event: "SessionStart", timeout: 30, guard: false },
|
|
815
|
+
{ event: "UserPromptSubmit", timeout: 10, guard: true },
|
|
816
|
+
{ event: "PreToolUse", timeout: 10, guard: true },
|
|
817
|
+
{ event: "PostToolUse", timeout: 30, guard: false },
|
|
818
|
+
{ event: "Stop", timeout: 600, guard: false },
|
|
819
|
+
{ event: "SubagentStart", timeout: 30, guard: false },
|
|
820
|
+
{ event: "SubagentStop", timeout: 600, guard: false },
|
|
821
|
+
{ event: "PreCompact", timeout: 30, guard: false },
|
|
822
|
+
{ event: "ConfigChange", timeout: 10, guard: true }
|
|
823
|
+
];
|
|
824
|
+
var PROJECT_BIN = `\${CLAUDE_PROJECT_DIR}/${LOCAL_BIN}`;
|
|
825
|
+
var VENDORED_BIN = `\${CLAUDE_PROJECT_DIR}/${VENDOR_BIN}`;
|
|
826
|
+
var isScript = (path) => /\.(c|m)?js$/i.test(path);
|
|
827
|
+
var execForm = (runner, event) => {
|
|
828
|
+
if (runner.kind === "local") return { command: "node", args: [PROJECT_BIN, "hook", event] };
|
|
829
|
+
if (runner.kind === "vendored") return { command: "node", args: [VENDORED_BIN, "hook", event] };
|
|
830
|
+
if (isScript(runner.path)) return { command: "node", args: [runner.path, "hook", event] };
|
|
831
|
+
return { command: runner.path, args: ["hook", event] };
|
|
832
|
+
};
|
|
833
|
+
var hookHandler = (runner, spec2, failClosedSupported) => {
|
|
834
|
+
const failure4 = spec2.guard && failClosedSupported ? { onFailure: "block" } : {};
|
|
835
|
+
const { command, args: args2 } = execForm(runner, spec2.event);
|
|
836
|
+
return { type: "command", command, args: args2, timeout: spec2.timeout, ...failure4 };
|
|
837
|
+
};
|
|
838
|
+
var OWN_SHELL = /(^|[\s/"'])finitemonkey(@[^\s"]+|\.mjs|\.cmd|\.exe)?"?\s+hook\s/;
|
|
839
|
+
var OWN_SCRIPT = /finitemonkey(\.mjs|\.cmd|\.exe)?$/;
|
|
840
|
+
var stringsOf = (value) => Array.isArray(value) ? value.filter((item2) => typeof item2 === "string") : [];
|
|
841
|
+
var isOwnHandler = (handler) => {
|
|
842
|
+
if (!isRecord(handler)) return false;
|
|
843
|
+
const command = handler["command"];
|
|
844
|
+
if (typeof command !== "string") return false;
|
|
845
|
+
const args2 = stringsOf(handler["args"]);
|
|
846
|
+
if (args2.length === 0) return OWN_SHELL.test(`${command} `);
|
|
847
|
+
const hookAt = args2.indexOf("hook");
|
|
848
|
+
if (hookAt < 0) return false;
|
|
849
|
+
const target2 = hookAt === 0 ? command : args2[hookAt - 1] ?? "";
|
|
850
|
+
return OWN_SCRIPT.test(target2);
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
// src/install/statusline.ts
|
|
854
|
+
var OWN = /finitemonkey(\.mjs|\.cmd|\.exe)?"?\s+statusline\s*$/;
|
|
855
|
+
var projectFile = (relative11) => relative11;
|
|
856
|
+
var commandFor = (runner) => {
|
|
857
|
+
if (runner.kind === "local") return `node ${projectFile(LOCAL_BIN)} statusline`;
|
|
858
|
+
if (runner.kind === "vendored") return `node ${projectFile(VENDOR_BIN)} statusline`;
|
|
859
|
+
return isScript(runner.path) ? `node "${runner.path}" statusline` : `"${runner.path}" statusline`;
|
|
860
|
+
};
|
|
861
|
+
var ownStatusLine = (runner) => ({
|
|
862
|
+
type: "command",
|
|
863
|
+
command: commandFor(runner),
|
|
864
|
+
padding: 0
|
|
865
|
+
});
|
|
866
|
+
var isOwnStatusLine = (value) => isRecord(value) && typeof value["command"] === "string" && OWN.test(value["command"]);
|
|
867
|
+
var statusLineCommand = (value) => {
|
|
868
|
+
if (!isRecord(value) || isOwnStatusLine(value)) return void 0;
|
|
869
|
+
const command = value["command"];
|
|
870
|
+
return value["type"] === "command" && typeof command === "string" && command.trim() !== "" ? command : void 0;
|
|
871
|
+
};
|
|
872
|
+
var statusLineIn = (text8) => {
|
|
873
|
+
const parsed = text8 === void 0 ? void 0 : parseJsonObject(text8);
|
|
874
|
+
return parsed?.ok === true ? parsed.value["statusLine"] : void 0;
|
|
875
|
+
};
|
|
876
|
+
var parseStatusLineAdded = (value) => {
|
|
877
|
+
if (!isRecord(value) || !("previous" in value)) return void 0;
|
|
878
|
+
const wrap = value["wrap"];
|
|
879
|
+
return { previous: value["previous"], wrap: typeof wrap === "string" ? wrap : null };
|
|
880
|
+
};
|
|
881
|
+
|
|
812
882
|
// src/install/lock-file.ts
|
|
813
883
|
var ENTRY_KINDS = ["settings", "gitignore", "generated", "markdown", "githook"];
|
|
814
884
|
var strings = (value) => Array.isArray(value) && value.every((item2) => typeof item2 === "string") ? value : void 0;
|
|
@@ -822,7 +892,17 @@ var parseAdded = (value) => {
|
|
|
822
892
|
const marketplace = value["marketplace"] === true;
|
|
823
893
|
const plugin = value["plugin"] === true;
|
|
824
894
|
const language = value["language"];
|
|
825
|
-
const
|
|
895
|
+
const statusLine = parseStatusLineAdded(value["statusLine"]);
|
|
896
|
+
const added = {
|
|
897
|
+
schema,
|
|
898
|
+
attribution,
|
|
899
|
+
deny: deny2,
|
|
900
|
+
ask: ask2,
|
|
901
|
+
blockReads,
|
|
902
|
+
marketplace,
|
|
903
|
+
plugin,
|
|
904
|
+
...statusLine === void 0 ? {} : { statusLine }
|
|
905
|
+
};
|
|
826
906
|
return typeof language === "string" ? { ...added, language } : added;
|
|
827
907
|
};
|
|
828
908
|
var parseEntry = (value) => {
|
|
@@ -863,8 +943,8 @@ var fromRecord = (record3) => {
|
|
|
863
943
|
}
|
|
864
944
|
return accept({ schema, version, preset: known2, installedAt, updatedAt, createdDirs, entries });
|
|
865
945
|
};
|
|
866
|
-
var parseLock = (
|
|
867
|
-
const parsed = parseJsonObject(
|
|
946
|
+
var parseLock = (text8) => {
|
|
947
|
+
const parsed = parseJsonObject(text8);
|
|
868
948
|
return parsed.ok ? fromRecord(parsed.value) : reject(parsed.error);
|
|
869
949
|
};
|
|
870
950
|
var renderLock = (lock) => `${JSON.stringify(lock, null, 2)}
|
|
@@ -926,10 +1006,10 @@ var ensureTree = (tree, key) => {
|
|
|
926
1006
|
tree[key] = fresh3;
|
|
927
1007
|
return fresh3;
|
|
928
1008
|
};
|
|
929
|
-
var parseConfigYaml = (
|
|
1009
|
+
var parseConfigYaml = (text8) => {
|
|
930
1010
|
const root = {};
|
|
931
1011
|
const stack = [{ indent: -1, tree: root, key: void 0 }];
|
|
932
|
-
for (const raw of
|
|
1012
|
+
for (const raw of text8.split(/\r?\n/)) {
|
|
933
1013
|
const line2 = stripComment(raw).trimEnd();
|
|
934
1014
|
if (line2.trim() === "" || line2.trim() === "---") continue;
|
|
935
1015
|
const indent = line2.length - line2.trimStart().length;
|
|
@@ -952,33 +1032,33 @@ var configMap = (tree, ...path) => {
|
|
|
952
1032
|
);
|
|
953
1033
|
};
|
|
954
1034
|
var loadProjectConfig = (runtime, root) => {
|
|
955
|
-
const
|
|
956
|
-
return
|
|
1035
|
+
const text8 = runtime.readText(toAbsolutePath(join2(root, CONFIG_FILE2)));
|
|
1036
|
+
return text8.ok ? parseConfigYaml(text8.value) : {};
|
|
957
1037
|
};
|
|
958
1038
|
|
|
959
1039
|
// src/install/managed-block.ts
|
|
960
|
-
var lineBreak = (
|
|
1040
|
+
var lineBreak = (text8) => text8.includes("\r\n") ? "\r\n" : "\n";
|
|
961
1041
|
var blockRange = (lines3, markers) => {
|
|
962
1042
|
const start = lines3.findIndex((line2) => line2.trimEnd() === markers.begin);
|
|
963
1043
|
if (start < 0) return void 0;
|
|
964
1044
|
const end = lines3.findIndex((line2, index) => index > start && line2.trimEnd() === markers.end);
|
|
965
1045
|
return end < 0 ? void 0 : { start, end };
|
|
966
1046
|
};
|
|
967
|
-
var withManagedBlock = (
|
|
968
|
-
const eol =
|
|
1047
|
+
var withManagedBlock = (text8, markers, body2) => {
|
|
1048
|
+
const eol = text8 === void 0 ? "\n" : lineBreak(text8);
|
|
969
1049
|
const block4 = [markers.begin, ...body2, markers.end];
|
|
970
|
-
if (
|
|
971
|
-
const lines3 =
|
|
1050
|
+
if (text8 === void 0 || text8 === "") return `${block4.join(eol)}${eol}`;
|
|
1051
|
+
const lines3 = text8.split(eol);
|
|
972
1052
|
const range = blockRange(lines3, markers);
|
|
973
1053
|
if (range !== void 0) return [...lines3.slice(0, range.start), ...block4, ...lines3.slice(range.end + 1)].join(eol);
|
|
974
|
-
const separator =
|
|
975
|
-
return `${
|
|
1054
|
+
const separator = text8.endsWith(eol) ? "" : eol;
|
|
1055
|
+
return `${text8}${separator}${block4.join(eol)}${eol}`;
|
|
976
1056
|
};
|
|
977
|
-
var withoutManagedBlock = (
|
|
978
|
-
const eol = lineBreak(
|
|
979
|
-
const lines3 =
|
|
1057
|
+
var withoutManagedBlock = (text8, markers) => {
|
|
1058
|
+
const eol = lineBreak(text8);
|
|
1059
|
+
const lines3 = text8.split(eol);
|
|
980
1060
|
const range = blockRange(lines3, markers);
|
|
981
|
-
if (range === void 0) return
|
|
1061
|
+
if (range === void 0) return text8;
|
|
982
1062
|
return [...lines3.slice(0, range.start), ...lines3.slice(range.end + 1)].join(eol);
|
|
983
1063
|
};
|
|
984
1064
|
|
|
@@ -1075,48 +1155,7 @@ var configTemplate = (preset, lang) => [
|
|
|
1075
1155
|
`lang: ${lang}`,
|
|
1076
1156
|
""
|
|
1077
1157
|
].join("\n");
|
|
1078
|
-
var hasMarker = (
|
|
1079
|
-
|
|
1080
|
-
// src/install/hook-entries.ts
|
|
1081
|
-
var INSTALLED_HOOKS = [
|
|
1082
|
-
{ event: "SessionStart", timeout: 30, guard: false },
|
|
1083
|
-
{ event: "UserPromptSubmit", timeout: 10, guard: true },
|
|
1084
|
-
{ event: "PreToolUse", timeout: 10, guard: true },
|
|
1085
|
-
{ event: "PostToolUse", timeout: 30, guard: false },
|
|
1086
|
-
{ event: "Stop", timeout: 600, guard: false },
|
|
1087
|
-
{ event: "SubagentStart", timeout: 30, guard: false },
|
|
1088
|
-
{ event: "SubagentStop", timeout: 600, guard: false },
|
|
1089
|
-
{ event: "PreCompact", timeout: 30, guard: false },
|
|
1090
|
-
{ event: "ConfigChange", timeout: 10, guard: true }
|
|
1091
|
-
];
|
|
1092
|
-
var PROJECT_BIN = `\${CLAUDE_PROJECT_DIR}/${LOCAL_BIN}`;
|
|
1093
|
-
var VENDORED_BIN = `\${CLAUDE_PROJECT_DIR}/${VENDOR_BIN}`;
|
|
1094
|
-
var isScript = (path) => /\.(c|m)?js$/i.test(path);
|
|
1095
|
-
var execForm = (runner, event) => {
|
|
1096
|
-
if (runner.kind === "local") return { command: "node", args: [PROJECT_BIN, "hook", event] };
|
|
1097
|
-
if (runner.kind === "vendored") return { command: "node", args: [VENDORED_BIN, "hook", event] };
|
|
1098
|
-
if (isScript(runner.path)) return { command: "node", args: [runner.path, "hook", event] };
|
|
1099
|
-
return { command: runner.path, args: ["hook", event] };
|
|
1100
|
-
};
|
|
1101
|
-
var hookHandler = (runner, spec2, failClosedSupported) => {
|
|
1102
|
-
const failure4 = spec2.guard && failClosedSupported ? { onFailure: "block" } : {};
|
|
1103
|
-
const { command, args: args2 } = execForm(runner, spec2.event);
|
|
1104
|
-
return { type: "command", command, args: args2, timeout: spec2.timeout, ...failure4 };
|
|
1105
|
-
};
|
|
1106
|
-
var OWN_SHELL = /(^|[\s/"'])finitemonkey(@[^\s"]+|\.mjs|\.cmd|\.exe)?"?\s+hook\s/;
|
|
1107
|
-
var OWN_SCRIPT = /finitemonkey(\.mjs|\.cmd|\.exe)?$/;
|
|
1108
|
-
var stringsOf = (value) => Array.isArray(value) ? value.filter((item2) => typeof item2 === "string") : [];
|
|
1109
|
-
var isOwnHandler = (handler) => {
|
|
1110
|
-
if (!isRecord(handler)) return false;
|
|
1111
|
-
const command = handler["command"];
|
|
1112
|
-
if (typeof command !== "string") return false;
|
|
1113
|
-
const args2 = stringsOf(handler["args"]);
|
|
1114
|
-
if (args2.length === 0) return OWN_SHELL.test(`${command} `);
|
|
1115
|
-
const hookAt = args2.indexOf("hook");
|
|
1116
|
-
if (hookAt < 0) return false;
|
|
1117
|
-
const target2 = hookAt === 0 ? command : args2[hookAt - 1] ?? "";
|
|
1118
|
-
return OWN_SCRIPT.test(target2);
|
|
1119
|
-
};
|
|
1158
|
+
var hasMarker = (text8) => text8.includes(GENERATED_MARKER);
|
|
1120
1159
|
|
|
1121
1160
|
// src/install/skill-template.ts
|
|
1122
1161
|
var cliCommand = (runner, args2) => {
|
|
@@ -1181,7 +1220,7 @@ var lefthookBody = (runner) => [
|
|
|
1181
1220
|
` run: ${cliCommand(runner, "push check {1}")}`,
|
|
1182
1221
|
" use_stdin: true"
|
|
1183
1222
|
];
|
|
1184
|
-
var definesHook = (
|
|
1223
|
+
var definesHook = (text8) => GIT_HOOK_NAMES.some((hook) => new RegExp(`^${hook}:`, "m").test(withoutManagedBlock(text8, HOOK_MARKERS)));
|
|
1185
1224
|
var lefthookItem = (project, runner, path) => {
|
|
1186
1225
|
const before = project.read(path);
|
|
1187
1226
|
const after = withManagedBlock(before, HOOK_MARKERS, lefthookBody(runner));
|
|
@@ -1206,8 +1245,8 @@ var preCommitBody = (runner, indent) => {
|
|
|
1206
1245
|
];
|
|
1207
1246
|
};
|
|
1208
1247
|
var TOP_KEY = /^[A-Za-z_][\w-]*:/;
|
|
1209
|
-
var reposIsLast = (
|
|
1210
|
-
const keys = withoutManagedBlock(
|
|
1248
|
+
var reposIsLast = (text8) => {
|
|
1249
|
+
const keys = withoutManagedBlock(text8, HOOK_MARKERS).split(/\r?\n/).filter((line2) => TOP_KEY.test(line2));
|
|
1211
1250
|
return keys.at(-1)?.startsWith("repos:") === true;
|
|
1212
1251
|
};
|
|
1213
1252
|
var preCommitItem = (project, runner) => {
|
|
@@ -1245,8 +1284,8 @@ var DEFAULT_POLICY = {
|
|
|
1245
1284
|
guards: {}
|
|
1246
1285
|
};
|
|
1247
1286
|
var pick = (options, value, fallback) => options.find((option) => option === value) ?? fallback;
|
|
1248
|
-
var parsePolicy = (
|
|
1249
|
-
const tree = parseConfigYaml(
|
|
1287
|
+
var parsePolicy = (text8) => {
|
|
1288
|
+
const tree = parseConfigYaml(text8);
|
|
1250
1289
|
const raw = configAt(tree, "promoteAfter");
|
|
1251
1290
|
const promoteAfter = typeof raw === "string" ? Number.parseInt(raw, 10) : Number.NaN;
|
|
1252
1291
|
const guards = Object.fromEntries(
|
|
@@ -1265,8 +1304,8 @@ var parsePolicy = (text7) => {
|
|
|
1265
1304
|
};
|
|
1266
1305
|
var policyPath = (root) => toAbsolutePath(join3(root, POLICY_FILE));
|
|
1267
1306
|
var loadPolicy = (runtime, root) => {
|
|
1268
|
-
const
|
|
1269
|
-
return
|
|
1307
|
+
const text8 = runtime.readText(policyPath(root));
|
|
1308
|
+
return text8.ok ? parsePolicy(text8.value) : DEFAULT_POLICY;
|
|
1270
1309
|
};
|
|
1271
1310
|
|
|
1272
1311
|
// src/review/agents.ts
|
|
@@ -1331,16 +1370,16 @@ var agentMarkdown = (spec2, marker) => [
|
|
|
1331
1370
|
// src/install/agents-block.ts
|
|
1332
1371
|
var MARKDOWN_MARKERS = { begin: MD_BLOCK_BEGIN, end: MD_BLOCK_END };
|
|
1333
1372
|
var IMPORT_LINE = /^@AGENTS\.md[ \t]*$/m;
|
|
1334
|
-
var withAgentsBlock = (
|
|
1335
|
-
|
|
1373
|
+
var withAgentsBlock = (text8, runner, lang) => withManagedBlock(
|
|
1374
|
+
text8,
|
|
1336
1375
|
MARKDOWN_MARKERS,
|
|
1337
1376
|
TEMPLATES[lang].agentsBlock({
|
|
1338
1377
|
gate: cliCommand(runner, "gate --fast --json"),
|
|
1339
1378
|
doctor: cliCommand(runner, "doctor --json")
|
|
1340
1379
|
})
|
|
1341
1380
|
);
|
|
1342
|
-
var withAgentsImport = (
|
|
1343
|
-
var withoutMarkdownBlock = (
|
|
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);
|
|
1344
1383
|
|
|
1345
1384
|
// src/install/ask-floor.ts
|
|
1346
1385
|
var DEFAULT_BRANCHES = ["main", "master"];
|
|
@@ -1416,7 +1455,7 @@ var ruleFor = (raw) => {
|
|
|
1416
1455
|
};
|
|
1417
1456
|
var claudeignoreRules = (texts2) => [
|
|
1418
1457
|
...new Set(
|
|
1419
|
-
texts2.flatMap((
|
|
1458
|
+
texts2.flatMap((text8) => (text8 ?? "").split("\n")).map(ruleFor).filter((rule) => rule !== void 0)
|
|
1420
1459
|
)
|
|
1421
1460
|
];
|
|
1422
1461
|
|
|
@@ -1449,12 +1488,12 @@ import { join as join4 } from "node:path";
|
|
|
1449
1488
|
|
|
1450
1489
|
// src/install/json-text.ts
|
|
1451
1490
|
var DEFAULT_STYLE = { indent: " ", finalNewline: true };
|
|
1452
|
-
var styleOf = (
|
|
1453
|
-
const indented = /\n([ \t]+)\S/.exec(
|
|
1454
|
-
return { indent: indented?.[1] ?? DEFAULT_STYLE.indent, finalNewline:
|
|
1491
|
+
var styleOf = (text8) => {
|
|
1492
|
+
const indented = /\n([ \t]+)\S/.exec(text8);
|
|
1493
|
+
return { indent: indented?.[1] ?? DEFAULT_STYLE.indent, finalNewline: text8.endsWith("\n") };
|
|
1455
1494
|
};
|
|
1456
1495
|
var renderJson = (value, style) => `${JSON.stringify(value, null, style.indent)}${style.finalNewline ? "\n" : ""}`;
|
|
1457
|
-
var parseSettings = (
|
|
1496
|
+
var parseSettings = (text8) => text8 === void 0 || text8.trim() === "" ? { ok: true, value: {} } : parseJsonObject(text8);
|
|
1458
1497
|
var asList = (value) => Array.isArray(value) ? value : [];
|
|
1459
1498
|
var asRecord = (value) => isRecord(value) ? value : {};
|
|
1460
1499
|
var setKey = (record3, key, value) => {
|
|
@@ -1491,8 +1530,8 @@ var WORKFLOW_DIRS = [
|
|
|
1491
1530
|
{ dir: "openspec", name: "openspec" }
|
|
1492
1531
|
];
|
|
1493
1532
|
var fromPackageJson = (project) => {
|
|
1494
|
-
const
|
|
1495
|
-
const parsed =
|
|
1533
|
+
const text8 = project.read("package.json");
|
|
1534
|
+
const parsed = text8 === void 0 ? void 0 : parseJsonObject(text8);
|
|
1496
1535
|
const field = parsed?.ok === true ? parsed.value["packageManager"] : void 0;
|
|
1497
1536
|
if (typeof field !== "string") return void 0;
|
|
1498
1537
|
return PACKAGE_MANAGERS.find(({ name }) => field.startsWith(`${name}@`))?.name;
|
|
@@ -1508,9 +1547,9 @@ var allHandlers = (settings) => {
|
|
|
1508
1547
|
const hooks = settings["hooks"];
|
|
1509
1548
|
return isRecord(hooks) ? Object.keys(hooks).flatMap((event) => handlersOf(settings, event)) : [];
|
|
1510
1549
|
};
|
|
1511
|
-
var settingsInfo = (
|
|
1512
|
-
if (
|
|
1513
|
-
const parsed =
|
|
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);
|
|
1514
1553
|
if (!parsed.ok) return { exists: true, valid: false, userHooks: 0, ownHooks: 0 };
|
|
1515
1554
|
const handlers = allHandlers(parsed.value);
|
|
1516
1555
|
const own = handlers.filter(isOwnHandler).length;
|
|
@@ -1524,8 +1563,8 @@ var pluginAuthorities = (settings, source) => {
|
|
|
1524
1563
|
return LOOP_PLUGINS.includes(name) ? [{ kind: "loop-plugin", source, name }] : [];
|
|
1525
1564
|
});
|
|
1526
1565
|
};
|
|
1527
|
-
var scopeAuthorities = (
|
|
1528
|
-
const parsed =
|
|
1566
|
+
var scopeAuthorities = (text8, source) => {
|
|
1567
|
+
const parsed = text8 === void 0 ? void 0 : parseJsonObject(text8);
|
|
1529
1568
|
if (parsed?.ok !== true) return [];
|
|
1530
1569
|
const stops = ["Stop", "SubagentStop"].flatMap(
|
|
1531
1570
|
(event) => handlersOf(parsed.value, event).filter((handler) => !isOwnHandler(handler)).map((handler) => {
|
|
@@ -1539,8 +1578,8 @@ var userSettingsText = (project, env) => {
|
|
|
1539
1578
|
const home = env["CLAUDE_CONFIG_DIR"] ?? env["HOME"] ?? env["USERPROFILE"];
|
|
1540
1579
|
if (home === void 0) return void 0;
|
|
1541
1580
|
const file = env["CLAUDE_CONFIG_DIR"] === void 0 ? join4(home, ".claude", "settings.json") : join4(home, "settings.json");
|
|
1542
|
-
const
|
|
1543
|
-
return
|
|
1581
|
+
const text8 = project.runtime.readText(toAbsolutePath(file));
|
|
1582
|
+
return text8.ok ? text8.value : void 0;
|
|
1544
1583
|
};
|
|
1545
1584
|
var detectAuthorities = (project, env) => [
|
|
1546
1585
|
...scopeAuthorities(project.read(SETTINGS_FILE), "project"),
|
|
@@ -1560,8 +1599,8 @@ var chooseRunner = (project, bin) => {
|
|
|
1560
1599
|
// src/install/gitignore-block.ts
|
|
1561
1600
|
var MARKERS = { begin: BLOCK_BEGIN, end: BLOCK_END };
|
|
1562
1601
|
var body = () => [`${STATE_DIR}/*`, ...COMMITTED_STATE.map((path) => `!${STATE_DIR}/${path}`)];
|
|
1563
|
-
var withBlock = (
|
|
1564
|
-
var withoutBlock = (
|
|
1602
|
+
var withBlock = (text8) => withManagedBlock(text8, MARKERS, body());
|
|
1603
|
+
var withoutBlock = (text8) => withoutManagedBlock(text8, MARKERS);
|
|
1565
1604
|
|
|
1566
1605
|
// src/core/semver.ts
|
|
1567
1606
|
var SEMVER = /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
@@ -1604,7 +1643,7 @@ var pendingMigrations = (migrations, from, to) => {
|
|
|
1604
1643
|
};
|
|
1605
1644
|
var migrateItem = (item2, previous, steps) => {
|
|
1606
1645
|
if (steps.length === 0 || item2.before === void 0) return item2;
|
|
1607
|
-
const migrated = steps.reduce((
|
|
1646
|
+
const migrated = steps.reduce((text8, step3) => step3(text8), item2.after);
|
|
1608
1647
|
if (migrated === item2.after) return item2;
|
|
1609
1648
|
const recorded = previous.entries.find((entry2) => entry2.path === item2.path)?.hash;
|
|
1610
1649
|
if (recorded !== hashText(item2.before)) {
|
|
@@ -1696,6 +1735,15 @@ var withPermissions = (settings, lists) => {
|
|
|
1696
1735
|
var missing = (wanted, existing) => wanted.filter((rule) => !existing.includes(rule));
|
|
1697
1736
|
var withoutAdded = (existing, added) => existing.filter((rule) => typeof rule !== "string" || !added.includes(rule));
|
|
1698
1737
|
var ownHookMatcher = (recorded) => (handler) => isOwnHandler(handler) || recorded.some((own) => JSON.stringify(own) === JSON.stringify(handler));
|
|
1738
|
+
var statusLineFor = (base, wanted) => {
|
|
1739
|
+
if (wanted.statusLine === void 0 || isOwnStatusLine(base["statusLine"])) return void 0;
|
|
1740
|
+
return { previous: base["statusLine"] ?? null, wrap: wanted.statusLine.wrap ?? null };
|
|
1741
|
+
};
|
|
1742
|
+
var restoreStatusLine = (settings, added) => {
|
|
1743
|
+
if (added.statusLine === void 0 || !isOwnStatusLine(settings["statusLine"])) return settings;
|
|
1744
|
+
const previous = added.statusLine.previous;
|
|
1745
|
+
return setKey(settings, "statusLine", previous === null ? void 0 : previous);
|
|
1746
|
+
};
|
|
1699
1747
|
var mergeSettings = (current2, wanted, recorded) => {
|
|
1700
1748
|
const schema = Object.keys(current2).length === 0;
|
|
1701
1749
|
const base = schema ? { $schema: SETTINGS_SCHEMA_URL } : current2;
|
|
@@ -1719,7 +1767,18 @@ var mergeSettings = (current2, wanted, recorded) => {
|
|
|
1719
1767
|
if (marketplace) settings = withEntry(settings, "extraKnownMarketplaces", MARKETPLACE_KEY, MARKETPLACE_ENTRY);
|
|
1720
1768
|
if (plugin) settings = withEntry(settings, "enabledPlugins", PLUGIN_ID, true);
|
|
1721
1769
|
if (language !== void 0) settings = { ...settings, language };
|
|
1722
|
-
const
|
|
1770
|
+
const status = statusLineFor(base, wanted);
|
|
1771
|
+
if (status !== void 0) settings = { ...settings, statusLine: wanted.statusLine?.entry };
|
|
1772
|
+
const added = {
|
|
1773
|
+
schema,
|
|
1774
|
+
deny: deny2,
|
|
1775
|
+
ask: ask2,
|
|
1776
|
+
blockReads,
|
|
1777
|
+
attribution,
|
|
1778
|
+
marketplace,
|
|
1779
|
+
plugin,
|
|
1780
|
+
...status === void 0 ? {} : { statusLine: status }
|
|
1781
|
+
};
|
|
1723
1782
|
return { settings, added: language === void 0 ? added : { ...added, language } };
|
|
1724
1783
|
};
|
|
1725
1784
|
var sameAttribution = (value) => JSON.stringify(value) === JSON.stringify(ATTRIBUTION_OFF);
|
|
@@ -1739,15 +1798,20 @@ var unmergeSettings = (current2, added, recorded) => {
|
|
|
1739
1798
|
if (added.language !== void 0 && settings["language"] === added.language) {
|
|
1740
1799
|
settings = setKey(settings, "language", void 0);
|
|
1741
1800
|
}
|
|
1801
|
+
settings = restoreStatusLine(settings, added);
|
|
1742
1802
|
if (added.schema && Object.keys(settings).length === 1 && settings["$schema"] === SETTINGS_SCHEMA_URL) return {};
|
|
1743
1803
|
return settings;
|
|
1744
1804
|
};
|
|
1745
1805
|
var ownHandlersIn = (settings) => Object.values(asRecord(settings["hooks"])).flatMap((groups) => asList(groups)).flatMap((group) => asList(asRecord(group)["hooks"])).filter(isOwnHandler).length;
|
|
1746
1806
|
|
|
1807
|
+
// src/install/statusline-search.ts
|
|
1808
|
+
var LOCAL_SETTINGS_FILE = ".claude/settings.local.json";
|
|
1809
|
+
var existingStatusLine = (project, projectSettings, env) => statusLineCommand(projectSettings["statusLine"]) ?? statusLineCommand(statusLineIn(project.read(LOCAL_SETTINGS_FILE))) ?? statusLineCommand(statusLineIn(userSettingsText(project, env)));
|
|
1810
|
+
|
|
1747
1811
|
// src/install/vendor.ts
|
|
1748
|
-
var isBundle = (
|
|
1749
|
-
var isOwnPackage = (
|
|
1750
|
-
const parsed = parseJsonObject(
|
|
1812
|
+
var isBundle = (text8) => text8.startsWith("#!/usr/bin/env node") && text8.includes("finitemonkey");
|
|
1813
|
+
var isOwnPackage = (text8) => {
|
|
1814
|
+
const parsed = parseJsonObject(text8);
|
|
1751
1815
|
return parsed.ok && parsed.value["name"] === "finitemonkey";
|
|
1752
1816
|
};
|
|
1753
1817
|
var vendoredPackage = (version) => `${JSON.stringify({ name: "finitemonkey", version, private: true, type: "module" }, null, 2)}
|
|
@@ -1778,9 +1842,9 @@ var detect = (project, gitRoot2, input) => ({
|
|
|
1778
1842
|
location: locationOf(project.root, input.env)
|
|
1779
1843
|
});
|
|
1780
1844
|
var readPreviousLock = (project) => {
|
|
1781
|
-
const
|
|
1782
|
-
if (
|
|
1783
|
-
const parsed = parseLock(
|
|
1845
|
+
const text8 = project.read(LOCK_FILE);
|
|
1846
|
+
if (text8 === void 0) return accept(void 0);
|
|
1847
|
+
const parsed = parseLock(text8);
|
|
1784
1848
|
return parsed.ok ? accept(parsed.value) : reject({ kind: "lock", detail: parsed.error });
|
|
1785
1849
|
};
|
|
1786
1850
|
var actionFor = (before, after, existing) => {
|
|
@@ -1796,7 +1860,8 @@ var joinAdded = (previous, next2) => previous === void 0 ? next2 : {
|
|
|
1796
1860
|
blockReads: previous.blockReads || next2.blockReads,
|
|
1797
1861
|
marketplace: previous.marketplace || next2.marketplace,
|
|
1798
1862
|
plugin: previous.plugin || next2.plugin,
|
|
1799
|
-
...(previous.language ?? next2.language) === void 0 ? {} : { language: previous.language ?? next2.language }
|
|
1863
|
+
...(previous.language ?? next2.language) === void 0 ? {} : { language: previous.language ?? next2.language },
|
|
1864
|
+
...(previous.statusLine ?? next2.statusLine) === void 0 ? {} : { statusLine: previous.statusLine ?? next2.statusLine }
|
|
1800
1865
|
};
|
|
1801
1866
|
var wantedHooks = (detection) => Object.fromEntries(
|
|
1802
1867
|
INSTALLED_HOOKS.map((spec2) => [spec2.event, hookHandler(detection.runner, spec2, detection.claude.failClosed)])
|
|
@@ -1812,7 +1877,8 @@ var settingsItem = (project, detection, input, previous) => {
|
|
|
1812
1877
|
ask: ASK_RULES,
|
|
1813
1878
|
attributionOff: input.preset === "gabriel",
|
|
1814
1879
|
marketplace: input.marketplace,
|
|
1815
|
-
language: input.claudeLanguage
|
|
1880
|
+
language: input.claudeLanguage,
|
|
1881
|
+
statusLine: { entry: ownStatusLine(detection.runner), wrap: existingStatusLine(project, current2.value, input.env) }
|
|
1816
1882
|
};
|
|
1817
1883
|
const merged2 = mergeSettings(current2.value, wanted, previous?.handlers ?? []);
|
|
1818
1884
|
const after = renderJson(merged2.settings, before === void 0 ? DEFAULT_STYLE : styleOf(before));
|
|
@@ -1827,7 +1893,7 @@ var settingsItem = (project, detection, input, previous) => {
|
|
|
1827
1893
|
});
|
|
1828
1894
|
};
|
|
1829
1895
|
var markdownItems = (project, runner, lang) => [
|
|
1830
|
-
{ path: AGENTS_FILE, render: (
|
|
1896
|
+
{ path: AGENTS_FILE, render: (text8) => withAgentsBlock(text8, runner, lang) },
|
|
1831
1897
|
{ path: CLAUDE_FILE, render: withAgentsImport }
|
|
1832
1898
|
].map(({ path, render: render3 }) => {
|
|
1833
1899
|
const before = project.read(path);
|
|
@@ -1857,7 +1923,7 @@ var playwrightVerifier = (project) => {
|
|
|
1857
1923
|
const wanted = configAt(parseConfigYaml(project.read(CONFIG_FILE) ?? ""), "review", "playwright") === "true";
|
|
1858
1924
|
if (!wanted) return [];
|
|
1859
1925
|
const template = agentMarkdown(withPlaywright(VERIFIER), GENERATED_MARKER);
|
|
1860
|
-
return [{ path: PLAYWRIGHT_VERIFIER, template, isOurs: (
|
|
1926
|
+
return [{ path: PLAYWRIGHT_VERIFIER, template, isOurs: (text8) => text8.includes(GENERATED_MARKER) }];
|
|
1861
1927
|
};
|
|
1862
1928
|
var generatedFiles = (project, input, runner, vendor) => {
|
|
1863
1929
|
const own = [
|
|
@@ -1902,9 +1968,9 @@ var needsWrite = (plan) => hasChanges(plan) || plan.previous === void 0;
|
|
|
1902
1968
|
// src/install/apply.ts
|
|
1903
1969
|
var stamp = (iso) => iso.replace(/[:.]/g, "-");
|
|
1904
1970
|
var ensureParent = (project, relative11) => project.runtime.mkdir(project.abs(dirname3(relative11)));
|
|
1905
|
-
var writeText = (project, relative11,
|
|
1971
|
+
var writeText = (project, relative11, text8) => {
|
|
1906
1972
|
const parent = ensureParent(project, relative11);
|
|
1907
|
-
const written = parent.ok ? project.runtime.writeAtomic(project.abs(relative11),
|
|
1973
|
+
const written = parent.ok ? project.runtime.writeAtomic(project.abs(relative11), text8) : parent;
|
|
1908
1974
|
return written.ok ? accept(void 0) : reject({ path: relative11, detail: written.error });
|
|
1909
1975
|
};
|
|
1910
1976
|
var restore = (project, items) => {
|
|
@@ -2052,8 +2118,8 @@ var projectAt = (root, runtime) => {
|
|
|
2052
2118
|
runtime,
|
|
2053
2119
|
abs,
|
|
2054
2120
|
read: (relative11) => {
|
|
2055
|
-
const
|
|
2056
|
-
return
|
|
2121
|
+
const text8 = runtime.readText(abs(relative11));
|
|
2122
|
+
return text8.ok ? text8.value : void 0;
|
|
2057
2123
|
},
|
|
2058
2124
|
exists: (relative11) => runtime.stat(abs(relative11)) !== void 0,
|
|
2059
2125
|
isDir: (relative11) => runtime.stat(abs(relative11))?.kind === "dir"
|
|
@@ -2126,8 +2192,8 @@ var invalidPreset = (raw) => problem("invalid_arguments", "error.invalid_preset"
|
|
|
2126
2192
|
});
|
|
2127
2193
|
var selfBundle = (ctx) => {
|
|
2128
2194
|
if (!isBundlePath(ctx.runtime.selfPath)) return void 0;
|
|
2129
|
-
const
|
|
2130
|
-
return
|
|
2195
|
+
const text8 = ctx.runtime.readText(ctx.runtime.selfPath);
|
|
2196
|
+
return text8.ok ? text8.value : void 0;
|
|
2131
2197
|
};
|
|
2132
2198
|
var INHERITED = /* @__PURE__ */ new Set(["default", "config", "claude-settings"]);
|
|
2133
2199
|
var planInputFor = (ctx, preset, bin, marketplace) => ({
|
|
@@ -2231,13 +2297,13 @@ var isPlaceholder = (value) => {
|
|
|
2231
2297
|
var isTestOrLockPath = (path) => path !== void 0 && (LOCKFILES.test(toPosixPath(path)) || TEST_PATH.test(toPosixPath(path)));
|
|
2232
2298
|
|
|
2233
2299
|
// src/secrets/entropy.ts
|
|
2234
|
-
var shannonEntropy = (
|
|
2235
|
-
if (
|
|
2300
|
+
var shannonEntropy = (text8) => {
|
|
2301
|
+
if (text8.length === 0) return 0;
|
|
2236
2302
|
const counts = /* @__PURE__ */ new Map();
|
|
2237
|
-
for (const char of
|
|
2303
|
+
for (const char of text8) counts.set(char, (counts.get(char) ?? 0) + 1);
|
|
2238
2304
|
let entropy = 0;
|
|
2239
2305
|
for (const count4 of counts.values()) {
|
|
2240
|
-
const p = count4 /
|
|
2306
|
+
const p = count4 / text8.length;
|
|
2241
2307
|
entropy -= p * Math.log2(p);
|
|
2242
2308
|
}
|
|
2243
2309
|
return entropy;
|
|
@@ -2245,7 +2311,7 @@ var shannonEntropy = (text7) => {
|
|
|
2245
2311
|
|
|
2246
2312
|
// src/secrets/fingerprint.ts
|
|
2247
2313
|
import { createHash as createHash2 } from "node:crypto";
|
|
2248
|
-
var sha256Hex = (
|
|
2314
|
+
var sha256Hex = (text8) => createHash2("sha256").update(text8).digest("hex");
|
|
2249
2315
|
var fingerprintOf = (secret) => sha256Hex(`finitemonkey:secret:${secret}`).slice(0, 16);
|
|
2250
2316
|
var ALNUM = /[A-Za-z0-9]/g;
|
|
2251
2317
|
var MIN_TAIL_LENGTH = 12;
|
|
@@ -2255,9 +2321,9 @@ var tailOf = (secret) => {
|
|
|
2255
2321
|
};
|
|
2256
2322
|
|
|
2257
2323
|
// src/secrets/lines.ts
|
|
2258
|
-
var lineLocator = (
|
|
2324
|
+
var lineLocator = (text8) => {
|
|
2259
2325
|
const starts = [0];
|
|
2260
|
-
for (let index =
|
|
2326
|
+
for (let index = text8.indexOf("\n"); index !== -1; index = text8.indexOf("\n", index + 1)) starts.push(index + 1);
|
|
2261
2327
|
return (offset) => {
|
|
2262
2328
|
let low = 0;
|
|
2263
2329
|
let high = starts.length - 1;
|
|
@@ -2551,12 +2617,12 @@ var hasRandomRun = (secret, length) => (secret.match(new RegExp(`[A-Za-z0-9]{${S
|
|
|
2551
2617
|
);
|
|
2552
2618
|
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);
|
|
2553
2619
|
var NAME_CHAR = /[A-Za-z0-9_.-]/;
|
|
2554
|
-
var nameAround = (
|
|
2620
|
+
var nameAround = (text8, at) => {
|
|
2555
2621
|
let start = at;
|
|
2556
|
-
while (start > 0 && NAME_CHAR.test(
|
|
2622
|
+
while (start > 0 && NAME_CHAR.test(text8.charAt(start - 1))) start -= 1;
|
|
2557
2623
|
let end = at;
|
|
2558
|
-
while (end <
|
|
2559
|
-
return
|
|
2624
|
+
while (end < text8.length && NAME_CHAR.test(text8.charAt(end))) end += 1;
|
|
2625
|
+
return text8.slice(start, end).toLowerCase();
|
|
2560
2626
|
};
|
|
2561
2627
|
var ignoredName = (rule, match) => rule.ignoredNames.length > 0 && rule.ignoredNames.includes(nameAround(match.input, match.index));
|
|
2562
2628
|
var spanOf = (rule, match) => {
|
|
@@ -2566,9 +2632,9 @@ var spanOf = (rule, match) => {
|
|
|
2566
2632
|
const secret = match.input.slice(start, end);
|
|
2567
2633
|
return accepted(rule, secret) ? { rule, start, end, secret } : void 0;
|
|
2568
2634
|
};
|
|
2569
|
-
var ruleSpans = (rule,
|
|
2635
|
+
var ruleSpans = (rule, text8) => {
|
|
2570
2636
|
const spans = [];
|
|
2571
|
-
for (const match of
|
|
2637
|
+
for (const match of text8.matchAll(rule.pattern)) {
|
|
2572
2638
|
const span = spanOf(rule, match);
|
|
2573
2639
|
if (span !== void 0) spans.push(span);
|
|
2574
2640
|
}
|
|
@@ -2594,31 +2660,31 @@ var toFinding = (span, locate2) => {
|
|
|
2594
2660
|
tail: tailOf(span.secret)
|
|
2595
2661
|
};
|
|
2596
2662
|
};
|
|
2597
|
-
var scanText = (
|
|
2598
|
-
if (
|
|
2599
|
-
const rules = applicable(options.rules ?? RULES,
|
|
2663
|
+
var scanText = (text8, options = {}) => {
|
|
2664
|
+
if (text8.length === 0) return [];
|
|
2665
|
+
const rules = applicable(options.rules ?? RULES, text8.toLowerCase(), options.path);
|
|
2600
2666
|
if (rules.length === 0) return [];
|
|
2601
2667
|
const taken = [];
|
|
2602
2668
|
for (const rule of rules) {
|
|
2603
|
-
for (const span of ruleSpans(rule,
|
|
2669
|
+
for (const span of ruleSpans(rule, text8)) if (!overlaps(taken, span)) taken.push(span);
|
|
2604
2670
|
}
|
|
2605
2671
|
if (taken.length === 0) return [];
|
|
2606
|
-
const locate2 = lineLocator(
|
|
2672
|
+
const locate2 = lineLocator(text8);
|
|
2607
2673
|
return taken.sort((a, b) => a.start - b.start).map((span) => toFinding(span, locate2));
|
|
2608
2674
|
};
|
|
2609
2675
|
|
|
2610
2676
|
// src/mask/mask.ts
|
|
2611
2677
|
var maskToken = (finding4) => `«secret:${finding4.ruleId}:${finding4.tail}»`;
|
|
2612
|
-
var maskText = (
|
|
2613
|
-
const findings = scanText(
|
|
2614
|
-
if (findings.length === 0) return
|
|
2678
|
+
var maskText = (text8) => {
|
|
2679
|
+
const findings = scanText(text8, { rules: CREDENTIAL_RULES });
|
|
2680
|
+
if (findings.length === 0) return text8;
|
|
2615
2681
|
let out = "";
|
|
2616
2682
|
let cursor = 0;
|
|
2617
2683
|
for (const finding4 of findings) {
|
|
2618
|
-
out +=
|
|
2684
|
+
out += text8.slice(cursor, finding4.start) + maskToken(finding4);
|
|
2619
2685
|
cursor = finding4.end;
|
|
2620
2686
|
}
|
|
2621
|
-
return out +
|
|
2687
|
+
return out + text8.slice(cursor);
|
|
2622
2688
|
};
|
|
2623
2689
|
var maskDeep = (value) => {
|
|
2624
2690
|
if (typeof value === "string") return maskText(value);
|
|
@@ -2836,14 +2902,14 @@ var toolVersion = (env, command) => {
|
|
|
2836
2902
|
return result.ok ? result.stdout.trim().split("\n")[0] ?? "" : "not found";
|
|
2837
2903
|
};
|
|
2838
2904
|
var lastHookFailures = (env) => {
|
|
2839
|
-
const
|
|
2840
|
-
if (!
|
|
2841
|
-
return
|
|
2905
|
+
const text8 = env.runtime.readText(fmPaths(env.root).log);
|
|
2906
|
+
if (!text8.ok) return [];
|
|
2907
|
+
return text8.value.split("\n").filter((line2) => {
|
|
2842
2908
|
const parsed = line2.trim() === "" ? void 0 : parseJsonObject(line2);
|
|
2843
2909
|
return parsed?.ok === true && isRecord(parsed.value) && parsed.value["event"] === HOOK_FAILURE_EVENT;
|
|
2844
2910
|
}).slice(-MAX_FAILURES);
|
|
2845
2911
|
};
|
|
2846
|
-
var withoutHome = (
|
|
2912
|
+
var withoutHome = (text8, home) => home.length > 1 ? text8.split(home).join("~") : text8;
|
|
2847
2913
|
var reportMarkdown = (env, ctx, envelope) => {
|
|
2848
2914
|
const failures = lastHookFailures(env);
|
|
2849
2915
|
const lines3 = [
|
|
@@ -2879,9 +2945,9 @@ var YIELD_STDOUT = `${JSON.stringify({ continue: true })}
|
|
|
2879
2945
|
`;
|
|
2880
2946
|
var projectOwnsHooks = (ctx) => {
|
|
2881
2947
|
const root = ctx.env["CLAUDE_PROJECT_DIR"] ?? ctx.cwd;
|
|
2882
|
-
const
|
|
2883
|
-
if (!
|
|
2884
|
-
const parsed = parseJsonObject(
|
|
2948
|
+
const text8 = ctx.runtime.readText(toAbsolutePath(join8(root, ...SETTINGS_FILE.split("/"))));
|
|
2949
|
+
if (!text8.ok) return false;
|
|
2950
|
+
const parsed = parseJsonObject(text8.value);
|
|
2885
2951
|
return parsed.ok && ownHandlersIn(parsed.value) > 0;
|
|
2886
2952
|
};
|
|
2887
2953
|
|
|
@@ -2915,8 +2981,8 @@ var settingsPaths = (env) => {
|
|
|
2915
2981
|
];
|
|
2916
2982
|
};
|
|
2917
2983
|
var readSettings = (env) => settingsPaths(env).flatMap(({ scope: scope2, path }) => {
|
|
2918
|
-
const
|
|
2919
|
-
const parsed =
|
|
2984
|
+
const text8 = env.runtime.readText(path);
|
|
2985
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
2920
2986
|
return parsed?.ok === true ? [{ scope: scope2, path, settings: parsed.value }] : [];
|
|
2921
2987
|
});
|
|
2922
2988
|
var scopesWith = (all, test) => all.filter(({ settings }) => test(settings)).map(({ scope: scope2 }) => scope2);
|
|
@@ -3173,6 +3239,36 @@ var deadlineFor = (event, raw) => {
|
|
|
3173
3239
|
return EVENT_DEADLINES[event] ?? DEFAULT_HOOK_DEADLINE_MS;
|
|
3174
3240
|
};
|
|
3175
3241
|
|
|
3242
|
+
// src/gate/hooks/common.ts
|
|
3243
|
+
import { dirname as dirname5, join as join11 } from "node:path";
|
|
3244
|
+
var DEFAULT_GATE_BUDGET_MS = 3e5;
|
|
3245
|
+
var sessionCwd = (input) => input.payload.cwd === void 0 ? input.cwd : toAbsolutePath(input.payload.cwd, input.cwd);
|
|
3246
|
+
var MAX_DEPTH = 64;
|
|
3247
|
+
var findRepoRoot = (runtime, start) => {
|
|
3248
|
+
let current2 = start;
|
|
3249
|
+
for (let depth = 0; depth < MAX_DEPTH; depth += 1) {
|
|
3250
|
+
if (runtime.stat(toAbsolutePath(join11(current2, ".git"))) !== void 0) return current2;
|
|
3251
|
+
const parent = toAbsolutePath(dirname5(current2));
|
|
3252
|
+
if (parent === current2) return void 0;
|
|
3253
|
+
current2 = parent;
|
|
3254
|
+
}
|
|
3255
|
+
return void 0;
|
|
3256
|
+
};
|
|
3257
|
+
var projectPaths = (input) => {
|
|
3258
|
+
const root = findRepoRoot(input.runtime, sessionCwd(input));
|
|
3259
|
+
return root === void 0 ? void 0 : fmPaths(root);
|
|
3260
|
+
};
|
|
3261
|
+
var gateBudget = (input) => {
|
|
3262
|
+
const parsed = Number.parseInt(input.env["FINITEMONKEY_GATE_BUDGET_MS"] ?? "", 10);
|
|
3263
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_GATE_BUDGET_MS;
|
|
3264
|
+
};
|
|
3265
|
+
var allowWith = (output) => ({ kind: "allow", output });
|
|
3266
|
+
var contextOutput = (event, additionalContext, systemMessage) => ({
|
|
3267
|
+
hookSpecificOutput: { hookEventName: event, additionalContext },
|
|
3268
|
+
...systemMessage === void 0 ? {} : { systemMessage }
|
|
3269
|
+
});
|
|
3270
|
+
var human = (text8) => `finitemonkey: ${text8}`;
|
|
3271
|
+
|
|
3176
3272
|
// src/hooks/fields.ts
|
|
3177
3273
|
var textField = (record3, ...names) => {
|
|
3178
3274
|
for (const name of names) {
|
|
@@ -3202,7 +3298,7 @@ var editsOf = (input) => {
|
|
|
3202
3298
|
});
|
|
3203
3299
|
};
|
|
3204
3300
|
var applyEdits = (before, edits) => edits.reduce(
|
|
3205
|
-
(
|
|
3301
|
+
(text8, edit) => edit.all ? text8.split(edit.oldText).join(edit.newText) : text8.replace(edit.oldText, () => edit.newText),
|
|
3206
3302
|
before
|
|
3207
3303
|
);
|
|
3208
3304
|
|
|
@@ -3222,7 +3318,7 @@ var familyOf = (base) => {
|
|
|
3222
3318
|
if (found === void 0) throw new Error(`no reason family for ${base}`);
|
|
3223
3319
|
return found;
|
|
3224
3320
|
};
|
|
3225
|
-
var capReason = (
|
|
3321
|
+
var capReason = (text8) => text8.length <= REASON_LIMIT ? text8 : `${text8.slice(0, REASON_LIMIT - 1)}…`;
|
|
3226
3322
|
var modelReason = (t, base, params2 = {}) => {
|
|
3227
3323
|
const claim = `${base}.claim`;
|
|
3228
3324
|
const requires = `${base}.requires`;
|
|
@@ -3249,9 +3345,9 @@ var textRecord = (value) => isRecord(value) ? Object.fromEntries(
|
|
|
3249
3345
|
Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
|
|
3250
3346
|
) : {};
|
|
3251
3347
|
var readJsonFile = (runtime, path) => {
|
|
3252
|
-
const
|
|
3253
|
-
if (!
|
|
3254
|
-
const parsed = parseJsonObject(
|
|
3348
|
+
const text8 = runtime.readText(path);
|
|
3349
|
+
if (!text8.ok) return void 0;
|
|
3350
|
+
const parsed = parseJsonObject(text8.value);
|
|
3255
3351
|
return parsed.ok ? parsed.value : void 0;
|
|
3256
3352
|
};
|
|
3257
3353
|
var writeJsonFile = (runtime, path, value) => runtime.writeText(path, `${JSON.stringify({ schemaVersion: 1, ...value }, null, 2)}
|
|
@@ -3364,7 +3460,7 @@ var SHELL_TOOLS = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
|
|
|
3364
3460
|
import { posix } from "node:path";
|
|
3365
3461
|
|
|
3366
3462
|
// src/shell/scan.ts
|
|
3367
|
-
var cursorOf = (
|
|
3463
|
+
var cursorOf = (text8) => ({ text: text8, pos: 0, incomplete: false, subs: [] });
|
|
3368
3464
|
var peek = (cursor, offset = 0) => cursor.text.charAt(cursor.pos + offset);
|
|
3369
3465
|
var atEnd = (cursor) => cursor.pos >= cursor.text.length;
|
|
3370
3466
|
var skipQuoted = (cursor, quote) => {
|
|
@@ -3579,8 +3675,8 @@ var nextToken = (cursor, tokens, pending) => {
|
|
|
3579
3675
|
tokens.push({ kind: "op", text: op });
|
|
3580
3676
|
if (op === "\n") flushHeredocs(cursor, tokens, pending);
|
|
3581
3677
|
};
|
|
3582
|
-
var lexBash = (
|
|
3583
|
-
const cursor = cursorOf(
|
|
3678
|
+
var lexBash = (text8) => {
|
|
3679
|
+
const cursor = cursorOf(text8.replace(/\r\n/g, "\n"));
|
|
3584
3680
|
const tokens = [];
|
|
3585
3681
|
const pending = [];
|
|
3586
3682
|
skipBlank(cursor);
|
|
@@ -3728,9 +3824,9 @@ var next = (cursor, tokens, cmd) => {
|
|
|
3728
3824
|
}
|
|
3729
3825
|
const op = OPERATORS2.find((candidate) => cursor.text.startsWith(candidate, cursor.pos));
|
|
3730
3826
|
if (op !== void 0 || cmd && peek(cursor) === "&") {
|
|
3731
|
-
const
|
|
3732
|
-
cursor.pos +=
|
|
3733
|
-
tokens.push({ kind: "op", text:
|
|
3827
|
+
const text8 = op ?? "&";
|
|
3828
|
+
cursor.pos += text8.length;
|
|
3829
|
+
tokens.push({ kind: "op", text: text8 });
|
|
3734
3830
|
return;
|
|
3735
3831
|
}
|
|
3736
3832
|
if ((peek(cursor) === "&" || peek(cursor) === ".") && peek(cursor, 1) === " " && commandStart(tokens)) {
|
|
@@ -3739,8 +3835,8 @@ var next = (cursor, tokens, cmd) => {
|
|
|
3739
3835
|
}
|
|
3740
3836
|
tokens.push(word(cursor, cmd));
|
|
3741
3837
|
};
|
|
3742
|
-
var lexPowerShell = (
|
|
3743
|
-
const cursor = cursorOf(
|
|
3838
|
+
var lexPowerShell = (text8, cmd = false) => {
|
|
3839
|
+
const cursor = cursorOf(text8.replace(/\r\n/g, "\n"));
|
|
3744
3840
|
const tokens = [];
|
|
3745
3841
|
const skip = () => {
|
|
3746
3842
|
while (peek(cursor) === " " || peek(cursor) === " ") cursor.pos += 1;
|
|
@@ -3939,11 +4035,11 @@ var unwrap = (initial, stdin) => {
|
|
|
3939
4035
|
};
|
|
3940
4036
|
|
|
3941
4037
|
// src/shell/normalize.ts
|
|
3942
|
-
var
|
|
4038
|
+
var MAX_DEPTH2 = 8;
|
|
3943
4039
|
var ASSIGNMENT2 = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/s;
|
|
3944
4040
|
var PS_ENV = /^\$env:([A-Za-z_][A-Za-z0-9_]*)=?(.*)$/is;
|
|
3945
4041
|
var CHANGES_DIR = /* @__PURE__ */ new Set(["cd", "pushd", "set-location", "push-location"]);
|
|
3946
|
-
var lex = (
|
|
4042
|
+
var lex = (text8, language) => language === "bash" ? lexBash(text8) : lexPowerShell(text8, language === "cmd");
|
|
3947
4043
|
var isWindowsAbsolute = (path) => /^[A-Za-z]:\//.test(path);
|
|
3948
4044
|
var joinDir = (current2, target2) => {
|
|
3949
4045
|
const next2 = toPosixPath(target2);
|
|
@@ -4014,21 +4110,21 @@ var visit = (raw, language, context, depth, collector) => {
|
|
|
4014
4110
|
emit(result.stage, raw, language, context, collector);
|
|
4015
4111
|
for (const extra of result.extra) emit(extra, raw, language, context, collector);
|
|
4016
4112
|
};
|
|
4017
|
-
var collect = (
|
|
4018
|
-
if (depth >
|
|
4113
|
+
var collect = (text8, language, context, depth, collector) => {
|
|
4114
|
+
if (depth > MAX_DEPTH2) {
|
|
4019
4115
|
collector.incomplete = true;
|
|
4020
4116
|
return;
|
|
4021
4117
|
}
|
|
4022
|
-
const lexed = lex(
|
|
4118
|
+
const lexed = lex(text8, language);
|
|
4023
4119
|
if (lexed.incomplete) collector.incomplete = true;
|
|
4024
4120
|
for (const substitution of lexed.substitutions) {
|
|
4025
4121
|
collect(substitution, language, { ...context }, depth + 1, collector);
|
|
4026
4122
|
}
|
|
4027
4123
|
for (const raw of rawCommands(lexed.tokens)) visit(raw, language, context, depth, collector);
|
|
4028
4124
|
};
|
|
4029
|
-
var normalizeCommand = (
|
|
4125
|
+
var normalizeCommand = (text8, dialect = "bash") => {
|
|
4030
4126
|
const collector = { out: [], incomplete: false };
|
|
4031
|
-
collect(
|
|
4127
|
+
collect(text8, dialect, { env: {}, dir: void 0, via: [] }, 0, collector);
|
|
4032
4128
|
return { commands: collector.out, incomplete: collector.incomplete };
|
|
4033
4129
|
};
|
|
4034
4130
|
var dialectOf = (toolName3) => toolName3 === "PowerShell" ? "powershell" : "bash";
|
|
@@ -4040,7 +4136,7 @@ var segmentsOf = (command) => normalizeCommand(command).commands.map((item2) =>
|
|
|
4040
4136
|
|
|
4041
4137
|
// src/guards/shell.ts
|
|
4042
4138
|
var HEREDOC = /^<<[-<]?$/;
|
|
4043
|
-
var parseShell = (
|
|
4139
|
+
var parseShell = (text8) => normalizeCommand(text8).commands.map((command) => ({
|
|
4044
4140
|
env: command.env,
|
|
4045
4141
|
argv: command.program === "" ? [] : [command.program, ...command.args],
|
|
4046
4142
|
redirects: command.redirects.filter((redirect) => !HEREDOC.test(redirect.op)).map((redirect) => redirect.target)
|
|
@@ -4202,9 +4298,9 @@ var writtenPaths = (command) => {
|
|
|
4202
4298
|
if (name === "git" && GIT_WRITERS.has(operands2[0] ?? "")) return [...command.redirects, ...operands2.slice(1)];
|
|
4203
4299
|
return command.redirects;
|
|
4204
4300
|
};
|
|
4205
|
-
var mentioned = (
|
|
4206
|
-
if (/\.finitemonkey[\\/]allow/.test(
|
|
4207
|
-
if (/\.finitemonkey|\.gitleaksignore/.test(
|
|
4301
|
+
var mentioned = (text8) => {
|
|
4302
|
+
if (/\.finitemonkey[\\/]allow/.test(text8)) return "allow_store";
|
|
4303
|
+
if (/\.finitemonkey|\.gitleaksignore/.test(text8)) return "policy";
|
|
4208
4304
|
return void 0;
|
|
4209
4305
|
};
|
|
4210
4306
|
var bashSelfTargets = (root, commandLine) => parseShell(commandLine).flatMap(
|
|
@@ -4244,7 +4340,7 @@ var selfGuard = (input) => {
|
|
|
4244
4340
|
};
|
|
4245
4341
|
|
|
4246
4342
|
// src/guards/tests/hook.ts
|
|
4247
|
-
import { join as
|
|
4343
|
+
import { join as join12, relative as relative3 } from "node:path";
|
|
4248
4344
|
|
|
4249
4345
|
// src/guards/tests/patterns.ts
|
|
4250
4346
|
var JS_TEST = /(^|\/)(__tests__|__test__)\/|\.(test|spec)\.[cm]?[jt]sx?$|(^|\/)tests?\/.*\.[cm]?[jt]sx?$/;
|
|
@@ -4408,8 +4504,8 @@ var compareConfig = (path, before, after, accepted2) => {
|
|
|
4408
4504
|
// src/guards/tests/hook.ts
|
|
4409
4505
|
var localPath = (root, path) => toPosixPath(relative3(root, toAbsolutePath(path, root)));
|
|
4410
4506
|
var read = (input, path) => {
|
|
4411
|
-
const
|
|
4412
|
-
return
|
|
4507
|
+
const text8 = input.runtime.readText(path);
|
|
4508
|
+
return text8.ok ? text8.value : void 0;
|
|
4413
4509
|
};
|
|
4414
4510
|
var proposed = (tool, args2, before) => {
|
|
4415
4511
|
if (tool === "Write") return textField(args2, "content") ?? "";
|
|
@@ -4434,7 +4530,7 @@ var shellViolations = (input, root) => {
|
|
|
4434
4530
|
return parseShell(command).flatMap(
|
|
4435
4531
|
(simple) => writtenPaths(simple).flatMap((path) => {
|
|
4436
4532
|
const local = localPath(root, path);
|
|
4437
|
-
const exists = input.runtime.stat(toAbsolutePath(
|
|
4533
|
+
const exists = input.runtime.stat(toAbsolutePath(join12(root, local))) !== void 0;
|
|
4438
4534
|
if (!exists) return [];
|
|
4439
4535
|
if (isProtectedConfig(local)) return [{ kind: "config_changed", path: local }];
|
|
4440
4536
|
return isTestFile(local) ? [{ kind: "test_removed", path: local }] : [];
|
|
@@ -4455,38 +4551,6 @@ var testsGuard = (input) => {
|
|
|
4455
4551
|
|
|
4456
4552
|
// src/look/reads.ts
|
|
4457
4553
|
import { relative as relative4 } from "node:path";
|
|
4458
|
-
|
|
4459
|
-
// src/gate/hooks/common.ts
|
|
4460
|
-
import { dirname as dirname5, join as join12 } from "node:path";
|
|
4461
|
-
var DEFAULT_GATE_BUDGET_MS = 3e5;
|
|
4462
|
-
var sessionCwd = (input) => input.payload.cwd === void 0 ? input.cwd : toAbsolutePath(input.payload.cwd, input.cwd);
|
|
4463
|
-
var MAX_DEPTH2 = 64;
|
|
4464
|
-
var findRepoRoot = (runtime, start) => {
|
|
4465
|
-
let current2 = start;
|
|
4466
|
-
for (let depth = 0; depth < MAX_DEPTH2; depth += 1) {
|
|
4467
|
-
if (runtime.stat(toAbsolutePath(join12(current2, ".git"))) !== void 0) return current2;
|
|
4468
|
-
const parent = toAbsolutePath(dirname5(current2));
|
|
4469
|
-
if (parent === current2) return void 0;
|
|
4470
|
-
current2 = parent;
|
|
4471
|
-
}
|
|
4472
|
-
return void 0;
|
|
4473
|
-
};
|
|
4474
|
-
var projectPaths = (input) => {
|
|
4475
|
-
const root = findRepoRoot(input.runtime, sessionCwd(input));
|
|
4476
|
-
return root === void 0 ? void 0 : fmPaths(root);
|
|
4477
|
-
};
|
|
4478
|
-
var gateBudget = (input) => {
|
|
4479
|
-
const parsed = Number.parseInt(input.env["FINITEMONKEY_GATE_BUDGET_MS"] ?? "", 10);
|
|
4480
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_GATE_BUDGET_MS;
|
|
4481
|
-
};
|
|
4482
|
-
var allowWith = (output) => ({ kind: "allow", output });
|
|
4483
|
-
var contextOutput = (event, additionalContext, systemMessage) => ({
|
|
4484
|
-
hookSpecificOutput: { hookEventName: event, additionalContext },
|
|
4485
|
-
...systemMessage === void 0 ? {} : { systemMessage }
|
|
4486
|
-
});
|
|
4487
|
-
var human = (text7) => `finitemonkey: ${text7}`;
|
|
4488
|
-
|
|
4489
|
-
// src/look/reads.ts
|
|
4490
4554
|
var READS_FILE = "look-reads.json";
|
|
4491
4555
|
var KEEP = 500;
|
|
4492
4556
|
var readLookReads = (runtime, paths) => {
|
|
@@ -4546,8 +4610,8 @@ var parseMarkers = (prompt) => {
|
|
|
4546
4610
|
// src/policy/pause.ts
|
|
4547
4611
|
var PAUSE_FILE = "pause.json";
|
|
4548
4612
|
var readPause = (runtime, paths) => {
|
|
4549
|
-
const
|
|
4550
|
-
const parsed =
|
|
4613
|
+
const text8 = runtime.readText(paths.cache(PAUSE_FILE));
|
|
4614
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
4551
4615
|
if (parsed?.ok !== true || !isRecord(parsed.value)) return void 0;
|
|
4552
4616
|
const { sessionId, since, by } = parsed.value;
|
|
4553
4617
|
if (typeof sessionId !== "string" || typeof since !== "string") return void 0;
|
|
@@ -4602,9 +4666,9 @@ var countIn = (value) => {
|
|
|
4602
4666
|
if (Array.isArray(value)) return value.reduce((sum, item2) => sum + countIn(item2), 0);
|
|
4603
4667
|
return isRecord(value) ? Object.values(value).reduce((sum, item2) => sum + countIn(item2), 0) : 0;
|
|
4604
4668
|
};
|
|
4605
|
-
var censusOf = (
|
|
4606
|
-
if (
|
|
4607
|
-
const parsed = parseJsonObject(
|
|
4669
|
+
var censusOf = (text8) => {
|
|
4670
|
+
if (text8 === void 0) return { hooks: 0, disabled: false };
|
|
4671
|
+
const parsed = parseJsonObject(text8);
|
|
4608
4672
|
if (!parsed.ok) return void 0;
|
|
4609
4673
|
return { hooks: countIn(parsed.value["hooks"]), disabled: parsed.value["disableAllHooks"] === true };
|
|
4610
4674
|
};
|
|
@@ -4614,8 +4678,8 @@ var settingsFiles = (paths, env) => [
|
|
|
4614
4678
|
...env["HOME"] === void 0 ? [] : [toAbsolutePath(join13(env["HOME"], ".claude", "settings.json"))]
|
|
4615
4679
|
];
|
|
4616
4680
|
var read2 = (runtime, path) => {
|
|
4617
|
-
const
|
|
4618
|
-
return
|
|
4681
|
+
const text8 = runtime.readText(path);
|
|
4682
|
+
return text8.ok ? text8.value : void 0;
|
|
4619
4683
|
};
|
|
4620
4684
|
var known = (runtime, paths) => {
|
|
4621
4685
|
const raw = readJsonFile(runtime, paths.cache(CONFIG_FILE3))?.["hooks"];
|
|
@@ -4670,9 +4734,9 @@ var LOCKFILES2 = [
|
|
|
4670
4734
|
];
|
|
4671
4735
|
var packageManager = (files) => LOCKFILES2.find(([file]) => files.exists(file))?.[1] ?? "npm";
|
|
4672
4736
|
var scriptChecks = (files) => {
|
|
4673
|
-
const
|
|
4674
|
-
if (
|
|
4675
|
-
const parsed = parseJsonObject(
|
|
4737
|
+
const text8 = files.read("package.json");
|
|
4738
|
+
if (text8 === void 0) return [];
|
|
4739
|
+
const parsed = parseJsonObject(text8);
|
|
4676
4740
|
const scripts = parsed.ok ? parsed.value["scripts"] : void 0;
|
|
4677
4741
|
if (!isRecord(scripts)) return [];
|
|
4678
4742
|
const pm = packageManager(files);
|
|
@@ -4762,16 +4826,16 @@ var unescapeDouble = (body2) => {
|
|
|
4762
4826
|
}
|
|
4763
4827
|
return accept(out);
|
|
4764
4828
|
};
|
|
4765
|
-
var quotedEnd = (
|
|
4766
|
-
const quote =
|
|
4767
|
-
for (let index = start + 1; index <
|
|
4768
|
-
const char =
|
|
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];
|
|
4769
4833
|
if (quote === '"' && char === "\\") {
|
|
4770
4834
|
index += 1;
|
|
4771
4835
|
continue;
|
|
4772
4836
|
}
|
|
4773
4837
|
if (char !== quote) continue;
|
|
4774
|
-
if (quote === "'" &&
|
|
4838
|
+
if (quote === "'" && text8[index + 1] === "'") {
|
|
4775
4839
|
index += 1;
|
|
4776
4840
|
continue;
|
|
4777
4841
|
}
|
|
@@ -4779,20 +4843,20 @@ var quotedEnd = (text7, start) => {
|
|
|
4779
4843
|
}
|
|
4780
4844
|
return -1;
|
|
4781
4845
|
};
|
|
4782
|
-
var parseQuoted = (
|
|
4783
|
-
const end = quotedEnd(
|
|
4784
|
-
if (end !==
|
|
4785
|
-
const body2 =
|
|
4786
|
-
return
|
|
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, "'"));
|
|
4787
4851
|
};
|
|
4788
4852
|
var NUMBER = /^-?(0|[1-9]\d*)(\.\d+)?([eE][-+]?\d+)?$/;
|
|
4789
4853
|
var UNSUPPORTED = /^[&*!|>%@`]/;
|
|
4790
|
-
var parsePlain = (
|
|
4791
|
-
if (UNSUPPORTED.test(
|
|
4792
|
-
if (
|
|
4793
|
-
if (
|
|
4794
|
-
if (NUMBER.test(
|
|
4795
|
-
return accept(
|
|
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);
|
|
4796
4860
|
};
|
|
4797
4861
|
|
|
4798
4862
|
// src/core/yaml/flow.ts
|
|
@@ -4819,17 +4883,17 @@ var splitTop = (body2) => {
|
|
|
4819
4883
|
if (last.trim() !== "" || parts.length > 0) parts.push(last);
|
|
4820
4884
|
return accept(parts.map((part2) => part2.trim()));
|
|
4821
4885
|
};
|
|
4822
|
-
var splitKey = (
|
|
4886
|
+
var splitKey = (text8) => {
|
|
4823
4887
|
let index = 0;
|
|
4824
|
-
if (
|
|
4825
|
-
index = quotedEnd(
|
|
4888
|
+
if (text8.startsWith('"') || text8.startsWith("'")) {
|
|
4889
|
+
index = quotedEnd(text8, 0) + 1;
|
|
4826
4890
|
if (index <= 0) return void 0;
|
|
4827
4891
|
}
|
|
4828
|
-
for (; index <
|
|
4829
|
-
const char =
|
|
4830
|
-
if (char === "#" &&
|
|
4831
|
-
if (char === ":" && (index + 1 ===
|
|
4832
|
-
return { key:
|
|
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() };
|
|
4833
4897
|
}
|
|
4834
4898
|
}
|
|
4835
4899
|
return void 0;
|
|
@@ -4858,39 +4922,39 @@ var flowSeq = (parts) => {
|
|
|
4858
4922
|
}
|
|
4859
4923
|
return accept(out);
|
|
4860
4924
|
};
|
|
4861
|
-
var parseValue = (
|
|
4862
|
-
if (
|
|
4863
|
-
const first =
|
|
4864
|
-
if (first === '"' || first === "'") return parseQuoted(
|
|
4925
|
+
var parseValue = (text8) => {
|
|
4926
|
+
if (text8 === "") return accept(null);
|
|
4927
|
+
const first = text8[0] ?? "";
|
|
4928
|
+
if (first === '"' || first === "'") return parseQuoted(text8);
|
|
4865
4929
|
const close2 = OPEN[first];
|
|
4866
|
-
if (close2 === void 0) return parsePlain(
|
|
4867
|
-
if (!
|
|
4868
|
-
const parts = splitTop(
|
|
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));
|
|
4869
4933
|
if (!parts.ok) return parts;
|
|
4870
4934
|
return first === "[" ? flowSeq(parts.value) : flowMap(parts.value);
|
|
4871
4935
|
};
|
|
4872
4936
|
|
|
4873
4937
|
// src/core/yaml/parse.ts
|
|
4874
4938
|
var BOM = new RegExp(`^${String.fromCharCode(65279)}`);
|
|
4875
|
-
var stripComment2 = (
|
|
4876
|
-
for (let index = 0; index <
|
|
4877
|
-
const char =
|
|
4939
|
+
var stripComment2 = (text8) => {
|
|
4940
|
+
for (let index = 0; index < text8.length; index += 1) {
|
|
4941
|
+
const char = text8[index];
|
|
4878
4942
|
if (char === '"' || char === "'") {
|
|
4879
|
-
const end = quotedEnd(
|
|
4880
|
-
if (end < 0) return
|
|
4943
|
+
const end = quotedEnd(text8, index);
|
|
4944
|
+
if (end < 0) return text8;
|
|
4881
4945
|
index = end;
|
|
4882
|
-
} else if (char === "#" && (index === 0 ||
|
|
4946
|
+
} else if (char === "#" && (index === 0 || text8[index - 1] === " ")) return text8.slice(0, index);
|
|
4883
4947
|
}
|
|
4884
|
-
return
|
|
4948
|
+
return text8;
|
|
4885
4949
|
};
|
|
4886
4950
|
var toLines = (source) => {
|
|
4887
4951
|
const lines3 = [];
|
|
4888
4952
|
const raw = source.replace(BOM, "").split(/\r?\n/);
|
|
4889
4953
|
for (const [index, full] of raw.entries()) {
|
|
4890
|
-
const
|
|
4891
|
-
const body2 =
|
|
4954
|
+
const text8 = stripComment2(full).trimEnd();
|
|
4955
|
+
const body2 = text8.trimStart();
|
|
4892
4956
|
if (body2 === "" || body2 === "---" && lines3.length === 0) continue;
|
|
4893
|
-
const indentText =
|
|
4957
|
+
const indentText = text8.slice(0, text8.length - body2.length);
|
|
4894
4958
|
if (indentText.includes(" ")) return reject({ line: index + 1, message: "tabs are not allowed for indentation" });
|
|
4895
4959
|
if (body2 === "---" || body2 === "...")
|
|
4896
4960
|
return reject({ line: index + 1, message: "only one YAML document is allowed" });
|
|
@@ -4899,11 +4963,11 @@ var toLines = (source) => {
|
|
|
4899
4963
|
return accept(lines3);
|
|
4900
4964
|
};
|
|
4901
4965
|
var fail = (line2, message) => reject({ line: line2?.number ?? 0, message });
|
|
4902
|
-
var scalar = (line2,
|
|
4903
|
-
const value = parseValue(
|
|
4966
|
+
var scalar = (line2, text8) => {
|
|
4967
|
+
const value = parseValue(text8);
|
|
4904
4968
|
return value.ok ? value : fail(line2, value.error);
|
|
4905
4969
|
};
|
|
4906
|
-
var isItem = (
|
|
4970
|
+
var isItem = (text8) => text8 === "-" || text8.startsWith("- ");
|
|
4907
4971
|
var child = (lines3, index, parent, sameIndentSeq) => {
|
|
4908
4972
|
const next2 = lines3[index];
|
|
4909
4973
|
if (next2 !== void 0 && next2.indent > parent.indent) return block(lines3, index, next2.indent);
|
|
@@ -5146,8 +5210,8 @@ var validateContract = (value, implicit = false) => {
|
|
|
5146
5210
|
// src/task/load.ts
|
|
5147
5211
|
var contractHash = (contract) => sha256Hex(JSON.stringify(contract.acceptance.map((check2) => [check2.id, check2.kind, check2.run ?? ""])));
|
|
5148
5212
|
var taskRelative = (id) => `.finitemonkey/tasks/${id}.yml`;
|
|
5149
|
-
var parseContract = (id,
|
|
5150
|
-
const yaml = parseYaml(
|
|
5213
|
+
var parseContract = (id, text8) => {
|
|
5214
|
+
const yaml = parseYaml(text8);
|
|
5151
5215
|
if (!yaml.ok)
|
|
5152
5216
|
return reject({ kind: "invalid", id, issues: [`line ${String(yaml.error.line)}: ${yaml.error.message}`] });
|
|
5153
5217
|
const valid = validateContract(yaml.value);
|
|
@@ -5257,10 +5321,10 @@ var resolveBase = (git2, declared) => {
|
|
|
5257
5321
|
const merged2 = remote === void 0 ? void 0 : git2.out(["merge-base", "HEAD", remote])?.trim();
|
|
5258
5322
|
return nonEmpty(merged2) ?? nonEmpty(headSha(git2)) ?? EMPTY_TREE;
|
|
5259
5323
|
};
|
|
5260
|
-
var nulFields = (
|
|
5324
|
+
var nulFields = (text8) => text8.split("\0").filter((part2) => part2 !== "");
|
|
5261
5325
|
var untracked = (git2) => nulFields(git2.out(["ls-files", "--others", "--exclude-standard", "-z", "--", ".", EXCLUDE_FM]) ?? "");
|
|
5262
|
-
var parseNameStatus = (
|
|
5263
|
-
const fields = nulFields(
|
|
5326
|
+
var parseNameStatus = (text8) => {
|
|
5327
|
+
const fields = nulFields(text8);
|
|
5264
5328
|
const changes = [];
|
|
5265
5329
|
for (let index = 0; index < fields.length; index += 1) {
|
|
5266
5330
|
const code = fields[index] ?? "";
|
|
@@ -5328,8 +5392,8 @@ var aheadOfUpstream = (git2) => {
|
|
|
5328
5392
|
|
|
5329
5393
|
// src/gate/diff-shape.ts
|
|
5330
5394
|
var untrackedLines = (runtime, root, changes) => changes.filter((change) => change.status === "A").reduce((sum, change) => {
|
|
5331
|
-
const
|
|
5332
|
-
return sum + (
|
|
5395
|
+
const text8 = runtime.readText(toAbsolutePath(join14(root, change.path)));
|
|
5396
|
+
return sum + (text8.ok ? text8.value.split("\n").length : 0);
|
|
5333
5397
|
}, 0);
|
|
5334
5398
|
var diffShape = (runtime, git2, base, changes) => ({
|
|
5335
5399
|
files: changes.map((change) => change.path),
|
|
@@ -5352,8 +5416,8 @@ var ciChecks = (value) => Array.isArray(value) ? value.flatMap((item2) => {
|
|
|
5352
5416
|
const name = optionalString(item2, "name") ?? optionalString(item2, "context") ?? "?";
|
|
5353
5417
|
return [{ name, state: ciState(item2) }];
|
|
5354
5418
|
}) : [];
|
|
5355
|
-
var parsePrView = (
|
|
5356
|
-
const parsed = parseJsonObject(
|
|
5419
|
+
var parsePrView = (text8) => {
|
|
5420
|
+
const parsed = parseJsonObject(text8);
|
|
5357
5421
|
if (!parsed.ok) return void 0;
|
|
5358
5422
|
const record3 = parsed.value;
|
|
5359
5423
|
const number2 = record3["number"];
|
|
@@ -5534,8 +5598,8 @@ var applySize = (contract, verdict) => {
|
|
|
5534
5598
|
// src/gate/prepare.ts
|
|
5535
5599
|
var diskFiles = (runtime, root) => ({
|
|
5536
5600
|
read: (relative11) => {
|
|
5537
|
-
const
|
|
5538
|
-
return
|
|
5601
|
+
const text8 = runtime.readText(toAbsolutePath(join15(root, relative11)));
|
|
5602
|
+
return text8.ok ? text8.value : void 0;
|
|
5539
5603
|
},
|
|
5540
5604
|
exists: (relative11) => runtime.stat(toAbsolutePath(join15(root, relative11))) !== void 0
|
|
5541
5605
|
});
|
|
@@ -5656,11 +5720,11 @@ var trust = (input, paths, task) => {
|
|
|
5656
5720
|
});
|
|
5657
5721
|
if (ready.kind !== "ready" || ready.prepared.ctx.contract.id !== task) return false;
|
|
5658
5722
|
const lock = readTrust(runtime, paths);
|
|
5659
|
-
const
|
|
5723
|
+
const text8 = runtime.readText(paths.task(task));
|
|
5660
5724
|
return writeTrust(runtime, paths, {
|
|
5661
5725
|
...lock,
|
|
5662
5726
|
contracts: { ...lock.contracts, [task]: ready.prepared.loaded.hash },
|
|
5663
|
-
approved: task === IMPLICIT_ID || !
|
|
5727
|
+
approved: task === IMPLICIT_ID || !text8.ok ? lock.approved : { ...lock.approved, [task]: text8.value }
|
|
5664
5728
|
}).ok;
|
|
5665
5729
|
};
|
|
5666
5730
|
var apply = (input, paths, markers) => {
|
|
@@ -5712,8 +5776,8 @@ var promptRelease = (input) => {
|
|
|
5712
5776
|
var PRS_CACHE_MS = 10 * 6e4;
|
|
5713
5777
|
var PRS_TIMEOUT_MS = 3e3;
|
|
5714
5778
|
var PRS_AUTH_TIMEOUT_MS = 2e3;
|
|
5715
|
-
var parsePrList = (
|
|
5716
|
-
const parsed = parseJson(
|
|
5779
|
+
var parsePrList = (text8) => {
|
|
5780
|
+
const parsed = parseJson(text8);
|
|
5717
5781
|
if (!parsed.ok || !Array.isArray(parsed.value)) return void 0;
|
|
5718
5782
|
return parsed.value.flatMap((item2) => {
|
|
5719
5783
|
if (!isRecord(item2) || typeof item2["number"] !== "number") return [];
|
|
@@ -6513,7 +6577,25 @@ var en_default = {
|
|
|
6513
6577
|
"reason.human.generic": "Blocked a tool call; the reason went to Claude.",
|
|
6514
6578
|
"review.command.packet": "Print the review packet for the reviewer and verifier agents: diff, task contract, evidence ledger and REVIEW.md, never the conversation",
|
|
6515
6579
|
"review.guard.read_only": "blocked: the {agent} agent is read-only and cannot use {tool}. Report the finding; the main session makes the change.",
|
|
6516
|
-
"review.error.no_base": "the base {detail} is not a commit here"
|
|
6580
|
+
"review.error.no_base": "the base {detail} is not a commit here",
|
|
6581
|
+
"command.statusline.summary": "Print the status line: active task, gate state and context use",
|
|
6582
|
+
"statusline.gate.green": "gate green",
|
|
6583
|
+
"statusline.gate.red": "gate red",
|
|
6584
|
+
"statusline.gate.incomplete": "gate incomplete",
|
|
6585
|
+
"statusline.gate.did_not_run": "gate did not run",
|
|
6586
|
+
"statusline.context": "ctx {percent}%",
|
|
6587
|
+
"command.gain.summary": "What the guards did: saves next to false alarms and gate failures",
|
|
6588
|
+
"gain.title": "Finite Monkey, last {days, plural, one {# day} other {# days}} (from .finitemonkey/log.jsonl and the evidence ledgers):",
|
|
6589
|
+
"gain.row.blocks": "{count, plural, one {# block} other {# blocks}} by the guards",
|
|
6590
|
+
"gain.row.saves": "{count, plural, one {# block} other {# blocks}} followed by a green gate within a day",
|
|
6591
|
+
"gain.row.false_positives": "{count, plural, one {# false alarm} other {# false alarms}} (releases typed by the person, and false positives recorded)",
|
|
6592
|
+
"gain.row.gate_runs": "{count, plural, one {# gate run} other {# gate runs}}",
|
|
6593
|
+
"gain.row.gate_failed": "{count, plural, one {# gate run} other {# gate runs}} failed",
|
|
6594
|
+
"gain.row.gate_unverified": "{count, plural, one {# gate run} other {# gate runs}} ended not verified",
|
|
6595
|
+
"gain.row.gate_time": "{minutes, plural, one {# minute} other {# minutes}} spent in the gate",
|
|
6596
|
+
"gain.up": "up {delta} on the window before",
|
|
6597
|
+
"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."
|
|
6517
6599
|
};
|
|
6518
6600
|
|
|
6519
6601
|
// src/i18n/es.json
|
|
@@ -7165,7 +7247,25 @@ var es_default = {
|
|
|
7165
7247
|
"reason.human.generic": "Bloqueé una llamada de herramienta; el motivo fue a Claude.",
|
|
7166
7248
|
"review.command.packet": "Muestra el paquete de revisión para los agentes revisor y verificador: diff, contrato de la tarea, registro de evidencias y REVIEW.md, nunca la conversación",
|
|
7167
7249
|
"review.guard.read_only": "bloqueado: el agente {agent} es de solo lectura y no puede usar {tool}. Informa el hallazgo; la sesión principal hace el cambio.",
|
|
7168
|
-
"review.error.no_base": "la base {detail} no es un commit aquí"
|
|
7250
|
+
"review.error.no_base": "la base {detail} no es un commit aquí",
|
|
7251
|
+
"command.statusline.summary": "Imprime la línea de estado: tarea activa, estado de la compuerta y uso del contexto",
|
|
7252
|
+
"statusline.gate.green": "compuerta verde",
|
|
7253
|
+
"statusline.gate.red": "compuerta roja",
|
|
7254
|
+
"statusline.gate.incomplete": "compuerta incompleta",
|
|
7255
|
+
"statusline.gate.did_not_run": "la compuerta no se ejecutó",
|
|
7256
|
+
"statusline.context": "ctx {percent}%",
|
|
7257
|
+
"command.gain.summary": "Lo que hicieron los bloqueos: aciertos junto a falsas alarmas y fallos de la compuerta",
|
|
7258
|
+
"gain.title": "Finite Monkey, {days, plural, one {último # día} other {últimos # días}} (de .finitemonkey/log.jsonl y los ledgers de evidencia):",
|
|
7259
|
+
"gain.row.blocks": "{count, plural, one {# bloqueo} other {# bloqueos}} de los guards",
|
|
7260
|
+
"gain.row.saves": "{count, plural, one {# bloqueo seguido} other {# bloqueos seguidos}} de una compuerta en verde en menos de un día",
|
|
7261
|
+
"gain.row.false_positives": "{count, plural, one {# falsa alarma} other {# falsas alarmas}} (liberaciones escritas por la persona y falsos positivos registrados)",
|
|
7262
|
+
"gain.row.gate_runs": "{count, plural, one {# ejecución de la compuerta} other {# ejecuciones de la compuerta}}",
|
|
7263
|
+
"gain.row.gate_failed": "{count, plural, one {# ejecución de la compuerta falló} other {# ejecuciones de la compuerta fallaron}}",
|
|
7264
|
+
"gain.row.gate_unverified": "{count, plural, one {# ejecución de la compuerta terminó} other {# ejecuciones de la compuerta terminaron}} sin verificar",
|
|
7265
|
+
"gain.row.gate_time": "{minutes, plural, one {# minuto} other {# minutos}} en la compuerta",
|
|
7266
|
+
"gain.up": "+{delta} respecto a la ventana anterior",
|
|
7267
|
+
"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."
|
|
7169
7269
|
};
|
|
7170
7270
|
|
|
7171
7271
|
// src/i18n/pt-BR.json
|
|
@@ -7817,7 +7917,25 @@ var pt_BR_default = {
|
|
|
7817
7917
|
"reason.human.generic": "Bloqueei uma chamada de ferramenta; o motivo foi para o Claude.",
|
|
7818
7918
|
"review.command.packet": "Mostra o pacote de revisão para os agentes revisor e verificador: diff, contrato da tarefa, registro de evidências e REVIEW.md, nunca a conversa",
|
|
7819
7919
|
"review.guard.read_only": "bloqueado: o agente {agent} é só leitura e não pode usar {tool}. Relate o achado; a sessão principal faz a mudança.",
|
|
7820
|
-
"review.error.no_base": "a base {detail} não é um commit aqui"
|
|
7920
|
+
"review.error.no_base": "a base {detail} não é um commit aqui",
|
|
7921
|
+
"command.statusline.summary": "Imprime a linha de status: tarefa ativa, estado do portão e uso do contexto",
|
|
7922
|
+
"statusline.gate.green": "portão verde",
|
|
7923
|
+
"statusline.gate.red": "portão vermelho",
|
|
7924
|
+
"statusline.gate.incomplete": "portão incompleto",
|
|
7925
|
+
"statusline.gate.did_not_run": "portão não rodou",
|
|
7926
|
+
"statusline.context": "ctx {percent}%",
|
|
7927
|
+
"command.gain.summary": "O que as travas fizeram: acertos ao lado dos alarmes falsos e das falhas do portão",
|
|
7928
|
+
"gain.title": "Finite Monkey, últimos {days, plural, one {# dia} other {# dias}} (de .finitemonkey/log.jsonl e dos ledgers de evidência):",
|
|
7929
|
+
"gain.row.blocks": "{count, plural, one {# bloqueio} other {# bloqueios}} das travas",
|
|
7930
|
+
"gain.row.saves": "{count, plural, one {# bloqueio seguido} other {# bloqueios seguidos}} de portão verde em até um dia",
|
|
7931
|
+
"gain.row.false_positives": "{count, plural, one {# alarme falso} other {# alarmes falsos}} (liberações digitadas pela pessoa e falsos positivos registrados)",
|
|
7932
|
+
"gain.row.gate_runs": "{count, plural, one {# execução do portão} other {# execuções do portão}}",
|
|
7933
|
+
"gain.row.gate_failed": "{count, plural, one {# execução do portão reprovou} other {# execuções do portão reprovaram}}",
|
|
7934
|
+
"gain.row.gate_unverified": "{count, plural, one {# execução do portão terminou} other {# execuções do portão terminaram}} sem verificar",
|
|
7935
|
+
"gain.row.gate_time": "{minutes, plural, one {# minuto} other {# minutos}} no portão",
|
|
7936
|
+
"gain.up": "+{delta} em relação à janela anterior",
|
|
7937
|
+
"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."
|
|
7821
7939
|
};
|
|
7822
7940
|
|
|
7823
7941
|
// src/i18n/review.json
|
|
@@ -8470,7 +8588,25 @@ var review_default = {
|
|
|
8470
8588
|
"reason.human.generic": "machine",
|
|
8471
8589
|
"review.command.packet": "machine",
|
|
8472
8590
|
"review.guard.read_only": "machine",
|
|
8473
|
-
"review.error.no_base": "machine"
|
|
8591
|
+
"review.error.no_base": "machine",
|
|
8592
|
+
"command.statusline.summary": "machine",
|
|
8593
|
+
"statusline.gate.green": "machine",
|
|
8594
|
+
"statusline.gate.red": "machine",
|
|
8595
|
+
"statusline.gate.incomplete": "machine",
|
|
8596
|
+
"statusline.gate.did_not_run": "machine",
|
|
8597
|
+
"statusline.context": "machine",
|
|
8598
|
+
"command.gain.summary": "machine",
|
|
8599
|
+
"gain.title": "machine",
|
|
8600
|
+
"gain.row.blocks": "machine",
|
|
8601
|
+
"gain.row.saves": "machine",
|
|
8602
|
+
"gain.row.false_positives": "machine",
|
|
8603
|
+
"gain.row.gate_runs": "machine",
|
|
8604
|
+
"gain.row.gate_failed": "machine",
|
|
8605
|
+
"gain.row.gate_unverified": "machine",
|
|
8606
|
+
"gain.row.gate_time": "machine",
|
|
8607
|
+
"gain.up": "machine",
|
|
8608
|
+
"gain.down": "machine",
|
|
8609
|
+
"gain.footer": "machine"
|
|
8474
8610
|
},
|
|
8475
8611
|
es: {
|
|
8476
8612
|
"command.init.summary": "machine",
|
|
@@ -9120,7 +9256,25 @@ var review_default = {
|
|
|
9120
9256
|
"reason.human.generic": "machine",
|
|
9121
9257
|
"review.command.packet": "machine",
|
|
9122
9258
|
"review.guard.read_only": "machine",
|
|
9123
|
-
"review.error.no_base": "machine"
|
|
9259
|
+
"review.error.no_base": "machine",
|
|
9260
|
+
"command.statusline.summary": "machine",
|
|
9261
|
+
"statusline.gate.green": "machine",
|
|
9262
|
+
"statusline.gate.red": "machine",
|
|
9263
|
+
"statusline.gate.incomplete": "machine",
|
|
9264
|
+
"statusline.gate.did_not_run": "machine",
|
|
9265
|
+
"statusline.context": "machine",
|
|
9266
|
+
"command.gain.summary": "machine",
|
|
9267
|
+
"gain.title": "machine",
|
|
9268
|
+
"gain.row.blocks": "machine",
|
|
9269
|
+
"gain.row.saves": "machine",
|
|
9270
|
+
"gain.row.false_positives": "machine",
|
|
9271
|
+
"gain.row.gate_runs": "machine",
|
|
9272
|
+
"gain.row.gate_failed": "machine",
|
|
9273
|
+
"gain.row.gate_unverified": "machine",
|
|
9274
|
+
"gain.row.gate_time": "machine",
|
|
9275
|
+
"gain.up": "machine",
|
|
9276
|
+
"gain.down": "machine",
|
|
9277
|
+
"gain.footer": "machine"
|
|
9124
9278
|
}
|
|
9125
9279
|
};
|
|
9126
9280
|
|
|
@@ -9410,10 +9564,10 @@ var pairs = (record3) => {
|
|
|
9410
9564
|
return names.filter((name) => typeof name === "string").map((name) => `${task}\0${name}`);
|
|
9411
9565
|
};
|
|
9412
9566
|
var callersRate = (runtime, paths) => {
|
|
9413
|
-
const
|
|
9567
|
+
const text8 = runtime.readText(paths.log);
|
|
9414
9568
|
const flagged = /* @__PURE__ */ new Set();
|
|
9415
9569
|
const released2 = /* @__PURE__ */ new Set();
|
|
9416
|
-
for (const line2 of
|
|
9570
|
+
for (const line2 of text8.ok ? text8.value.split("\n") : []) {
|
|
9417
9571
|
const parsed = line2.trim() === "" ? void 0 : parseJsonObject(line2);
|
|
9418
9572
|
if (parsed === void 0 || !parsed.ok || !isRecord(parsed.value)) continue;
|
|
9419
9573
|
const event = parsed.value["event"];
|
|
@@ -9438,7 +9592,7 @@ import { posix as posix2 } from "node:path";
|
|
|
9438
9592
|
var JS_SPECIFIER = /(?:\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)["']([^"']+)["']/g;
|
|
9439
9593
|
var JS_EXTENSION = /\.(?:[cm]?[jt]sx?)$/;
|
|
9440
9594
|
var stripModule = (path) => path.replace(JS_EXTENSION, "").replace(/\/index$/, "");
|
|
9441
|
-
var jsSpecifiers = (
|
|
9595
|
+
var jsSpecifiers = (text8) => [...text8.matchAll(JS_SPECIFIER)].map((match) => match[1] ?? "");
|
|
9442
9596
|
var jsImports = (callerPath, callerText, origin) => {
|
|
9443
9597
|
const target2 = stripModule(origin);
|
|
9444
9598
|
return jsSpecifiers(callerText).some((specifier) => {
|
|
@@ -9540,9 +9694,9 @@ var languageOf = (path) => {
|
|
|
9540
9694
|
const extension = extname(path).toLowerCase();
|
|
9541
9695
|
return LANGUAGES2.find((language) => language.extensions.includes(extension));
|
|
9542
9696
|
};
|
|
9543
|
-
var definitionsIn = (language,
|
|
9697
|
+
var definitionsIn = (language, text8) => {
|
|
9544
9698
|
const names = /* @__PURE__ */ new Set();
|
|
9545
|
-
for (const line2 of
|
|
9699
|
+
for (const line2 of text8.split(/\r?\n/)) {
|
|
9546
9700
|
for (const pattern of language.definitions) {
|
|
9547
9701
|
const name = pattern.exec(line2)?.[1];
|
|
9548
9702
|
if (name !== void 0 && !KEYWORDS.has(name)) names.add(name);
|
|
@@ -9555,8 +9709,8 @@ var definesName = (language, line2, name) => language.definitions.some((pattern)
|
|
|
9555
9709
|
// src/gate/callers/scan.ts
|
|
9556
9710
|
var MAX_SYMBOLS = 200;
|
|
9557
9711
|
var current = (runtime, root, path) => {
|
|
9558
|
-
const
|
|
9559
|
-
return
|
|
9712
|
+
const text8 = runtime.readText(toAbsolutePath(join16(root, path)));
|
|
9713
|
+
return text8.ok ? text8.value : "";
|
|
9560
9714
|
};
|
|
9561
9715
|
var keyOf = (language, name) => `${language.id}\0${name}`;
|
|
9562
9716
|
var redefinedNames = (runtime, git2, changes) => {
|
|
@@ -9597,14 +9751,14 @@ var grep = (git2, names) => {
|
|
|
9597
9751
|
git2.out(["grep", "-n", "-z", "-w", "-I", "--untracked", "--no-color", ...patterns, "--", ".", EXCLUDE_FM]) ?? ""
|
|
9598
9752
|
);
|
|
9599
9753
|
};
|
|
9600
|
-
var mentions = (
|
|
9754
|
+
var mentions = (text8, name) => new RegExp(`(?<![\\w$])${name.replace(/[$?!]/g, "\\$&")}(?![\\w$])`).test(text8);
|
|
9601
9755
|
var callSites = (runtime, git2, symbols) => {
|
|
9602
9756
|
const hits = grep(git2, [...new Set(symbols.map((symbol) => symbol.name))]);
|
|
9603
9757
|
const texts2 = /* @__PURE__ */ new Map();
|
|
9604
9758
|
const textOf4 = (file) => {
|
|
9605
|
-
const
|
|
9606
|
-
texts2.set(file,
|
|
9607
|
-
return
|
|
9759
|
+
const cached3 = texts2.get(file) ?? current(runtime, git2.cwd, file);
|
|
9760
|
+
texts2.set(file, cached3);
|
|
9761
|
+
return cached3;
|
|
9608
9762
|
};
|
|
9609
9763
|
return symbols.flatMap(
|
|
9610
9764
|
(symbol) => hits.filter((hit3) => languageOf(hit3.file)?.id === symbol.language.id && mentions(hit3.text, symbol.name)).filter((hit3) => !symbol.language.comment.test(hit3.text) && !definesName(symbol.language, hit3.text, symbol.name)).filter((hit3) => importsOrigin(symbol.language, hit3.file, textOf4(hit3.file), symbol.origins)).map((hit3) => ({ name: symbol.name, file: hit3.file, line: hit3.line }))
|
|
@@ -9672,31 +9826,31 @@ var ZERO = [
|
|
|
9672
9826
|
/^ℹ tests 0$/m,
|
|
9673
9827
|
/No tests found/
|
|
9674
9828
|
];
|
|
9675
|
-
var pyTotal = (
|
|
9676
|
-
const line2 = /^=+ (.*) in [\d.]+s.*=+$/m.exec(
|
|
9829
|
+
var pyTotal = (text8) => {
|
|
9830
|
+
const line2 = /^=+ (.*) in [\d.]+s.*=+$/m.exec(text8)?.[1];
|
|
9677
9831
|
if (line2 === void 0) return void 0;
|
|
9678
9832
|
return [...line2.matchAll(/(\d+) (passed|failed|error|errors|skipped|xfailed|xpassed)/g)].reduce(
|
|
9679
9833
|
(sum, match) => sum + Number(match[1] ?? "0"),
|
|
9680
9834
|
0
|
|
9681
9835
|
);
|
|
9682
9836
|
};
|
|
9683
|
-
var counted = (
|
|
9837
|
+
var counted = (text8) => {
|
|
9684
9838
|
for (const counter of COUNTERS) {
|
|
9685
|
-
const total = counter.total.exec(
|
|
9839
|
+
const total = counter.total.exec(text8)?.[1];
|
|
9686
9840
|
if (total === void 0) continue;
|
|
9687
|
-
const failed4 = counter.failed?.exec(
|
|
9841
|
+
const failed4 = counter.failed?.exec(text8)?.[1];
|
|
9688
9842
|
return { total: Number(total), ...failed4 === void 0 ? {} : { failed: Number(failed4) } };
|
|
9689
9843
|
}
|
|
9690
|
-
const py = pyTotal(
|
|
9844
|
+
const py = pyTotal(text8);
|
|
9691
9845
|
return py === void 0 ? {} : { total: py };
|
|
9692
9846
|
};
|
|
9693
9847
|
var parseTestSummary = (output) => {
|
|
9694
|
-
const
|
|
9848
|
+
const text8 = output.replace(ANSI2, "");
|
|
9695
9849
|
const failures = [
|
|
9696
|
-
...new Set(FAILURE_LINES.flatMap((pattern) => [...
|
|
9850
|
+
...new Set(FAILURE_LINES.flatMap((pattern) => [...text8.matchAll(pattern)].map((match) => (match[1] ?? "").trim())))
|
|
9697
9851
|
].filter((name) => name !== "" && !/^failing tests:?$/i.test(name));
|
|
9698
|
-
if (ZERO.some((pattern) => pattern.test(
|
|
9699
|
-
return { ...counted(
|
|
9852
|
+
if (ZERO.some((pattern) => pattern.test(text8))) return { total: 0, failures };
|
|
9853
|
+
return { ...counted(text8), failures };
|
|
9700
9854
|
};
|
|
9701
9855
|
|
|
9702
9856
|
// src/gate/checks/command.ts
|
|
@@ -9707,8 +9861,8 @@ var REWRITERS = /^\s*(rtk)\b/;
|
|
|
9707
9861
|
var TRIVIAL = /* @__PURE__ */ new Set(["echo", "true", ":", "printf", "exit", "sleep", "cat", "yes", "pwd", "ls", "date", "env"]);
|
|
9708
9862
|
var TAIL = 160;
|
|
9709
9863
|
var MAX_FAILURES2 = 5;
|
|
9710
|
-
var tail = (
|
|
9711
|
-
const lines3 =
|
|
9864
|
+
var tail = (text8) => {
|
|
9865
|
+
const lines3 = text8.split(/\r?\n/).filter((line2) => line2.trim() !== "");
|
|
9712
9866
|
const last = lines3[lines3.length - 1]?.trim() ?? "";
|
|
9713
9867
|
return last.length > TAIL ? `${last.slice(0, TAIL)}…` : last;
|
|
9714
9868
|
};
|
|
@@ -9922,8 +10076,8 @@ var findingNote = (value) => {
|
|
|
9922
10076
|
];
|
|
9923
10077
|
};
|
|
9924
10078
|
var readSummary = (ctx) => {
|
|
9925
|
-
const
|
|
9926
|
-
const parsed =
|
|
10079
|
+
const text8 = ctx.runtime.readText(summaryPath(ctx.paths, ctx.treeHash));
|
|
10080
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
9927
10081
|
if (!parsed?.ok) return void 0;
|
|
9928
10082
|
const { at, captures, findings, ignoredAllows } = parsed.value;
|
|
9929
10083
|
if (typeof at !== "string" || !Array.isArray(captures) || !Array.isArray(findings)) return void 0;
|
|
@@ -9992,8 +10146,8 @@ var SKIP_FILES = /\.(md|mdx|txt|rst|lock|snap|svg|png|jpg|gif|json)$|(^|\/)(CHAN
|
|
|
9992
10146
|
var PLACEHOLDER = /\b(TODO|FIXME|XXX)\b|not implemented|NotImplementedError|unimplemented!|todo!\(|lorem ipsum/i;
|
|
9993
10147
|
var findPlaceholders = (lines3) => lines3.filter((line2) => !isTestFile(line2.path) && !SKIP_FILES.test(line2.path) && PLACEHOLDER.test(line2.text));
|
|
9994
10148
|
var untrackedAdded = (ctx) => ctx.changes.filter((change) => change.status === "A" && !SKIP_FILES.test(change.path)).flatMap((change) => {
|
|
9995
|
-
const
|
|
9996
|
-
return
|
|
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 })) : [];
|
|
9997
10151
|
});
|
|
9998
10152
|
var placeholdersCheck = (_check, ctx) => {
|
|
9999
10153
|
const tracked = addedLines(ctx.git, ctx.base);
|
|
@@ -10052,7 +10206,7 @@ var prOutcome = (check2, ctx) => {
|
|
|
10052
10206
|
|
|
10053
10207
|
// src/prose/preset.ts
|
|
10054
10208
|
var PRESET_LINE = /^preset:\s*([A-Za-z0-9_-]+)\s*$/m;
|
|
10055
|
-
var presetFromConfig = (
|
|
10209
|
+
var presetFromConfig = (text8) => parsePreset(PRESET_LINE.exec(text8)?.[1]) ?? "neutral";
|
|
10056
10210
|
var projectPreset = (runtime, root) => {
|
|
10057
10211
|
const config = runtime.readText(toAbsolutePath(CONFIG_FILE, root));
|
|
10058
10212
|
return config.ok ? presetFromConfig(config.value) : "neutral";
|
|
@@ -10229,16 +10383,16 @@ var ANY_MARKER = /<!--\s*finitemonkey:prose-allow(?:-file)?\s*-->/g;
|
|
|
10229
10383
|
var INLINE_CODE = /`[^`]*`/g;
|
|
10230
10384
|
var FENCE2 = /^\s*(```|~~~)/;
|
|
10231
10385
|
var outsideCode = (line2) => line2.replace(INLINE_CODE, "");
|
|
10232
|
-
var unfencedLines = (
|
|
10386
|
+
var unfencedLines = (text8) => {
|
|
10233
10387
|
let fenced = false;
|
|
10234
|
-
return
|
|
10388
|
+
return text8.split("\n").filter((line2) => {
|
|
10235
10389
|
if (FENCE2.test(line2)) fenced = !fenced;
|
|
10236
10390
|
return !fenced && !FENCE2.test(line2);
|
|
10237
10391
|
});
|
|
10238
10392
|
};
|
|
10239
|
-
var allowsWholeFile = (
|
|
10393
|
+
var allowsWholeFile = (text8) => unfencedLines(text8).some((line2) => FILE_MARKER.test(outsideCode(line2)));
|
|
10240
10394
|
var allowsLine = (line2) => LINE_MARKER.test(outsideCode(line2));
|
|
10241
|
-
var markerCount = (
|
|
10395
|
+
var markerCount = (text8) => text8.match(ANY_MARKER)?.length ?? 0;
|
|
10242
10396
|
var isMarkerOnlyLine = (line2) => allowsLine(line2) && outsideCode(line2).replace(LINE_MARKER, "").trim() === "";
|
|
10243
10397
|
|
|
10244
10398
|
// src/prose/extract.ts
|
|
@@ -10258,7 +10412,7 @@ var MASKS = [
|
|
|
10258
10412
|
[/<[^>]+>/g, ""],
|
|
10259
10413
|
[/\*\*|__/g, ""]
|
|
10260
10414
|
];
|
|
10261
|
-
var maskInline = (line2) => MASKS.reduce((
|
|
10415
|
+
var maskInline = (line2) => MASKS.reduce((text8, [pattern, replacement]) => text8.replace(pattern, replacement), line2);
|
|
10262
10416
|
var BlockReader = class {
|
|
10263
10417
|
blocks = [];
|
|
10264
10418
|
current;
|
|
@@ -10349,8 +10503,8 @@ var splitBlock = (block4) => {
|
|
|
10349
10503
|
return joined.split(SENTENCE_END).flatMap((sentence) => {
|
|
10350
10504
|
const at = joined.indexOf(sentence, offset);
|
|
10351
10505
|
offset = at + sentence.length;
|
|
10352
|
-
const
|
|
10353
|
-
return
|
|
10506
|
+
const text8 = sentence.trim();
|
|
10507
|
+
return text8 === "" ? [] : [{ text: text8, line: lineAt(at) }];
|
|
10354
10508
|
});
|
|
10355
10509
|
};
|
|
10356
10510
|
var unitsOf = (blocks) => {
|
|
@@ -10371,9 +10525,9 @@ var unitsOf = (blocks) => {
|
|
|
10371
10525
|
}));
|
|
10372
10526
|
});
|
|
10373
10527
|
};
|
|
10374
|
-
var extractProse = (
|
|
10375
|
-
if (allowsWholeFile(
|
|
10376
|
-
const lines3 =
|
|
10528
|
+
var extractProse = (text8) => {
|
|
10529
|
+
if (allowsWholeFile(text8)) return { units: [], allowed: true };
|
|
10530
|
+
const lines3 = text8.split("\n").map(withoutCarriageReturn);
|
|
10377
10531
|
return { units: unitsOf(readBlocks(lines3)), allowed: false };
|
|
10378
10532
|
};
|
|
10379
10533
|
|
|
@@ -10498,7 +10652,7 @@ var CONNECTIVES = new Set(
|
|
|
10498
10652
|
" "
|
|
10499
10653
|
)
|
|
10500
10654
|
);
|
|
10501
|
-
var wordsOf = (
|
|
10655
|
+
var wordsOf = (text8) => text8.split(/\s+/).filter((word2) => WORD.test(word2));
|
|
10502
10656
|
var hit = (rule, units, index, weight) => {
|
|
10503
10657
|
const unit = units[index];
|
|
10504
10658
|
return { rule, unit: index, line: unit?.line ?? 0, weight, excerpt: unit?.text ?? "" };
|
|
@@ -10626,8 +10780,8 @@ var mergeSpans = (hits, windows) => {
|
|
|
10626
10780
|
return ranges.map(([from, to]) => spanOf2(hits, from, to)).filter((span) => span.hits.length > 0);
|
|
10627
10781
|
};
|
|
10628
10782
|
var byPosition = (left, right) => left.unit - right.unit || left.line - right.line;
|
|
10629
|
-
var scoreProse = (
|
|
10630
|
-
const { units, allowed: allowed2 } = extractProse(
|
|
10783
|
+
var scoreProse = (text8, options = {}) => {
|
|
10784
|
+
const { units, allowed: allowed2 } = extractProse(text8);
|
|
10631
10785
|
const size = options.window ?? DENSITY_WINDOW;
|
|
10632
10786
|
const threshold2 = options.threshold ?? DENSITY_THRESHOLD;
|
|
10633
10787
|
const catalog = options.context === "record" ? [...phraseRules(), ...recordRules()] : phraseRules();
|
|
@@ -10659,9 +10813,9 @@ var changedMarkdown = (runtime, root) => {
|
|
|
10659
10813
|
var scoreFiles = (runtime, root, files, forced) => {
|
|
10660
10814
|
const reports = [];
|
|
10661
10815
|
for (const file of files) {
|
|
10662
|
-
const
|
|
10663
|
-
if (!
|
|
10664
|
-
reports.push({ file: toPosixPath(file), report: scoreProse(
|
|
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) }) });
|
|
10665
10819
|
}
|
|
10666
10820
|
return accept(reports);
|
|
10667
10821
|
};
|
|
@@ -10760,8 +10914,8 @@ var parseAppCheck = (stdout) => {
|
|
|
10760
10914
|
var PROBE_MARGIN_MS = 15e3;
|
|
10761
10915
|
var MIN_REMAINING_MS2 = 5e3;
|
|
10762
10916
|
var TAIL2 = 160;
|
|
10763
|
-
var lastLine = (
|
|
10764
|
-
const rows =
|
|
10917
|
+
var lastLine = (text8) => {
|
|
10918
|
+
const rows = text8.split(/\r?\n/).filter((row) => row.trim() !== "" && !/^\s*at\s|^Node\.js v\d/.test(row));
|
|
10765
10919
|
const line2 = (rows.findLast((row) => /error/i.test(row)) ?? rows.at(-1))?.trim() ?? "";
|
|
10766
10920
|
return line2.length > TAIL2 ? `${line2.slice(0, TAIL2)}…` : line2;
|
|
10767
10921
|
};
|
|
@@ -11104,7 +11258,7 @@ var diffScanner = () => {
|
|
|
11104
11258
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
11105
11259
|
var SKIPPED = /(?:^|\/)(?:\.git|node_modules|\.finitemonkey)\//;
|
|
11106
11260
|
var DIFF_FLAGS = ["-U0", "--no-color", "--no-ext-diff", "--no-renames"];
|
|
11107
|
-
var binary = (
|
|
11261
|
+
var binary = (text8) => text8.slice(0, 8e3).includes("\0");
|
|
11108
11262
|
var listed2 = (runtime, root, target2) => {
|
|
11109
11263
|
const git2 = runtime.git(["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", target2], root);
|
|
11110
11264
|
if (git2.ok)
|
|
@@ -11117,9 +11271,9 @@ var listed2 = (runtime, root, target2) => {
|
|
|
11117
11271
|
return accept(walked.value.map((name) => toPosixPath(relative6(root, join23(base, toPosixPath(name))))));
|
|
11118
11272
|
};
|
|
11119
11273
|
var scanFile = (runtime, root, file) => {
|
|
11120
|
-
const
|
|
11121
|
-
if (!
|
|
11122
|
-
return scanText(
|
|
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));
|
|
11123
11277
|
};
|
|
11124
11278
|
var scanPath = (runtime, root, target2) => {
|
|
11125
11279
|
const files = listed2(runtime, root, target2);
|
|
@@ -11216,8 +11370,8 @@ var treeViolations = (ctx) => {
|
|
|
11216
11370
|
changes: relevant,
|
|
11217
11371
|
before: (path) => fileAt(ctx.git, ctx.base, path),
|
|
11218
11372
|
after: (path) => {
|
|
11219
|
-
const
|
|
11220
|
-
return
|
|
11373
|
+
const text8 = ctx.runtime.readText(toAbsolutePath(join24(ctx.paths.root, path)));
|
|
11374
|
+
return text8.ok ? text8.value : void 0;
|
|
11221
11375
|
},
|
|
11222
11376
|
acceptedConfigs: ctx.trust.configs,
|
|
11223
11377
|
scopePaths: ctx.contract.scope.paths,
|
|
@@ -11381,10 +11535,10 @@ var bodyLine = (draft, line2) => {
|
|
|
11381
11535
|
else if (line2.startsWith("+")) draft.current.added.push(line2.slice(1));
|
|
11382
11536
|
};
|
|
11383
11537
|
var DIFF_GIT = /^diff --git a\/(.+) b\/(.+)$/;
|
|
11384
|
-
var parseUnifiedDiff = (
|
|
11538
|
+
var parseUnifiedDiff = (text8) => {
|
|
11385
11539
|
const out = [];
|
|
11386
11540
|
let draft;
|
|
11387
|
-
for (const raw of
|
|
11541
|
+
for (const raw of text8.split("\n")) {
|
|
11388
11542
|
const line2 = withoutCarriageReturn(raw);
|
|
11389
11543
|
const start = DIFF_GIT.exec(line2);
|
|
11390
11544
|
if (start !== null) {
|
|
@@ -11485,10 +11639,10 @@ var mergeInProgress = (runtime, repo) => revParse(runtime, repo, "MERGE_HEAD") !
|
|
|
11485
11639
|
|
|
11486
11640
|
// src/git/facts.ts
|
|
11487
11641
|
var memo2 = (compute) => {
|
|
11488
|
-
let
|
|
11642
|
+
let cached3;
|
|
11489
11643
|
return () => {
|
|
11490
|
-
|
|
11491
|
-
return
|
|
11644
|
+
cached3 ??= { value: compute() };
|
|
11645
|
+
return cached3.value;
|
|
11492
11646
|
};
|
|
11493
11647
|
};
|
|
11494
11648
|
var memoBy = (compute) => {
|
|
@@ -11535,7 +11689,7 @@ var normalizeLine = (line2) => line2.replace(/\r$/, "").trim().replace(/\s*,$/,
|
|
|
11535
11689
|
var WEAK = /^[\p{P}\p{S}\s]+$/u;
|
|
11536
11690
|
var isWeak = (line2) => WEAK.test(line2);
|
|
11537
11691
|
var meaningful = (lines3) => lines3.map(normalizeLine).filter((line2) => line2 !== "");
|
|
11538
|
-
var contentLines = (
|
|
11692
|
+
var contentLines = (text8) => text8 === void 0 ? [] : meaningful(text8.split("\n"));
|
|
11539
11693
|
var countLines = (lines3) => {
|
|
11540
11694
|
const counts = /* @__PURE__ */ new Map();
|
|
11541
11695
|
for (const line2 of lines3) counts.set(line2, (counts.get(line2) ?? 0) + 1);
|
|
@@ -11584,16 +11738,16 @@ var itemOf = (value) => {
|
|
|
11584
11738
|
approvedBy: optionalString(value, "approved_by")
|
|
11585
11739
|
};
|
|
11586
11740
|
};
|
|
11587
|
-
var invalidFinding = (file,
|
|
11741
|
+
var invalidFinding = (file, text8) => ({
|
|
11588
11742
|
kind: "invalid-dropped",
|
|
11589
11743
|
file,
|
|
11590
11744
|
side: void 0,
|
|
11591
11745
|
line: void 0,
|
|
11592
|
-
text:
|
|
11746
|
+
text: text8,
|
|
11593
11747
|
weak: false
|
|
11594
11748
|
});
|
|
11595
|
-
var parseDropped = (
|
|
11596
|
-
const parsed = parseJson(
|
|
11749
|
+
var parseDropped = (text8) => {
|
|
11750
|
+
const parsed = parseJson(text8);
|
|
11597
11751
|
if (!parsed.ok) return parsed;
|
|
11598
11752
|
const list4 = isRecord(parsed.value) ? parsed.value["dropped"] : parsed.value;
|
|
11599
11753
|
if (!Array.isArray(list4)) return reject("expected a list of dropped items");
|
|
@@ -11609,9 +11763,9 @@ var parseDropped = (text7) => {
|
|
|
11609
11763
|
};
|
|
11610
11764
|
var loadDropped = (runtime, root, file) => {
|
|
11611
11765
|
const path = toAbsolutePath(file ?? join26(root, DROPPED_FILE), root);
|
|
11612
|
-
const
|
|
11613
|
-
if (!
|
|
11614
|
-
return parseDropped(
|
|
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);
|
|
11615
11769
|
};
|
|
11616
11770
|
var isDropped = (finding4, items) => items.some(
|
|
11617
11771
|
(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)
|
|
@@ -11734,13 +11888,13 @@ var removalOf = (ours, theirs, paired) => {
|
|
|
11734
11888
|
var missingAdditions = (hunks, file, elsewhere) => {
|
|
11735
11889
|
const present2 = new Set(file.result);
|
|
11736
11890
|
return hunks.flatMap(({ side, hunk, added }) => {
|
|
11737
|
-
const
|
|
11738
|
-
if (
|
|
11891
|
+
const text8 = added.find((line2) => !isWeak(line2) && !present2.has(line2));
|
|
11892
|
+
if (text8 === void 0 || containsSequence(file.result, added) || elsewhere(added)) return [];
|
|
11739
11893
|
const offset = Math.max(
|
|
11740
11894
|
0,
|
|
11741
|
-
hunk.added.findIndex((line2) => normalizeLine(line2) ===
|
|
11895
|
+
hunk.added.findIndex((line2) => normalizeLine(line2) === text8)
|
|
11742
11896
|
);
|
|
11743
|
-
return [{ kind: "missing-addition", file: file.path, side, line: hunk.newStart + offset, text:
|
|
11897
|
+
return [{ kind: "missing-addition", file: file.path, side, line: hunk.newStart + offset, text: text8, weak: false }];
|
|
11744
11898
|
});
|
|
11745
11899
|
};
|
|
11746
11900
|
var expectations = (file, hunks, removal) => {
|
|
@@ -11813,8 +11967,8 @@ var symbolsOf = (line2) => {
|
|
|
11813
11967
|
...title === void 0 ? [] : [{ name: title, test: true }]
|
|
11814
11968
|
];
|
|
11815
11969
|
};
|
|
11816
|
-
var escapeRegex = (
|
|
11817
|
-
var containsSymbol = (
|
|
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);
|
|
11818
11972
|
var missingSymbols = (file, side, hunks, present2) => {
|
|
11819
11973
|
const seen = /* @__PURE__ */ new Set();
|
|
11820
11974
|
return hunks.flatMap(
|
|
@@ -11833,12 +11987,12 @@ var created2 = (diff) => diff?.status === "added" || diff?.status === "copied";
|
|
|
11833
11987
|
var resultPathOf = (context, pair) => [pair.ours?.newPath, pair.theirs?.newPath, pair.key].find(
|
|
11834
11988
|
(path) => path !== void 0 && context.resultText(path) !== void 0
|
|
11835
11989
|
);
|
|
11836
|
-
var finding2 = (kind, file, side,
|
|
11990
|
+
var finding2 = (kind, file, side, text8) => ({
|
|
11837
11991
|
kind,
|
|
11838
11992
|
file,
|
|
11839
11993
|
side,
|
|
11840
11994
|
line: void 0,
|
|
11841
|
-
text:
|
|
11995
|
+
text: text8,
|
|
11842
11996
|
weak: false
|
|
11843
11997
|
});
|
|
11844
11998
|
var binaryFindings = (context, pair, path) => {
|
|
@@ -11857,8 +12011,8 @@ var binaryFindings = (context, pair, path) => {
|
|
|
11857
12011
|
return result === expected ? [] : [finding2("binary", file, side, file)];
|
|
11858
12012
|
};
|
|
11859
12013
|
var elsewhereOf = (context) => (lines3) => context.resultFiles().some((path) => containsSequence(contentLines(context.resultText(path)), lines3));
|
|
11860
|
-
var symbolFindings = (context, pair, path,
|
|
11861
|
-
const present2 = (symbol) =>
|
|
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));
|
|
11862
12016
|
return [
|
|
11863
12017
|
...missingSymbols(path, "ours", pair.ours?.hunks ?? [], present2),
|
|
11864
12018
|
...missingSymbols(path, "theirs", pair.theirs?.hunks ?? [], present2)
|
|
@@ -11874,19 +12028,19 @@ var verifyFile = (context, pair) => {
|
|
|
11874
12028
|
const copied = [pair.ours, pair.theirs].find((diff) => diff?.status === "copied");
|
|
11875
12029
|
const baseSource = copied?.oldPath ?? (created2(pair.ours) || created2(pair.theirs) ? void 0 : pair.key);
|
|
11876
12030
|
const baseText = baseSource === void 0 ? void 0 : blobText(context.runtime, context.repo, context.inputs.base, baseSource);
|
|
11877
|
-
const
|
|
12031
|
+
const text8 = path === void 0 ? void 0 : context.resultText(path);
|
|
11878
12032
|
const file = path ?? pair.key;
|
|
11879
12033
|
const lines3 = checkFile(
|
|
11880
12034
|
{
|
|
11881
12035
|
path: file,
|
|
11882
12036
|
base: contentLines(baseText),
|
|
11883
|
-
result: contentLines(
|
|
12037
|
+
result: contentLines(text8),
|
|
11884
12038
|
ours: pair.ours?.hunks ?? [],
|
|
11885
12039
|
theirs: pair.theirs?.hunks ?? []
|
|
11886
12040
|
},
|
|
11887
12041
|
elsewhereOf(context)
|
|
11888
12042
|
);
|
|
11889
|
-
return [...lines3, ...symbolFindings(context, pair, file,
|
|
12043
|
+
return [...lines3, ...symbolFindings(context, pair, file, text8)];
|
|
11890
12044
|
};
|
|
11891
12045
|
|
|
11892
12046
|
// src/git/merge/verify.ts
|
|
@@ -12006,8 +12160,8 @@ var hashesOf = (value) => isRecord(value) ? Object.fromEntries(
|
|
|
12006
12160
|
Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
|
|
12007
12161
|
) : {};
|
|
12008
12162
|
var readObject = (runtime, path) => {
|
|
12009
|
-
const
|
|
12010
|
-
return
|
|
12163
|
+
const text8 = runtime.readText(path);
|
|
12164
|
+
return text8.ok ? parseJsonObject(text8.value) : text8;
|
|
12011
12165
|
};
|
|
12012
12166
|
var readBaseline2 = (runtime, repo, owner) => {
|
|
12013
12167
|
const parsed = readObject(runtime, fileOf(repo, "baseline", owner));
|
|
@@ -12073,7 +12227,7 @@ var foreignFiles = (runtime, repo, owner, targets2) => {
|
|
|
12073
12227
|
};
|
|
12074
12228
|
|
|
12075
12229
|
// src/git/gate-status.ts
|
|
12076
|
-
var lines = (
|
|
12230
|
+
var lines = (text8) => text8.split("\n").filter((line2) => line2.trim() !== "");
|
|
12077
12231
|
var unpushedSince = (runtime, repo, since) => {
|
|
12078
12232
|
if (lines(runtime.git(["remote"], repo.root).stdout).length === 0) return [];
|
|
12079
12233
|
const result = runtime.git(["rev-list", `${since}..HEAD`, "--not", "--remotes"], repo.root);
|
|
@@ -12477,8 +12631,8 @@ var subagentStart = (input) => {
|
|
|
12477
12631
|
const active = readState(runtime, paths).active;
|
|
12478
12632
|
if (active === void 0) return { kind: "allow" };
|
|
12479
12633
|
const disk = (relative11) => {
|
|
12480
|
-
const
|
|
12481
|
-
return
|
|
12634
|
+
const text8 = runtime.readText(toAbsolutePath(join29(paths.root, relative11)));
|
|
12635
|
+
return text8.ok ? text8.value : void 0;
|
|
12482
12636
|
};
|
|
12483
12637
|
const loaded = loadTask(runtime, paths, { disk, base: () => void 0 }, active);
|
|
12484
12638
|
if (!loaded.ok) return { kind: "allow" };
|
|
@@ -12504,8 +12658,8 @@ import { join as join31 } from "node:path";
|
|
|
12504
12658
|
|
|
12505
12659
|
// src/git/command-text.ts
|
|
12506
12660
|
var MAX_SHOWN = 160;
|
|
12507
|
-
var shown = (
|
|
12508
|
-
const flat =
|
|
12661
|
+
var shown = (text8) => {
|
|
12662
|
+
const flat = text8.replace(/\s+/g, " ").trim();
|
|
12509
12663
|
return flat.length > MAX_SHOWN ? `${flat.slice(0, MAX_SHOWN - 3)}...` : flat;
|
|
12510
12664
|
};
|
|
12511
12665
|
var commandText = (command) => shown([command.program, ...command.args].join(" "));
|
|
@@ -12855,9 +13009,9 @@ var SSH_URL = /ssh:\/\/(?:[^@\s/]+@)?([A-Za-z0-9.-]+)/g;
|
|
|
12855
13009
|
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;
|
|
12856
13010
|
var hostKey = (raw) => raw.trim().toLowerCase().replace(/^[^@]*@/, "").replace(/^\[([^\]]+)\].*$/, "$1").replace(/:\d+$/, "").replace(/\.$/, "");
|
|
12857
13011
|
var shellLine = (line2) => line2.replace(/^\s*(?:-\s*)?(?:run:\s*\|?)?/, "");
|
|
12858
|
-
var hostsInText = (
|
|
12859
|
-
const urls = [...
|
|
12860
|
-
const lines3 =
|
|
13012
|
+
var hostsInText = (text8) => {
|
|
13013
|
+
const urls = [...text8.matchAll(SSH_URL)].map((match) => match[1] ?? "");
|
|
13014
|
+
const lines3 = text8.split(/\r?\n/);
|
|
12861
13015
|
const keyed = lines3.flatMap((line2) => {
|
|
12862
13016
|
const match = HOST_KEY.exec(line2);
|
|
12863
13017
|
return match?.[1] === void 0 ? [] : [match[1]];
|
|
@@ -12871,8 +13025,8 @@ var workflowFiles = (runtime, root) => {
|
|
|
12871
13025
|
return listed4.value.filter((name) => /deploy/i.test(name) && /\.ya?ml$/i.test(name)).map((name) => `.github/workflows/${name}`);
|
|
12872
13026
|
};
|
|
12873
13027
|
var detectedHosts = (runtime, root, files) => files.flatMap((file) => {
|
|
12874
|
-
const
|
|
12875
|
-
return
|
|
13028
|
+
const text8 = runtime.readText(toAbsolutePath(join30(root, file)));
|
|
13029
|
+
return text8.ok ? hostsInText(text8.value) : [];
|
|
12876
13030
|
});
|
|
12877
13031
|
var loadProdTargets = (runtime, root, config) => {
|
|
12878
13032
|
const workflows = workflowFiles(runtime, root);
|
|
@@ -13046,8 +13200,8 @@ var prodGuard = (runtime, root, t) => {
|
|
|
13046
13200
|
if (!PROD_PROGRAMS.has(command.program)) return void 0;
|
|
13047
13201
|
const hit3 = prodHit(command, targets2(), isProd);
|
|
13048
13202
|
if (hit3 === void 0) return void 0;
|
|
13049
|
-
const
|
|
13050
|
-
return hit3.effect === "read" ? { kind: "read", hit: hit3, command:
|
|
13203
|
+
const text8 = commandText(command);
|
|
13204
|
+
return hit3.effect === "read" ? { kind: "read", hit: hit3, command: text8 } : blockOf(t, hit3, text8, targets2().deployPath);
|
|
13051
13205
|
}
|
|
13052
13206
|
};
|
|
13053
13207
|
};
|
|
@@ -13067,8 +13221,8 @@ var recordEvidence = (runtime, root, owner, reads) => {
|
|
|
13067
13221
|
const path = toAbsolutePath(join31(root, EVIDENCE_FILE));
|
|
13068
13222
|
runtime.mkdir(toAbsolutePath(join31(root, ".finitemonkey")));
|
|
13069
13223
|
const before = runtime.readText(path);
|
|
13070
|
-
const
|
|
13071
|
-
runtime.writeAtomic(path, `${
|
|
13224
|
+
const text8 = before.ok ? before.value : "";
|
|
13225
|
+
runtime.writeAtomic(path, `${text8}${text8 === "" || text8.endsWith("\n") ? "" : "\n"}${lines3.join("\n")}
|
|
13072
13226
|
`);
|
|
13073
13227
|
};
|
|
13074
13228
|
|
|
@@ -13083,15 +13237,15 @@ var editTexts = (input, file) => {
|
|
|
13083
13237
|
if (!Array.isArray(edits)) return [];
|
|
13084
13238
|
return edits.flatMap((edit) => {
|
|
13085
13239
|
if (!isRecord(edit)) return [];
|
|
13086
|
-
const
|
|
13087
|
-
return
|
|
13240
|
+
const text8 = optionalString(edit, "new_string");
|
|
13241
|
+
return text8 === void 0 ? [] : [{ file, text: text8, whole: false }];
|
|
13088
13242
|
});
|
|
13089
13243
|
};
|
|
13090
13244
|
var writtenTexts = (tool, input) => {
|
|
13091
13245
|
const file = optionalString(input, "file_path") ?? optionalString(input, "notebook_path") ?? "";
|
|
13092
13246
|
const single2 = (key, whole) => {
|
|
13093
|
-
const
|
|
13094
|
-
return
|
|
13247
|
+
const text8 = optionalString(input, key);
|
|
13248
|
+
return text8 === void 0 ? [] : [{ file, text: text8, whole }];
|
|
13095
13249
|
};
|
|
13096
13250
|
switch (tool) {
|
|
13097
13251
|
case "Write":
|
|
@@ -13773,24 +13927,24 @@ var withChain = (facts, chain) => {
|
|
|
13773
13927
|
const branch = chain?.branch;
|
|
13774
13928
|
return branch === void 0 ? facts : { ...facts, currentBranch: () => branch };
|
|
13775
13929
|
};
|
|
13776
|
-
var decide = (context, repo, verdict, facts,
|
|
13930
|
+
var decide = (context, repo, verdict, facts, text8) => {
|
|
13777
13931
|
switch (verdict.kind) {
|
|
13778
13932
|
case "pass":
|
|
13779
13933
|
return { kind: "pass" };
|
|
13780
13934
|
case "block":
|
|
13781
|
-
return { kind: "block", rule: verdict.rule, subject: verdict.subject, command:
|
|
13935
|
+
return { kind: "block", rule: verdict.rule, subject: verdict.subject, command: text8 };
|
|
13782
13936
|
case "snapshot":
|
|
13783
|
-
return { kind: "snapshot", repo, op: verdict.op, command:
|
|
13937
|
+
return { kind: "snapshot", repo, op: verdict.op, command: text8 };
|
|
13784
13938
|
case "discard": {
|
|
13785
13939
|
const entries = facts.dirty();
|
|
13786
|
-
if (entries === void 0) return { kind: "unknown", command:
|
|
13940
|
+
if (entries === void 0) return { kind: "unknown", command: text8 };
|
|
13787
13941
|
const foreign = foreignFiles(
|
|
13788
13942
|
context.runtime,
|
|
13789
13943
|
repo,
|
|
13790
13944
|
context.owner,
|
|
13791
13945
|
targetsOf(entries, verdict.discard, facts.prefix)
|
|
13792
13946
|
);
|
|
13793
|
-
return foreign.length > 0 ? { kind: "foreign", files: foreign, command:
|
|
13947
|
+
return foreign.length > 0 ? { kind: "foreign", files: foreign, command: text8 } : { kind: "snapshot", repo, op: verdict.op, command: text8 };
|
|
13794
13948
|
}
|
|
13795
13949
|
}
|
|
13796
13950
|
};
|
|
@@ -13907,9 +14061,9 @@ var parseJournalLine = (line2) => {
|
|
|
13907
14061
|
};
|
|
13908
14062
|
};
|
|
13909
14063
|
var readJournal = (runtime, root) => {
|
|
13910
|
-
const
|
|
13911
|
-
if (!
|
|
13912
|
-
return
|
|
14064
|
+
const text8 = runtime.readText(journalPath(root));
|
|
14065
|
+
if (!text8.ok) return [];
|
|
14066
|
+
return text8.value.split("\n").flatMap((line2) => {
|
|
13913
14067
|
const entry2 = line2.trim() === "" ? void 0 : parseJournalLine(line2);
|
|
13914
14068
|
return entry2 === void 0 ? [] : [entry2];
|
|
13915
14069
|
});
|
|
@@ -14101,9 +14255,9 @@ var review = (input, commands, owner) => {
|
|
|
14101
14255
|
return { blocked: void 0, snapshots, reads };
|
|
14102
14256
|
};
|
|
14103
14257
|
var gitPreToolUse = (input) => {
|
|
14104
|
-
const
|
|
14105
|
-
if (
|
|
14106
|
-
const commands = normalizeCommand(
|
|
14258
|
+
const text8 = shellText(input.payload);
|
|
14259
|
+
if (text8 === void 0) return ALLOW;
|
|
14260
|
+
const commands = normalizeCommand(text8, dialectOf(toolName2(input.payload))).commands;
|
|
14107
14261
|
if (commands.length === 0) return ALLOW;
|
|
14108
14262
|
const owner = ownerOf2(input.payload);
|
|
14109
14263
|
const result = review(input, commands, owner);
|
|
@@ -14165,8 +14319,8 @@ var GIT_HANDLERS = {
|
|
|
14165
14319
|
var SHADOW_FILE = "shadow.json";
|
|
14166
14320
|
var numberOf = (value) => typeof value === "number" && Number.isInteger(value) ? value : 0;
|
|
14167
14321
|
var readShadow = (runtime, paths) => {
|
|
14168
|
-
const
|
|
14169
|
-
const parsed =
|
|
14322
|
+
const text8 = runtime.readText(paths.cache(SHADOW_FILE));
|
|
14323
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
14170
14324
|
if (parsed?.ok !== true) return {};
|
|
14171
14325
|
return Object.fromEntries(
|
|
14172
14326
|
Object.entries(parsed.value).flatMap(
|
|
@@ -14250,11 +14404,11 @@ var ASSIGNMENT3 = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
|
14250
14404
|
var WRAPPERS2 = /* @__PURE__ */ new Set(["sudo", "command", "exec", "time", "nice", "nohup", "builtin", "env"]);
|
|
14251
14405
|
var liftHeredocs = (command) => {
|
|
14252
14406
|
const heredocs = [];
|
|
14253
|
-
const
|
|
14407
|
+
const text8 = command.replace(HEREDOC2, (_match, _quote, _tag, body2) => {
|
|
14254
14408
|
heredocs.push(body2);
|
|
14255
14409
|
return `<<__FM_HEREDOC_${String(heredocs.length - 1)}__`;
|
|
14256
14410
|
});
|
|
14257
|
-
return { text:
|
|
14411
|
+
return { text: text8, heredocs };
|
|
14258
14412
|
};
|
|
14259
14413
|
var endWord = (scan) => {
|
|
14260
14414
|
if (scan.word !== void 0) scan.words.push(scan.word);
|
|
@@ -14265,23 +14419,23 @@ var endSegment = (scan) => {
|
|
|
14265
14419
|
if (scan.words.length > 0) scan.segments.push(scan.words);
|
|
14266
14420
|
scan.words = [];
|
|
14267
14421
|
};
|
|
14268
|
-
var quotedEnd2 = (
|
|
14422
|
+
var quotedEnd2 = (text8, from, quote) => {
|
|
14269
14423
|
let index = from;
|
|
14270
|
-
while (index <
|
|
14424
|
+
while (index < text8.length && text8[index] !== quote) index += quote === '"' && text8[index] === "\\" ? 2 : 1;
|
|
14271
14425
|
return index;
|
|
14272
14426
|
};
|
|
14273
14427
|
var unescape = (value) => value.replace(/\\(["\\$`])/g, "$1");
|
|
14274
|
-
var splitWords = (
|
|
14428
|
+
var splitWords = (text8) => {
|
|
14275
14429
|
const scan = { words: [], segments: [], word: void 0 };
|
|
14276
|
-
for (let index = 0; index <
|
|
14277
|
-
const char =
|
|
14430
|
+
for (let index = 0; index < text8.length; index += 1) {
|
|
14431
|
+
const char = text8[index] ?? "";
|
|
14278
14432
|
if (char === '"' || char === "'") {
|
|
14279
|
-
const end = quotedEnd2(
|
|
14280
|
-
const inner =
|
|
14433
|
+
const end = quotedEnd2(text8, index + 1, char);
|
|
14434
|
+
const inner = text8.slice(index + 1, end);
|
|
14281
14435
|
scan.word = (scan.word ?? "") + (char === '"' ? unescape(inner) : inner);
|
|
14282
14436
|
index = end;
|
|
14283
|
-
} else if (char === "\\" && index + 1 <
|
|
14284
|
-
scan.word = (scan.word ?? "") + (
|
|
14437
|
+
} else if (char === "\\" && index + 1 < text8.length) {
|
|
14438
|
+
scan.word = (scan.word ?? "") + (text8[index + 1] === "\n" ? "" : text8[index + 1] ?? "");
|
|
14285
14439
|
index += 1;
|
|
14286
14440
|
} else if (SEPARATOR.test(char)) {
|
|
14287
14441
|
endSegment(scan);
|
|
@@ -14302,8 +14456,8 @@ var segmentOf = (words) => {
|
|
|
14302
14456
|
return { program: program.split(/[\\/]/).pop() ?? program, args: words.slice(index + 1) };
|
|
14303
14457
|
};
|
|
14304
14458
|
var shellWords = (command) => {
|
|
14305
|
-
const { text:
|
|
14306
|
-
const segments = splitWords(
|
|
14459
|
+
const { text: text8, heredocs } = liftHeredocs(command);
|
|
14460
|
+
const segments = splitWords(text8).flatMap((words) => {
|
|
14307
14461
|
const segment = segmentOf(words);
|
|
14308
14462
|
return segment === void 0 ? [] : [segment];
|
|
14309
14463
|
});
|
|
@@ -14408,7 +14562,7 @@ var sourceOf = (value, isFile, segment, heredocs) => {
|
|
|
14408
14562
|
if (inline3 !== void 0) return { kind: "inline", text: inline3 };
|
|
14409
14563
|
if (!isFile) return { kind: "inline", text: value };
|
|
14410
14564
|
if (value !== "-") return { kind: "file", path: value };
|
|
14411
|
-
const piped = segment.args.map((arg) => heredocIn(arg, heredocs)).find((
|
|
14565
|
+
const piped = segment.args.map((arg) => heredocIn(arg, heredocs)).find((text8) => text8 !== void 0);
|
|
14412
14566
|
return { kind: "inline", text: piped ?? "" };
|
|
14413
14567
|
};
|
|
14414
14568
|
var collect3 = (target2, segment, args2, spec2, heredocs) => {
|
|
@@ -14505,15 +14659,15 @@ var textOf2 = (input, found) => {
|
|
|
14505
14659
|
return accept(parts.join("\n\n"));
|
|
14506
14660
|
};
|
|
14507
14661
|
var decisionFor = (input, found, blocking2) => {
|
|
14508
|
-
const
|
|
14509
|
-
if (!
|
|
14662
|
+
const text8 = textOf2(input, found);
|
|
14663
|
+
if (!text8.ok) {
|
|
14510
14664
|
return blocking2 ? {
|
|
14511
14665
|
kind: "block",
|
|
14512
|
-
reason: modelReason(input.t, "prose.block.unreadable", { ...
|
|
14666
|
+
reason: modelReason(input.t, "prose.block.unreadable", { ...text8.error }),
|
|
14513
14667
|
human: input.t("prose.block.human")
|
|
14514
14668
|
} : void 0;
|
|
14515
14669
|
}
|
|
14516
|
-
const report2 = scoreProse(
|
|
14670
|
+
const report2 = scoreProse(text8.value, { context: "record" });
|
|
14517
14671
|
if (report2.allowed || report2.spans.length === 0) return void 0;
|
|
14518
14672
|
const params2 = {
|
|
14519
14673
|
target: input.t(TARGET_KEYS[found.target]),
|
|
@@ -14560,7 +14714,7 @@ var HOOK_ACTOR = "finitemonkey";
|
|
|
14560
14714
|
var liveFindings = (input, texts2) => {
|
|
14561
14715
|
const baseline = loadBaseline(input.runtime, input.cwd);
|
|
14562
14716
|
const accepted2 = acceptedFingerprints(baseline.ok ? baseline.value : EMPTY_BASELINE, input.runtime.now());
|
|
14563
|
-
return texts2.flatMap((
|
|
14717
|
+
return texts2.flatMap((text8) => scanText(text8, { rules: CREDENTIAL_RULES })).filter((finding4) => !accepted2.has(finding4.fingerprint));
|
|
14564
14718
|
};
|
|
14565
14719
|
var record2 = (input, findings, draft, where) => {
|
|
14566
14720
|
const ids2 = findings.map((finding4) => {
|
|
@@ -14821,12 +14975,12 @@ var secretsSessionStart = (input) => {
|
|
|
14821
14975
|
const remotes = remotesWithCredentials(input.runtime, input.cwd);
|
|
14822
14976
|
if (remotes.length === 0) return { kind: "allow" };
|
|
14823
14977
|
const params2 = { remotes: remotes.join(", "), remote: remotes[0] ?? "origin" };
|
|
14824
|
-
const
|
|
14978
|
+
const text8 = input.t("secrets.session.remote_credential", params2);
|
|
14825
14979
|
return {
|
|
14826
14980
|
kind: "allow",
|
|
14827
14981
|
output: {
|
|
14828
|
-
systemMessage: `finitemonkey: ${
|
|
14829
|
-
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext:
|
|
14982
|
+
systemMessage: `finitemonkey: ${text8}`,
|
|
14983
|
+
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: text8 }
|
|
14830
14984
|
}
|
|
14831
14985
|
};
|
|
14832
14986
|
};
|
|
@@ -14862,7 +15016,18 @@ var mergeOutputs = (left, right) => {
|
|
|
14862
15016
|
if (specific !== void 0) merged2["hookSpecificOutput"] = specific;
|
|
14863
15017
|
return merged2;
|
|
14864
15018
|
};
|
|
15019
|
+
var recordBlock = (input, decision) => {
|
|
15020
|
+
const paths = projectPaths(input);
|
|
15021
|
+
if (paths === void 0) return;
|
|
15022
|
+
logEvent(input.runtime, paths, "guard_block", {
|
|
15023
|
+
hook: input.event,
|
|
15024
|
+
tool: toolName2(input.payload),
|
|
15025
|
+
sessionId: input.payload.sessionId ?? null,
|
|
15026
|
+
claim: (decision.reason.split("\n")[0] ?? "").slice(0, 200)
|
|
15027
|
+
});
|
|
15028
|
+
};
|
|
14865
15029
|
var shaped = (input, decision) => {
|
|
15030
|
+
recordBlock(input, decision);
|
|
14866
15031
|
if (input.event !== "PreToolUse" || decision.output !== void 0) return decision;
|
|
14867
15032
|
const reason = capReason(decision.reason);
|
|
14868
15033
|
return {
|
|
@@ -14915,9 +15080,9 @@ var EMPTY_PAYLOAD = {
|
|
|
14915
15080
|
stopHookActive: false,
|
|
14916
15081
|
raw: {}
|
|
14917
15082
|
};
|
|
14918
|
-
var parseHookPayload = (event,
|
|
14919
|
-
if (interactive &&
|
|
14920
|
-
const parsed = parseJsonObject(
|
|
15083
|
+
var parseHookPayload = (event, text8, interactive) => {
|
|
15084
|
+
if (interactive && text8.trim() === "") return accept(EMPTY_PAYLOAD);
|
|
15085
|
+
const parsed = parseJsonObject(text8);
|
|
14921
15086
|
if (!parsed.ok) return reject(`stdin: ${parsed.error}`);
|
|
14922
15087
|
const payload = {
|
|
14923
15088
|
hookEventName: optionalString(parsed.value, "hook_event_name"),
|
|
@@ -14977,8 +15142,8 @@ var HOOK_PROBE_ENV = "FINITEMONKEY_HOOK_PROBE";
|
|
|
14977
15142
|
var messageOf2 = (error) => error instanceof Error ? error.message : String(error);
|
|
14978
15143
|
var decide2 = (input, handlers) => composeDecisions(input, handlers[input.event] ?? [allow]);
|
|
14979
15144
|
var evaluate = async (event, options) => {
|
|
14980
|
-
const
|
|
14981
|
-
const parsed = parseHookPayload(event,
|
|
15145
|
+
const text8 = await readAll(options.stdin);
|
|
15146
|
+
const parsed = parseHookPayload(event, text8, options.stdin.isTTY === true);
|
|
14982
15147
|
if (!parsed.ok) return { kind: "failed", payload: void 0, detail: parsed.error };
|
|
14983
15148
|
const payload = parsed.value;
|
|
14984
15149
|
if (options.env?.[HOOK_PROBE_ENV] === "1") return { kind: "decided", payload, decision: { kind: "allow" } };
|
|
@@ -15055,7 +15220,7 @@ var runHook = async (event, options) => {
|
|
|
15055
15220
|
var PROBE_TIMEOUT_MS2 = 1e4;
|
|
15056
15221
|
var SLOW_HOOK_MS = 1500;
|
|
15057
15222
|
var DEFAULT_TIMEOUT_S = 60;
|
|
15058
|
-
var expand = (
|
|
15223
|
+
var expand = (text8, vars) => text8.replace(/\$\{(\w+)\}|\$(\w+)/g, (match, braced, bare) => {
|
|
15059
15224
|
const name = braced ?? bare ?? "";
|
|
15060
15225
|
return vars[name] ?? match;
|
|
15061
15226
|
});
|
|
@@ -15104,7 +15269,7 @@ var run2 = (exec, invocation, cwd, env, input) => exec({
|
|
|
15104
15269
|
env: { ...env, [HOOK_PROBE_ENV]: "1" },
|
|
15105
15270
|
...input === void 0 ? {} : { input }
|
|
15106
15271
|
});
|
|
15107
|
-
var firstLine = (
|
|
15272
|
+
var firstLine = (text8) => maskText(text8.trim().split("\n")[0] ?? "").slice(0, 160);
|
|
15108
15273
|
var classify2 = (result) => {
|
|
15109
15274
|
if (!result.found) return { kind: "failed", problem: "missing", detail: "" };
|
|
15110
15275
|
if (result.timedOut) return { kind: "failed", problem: "timeout", detail: "" };
|
|
@@ -15335,8 +15500,8 @@ var installRecord = (value, root) => {
|
|
|
15335
15500
|
return records.find((record3) => record3["projectPath"] === root) ?? records[0];
|
|
15336
15501
|
};
|
|
15337
15502
|
var readJson = (env, path) => {
|
|
15338
|
-
const
|
|
15339
|
-
const parsed =
|
|
15503
|
+
const text8 = env.runtime.readText(path);
|
|
15504
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
15340
15505
|
return parsed?.ok === true ? parsed.value : void 0;
|
|
15341
15506
|
};
|
|
15342
15507
|
var installedPlugin = (env) => {
|
|
@@ -15347,13 +15512,13 @@ var installedPlugin = (env) => {
|
|
|
15347
15512
|
const root = toAbsolutePath(installPath);
|
|
15348
15513
|
const manifest = readJson(env, toAbsolutePath(join33(root, ".claude-plugin", "plugin.json")));
|
|
15349
15514
|
const hooksPath = toAbsolutePath(join33(root, "hooks", "hooks.json"));
|
|
15350
|
-
const
|
|
15351
|
-
const parsed =
|
|
15515
|
+
const text8 = env.runtime.readText(hooksPath);
|
|
15516
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
15352
15517
|
return {
|
|
15353
15518
|
root,
|
|
15354
15519
|
version: (manifest === void 0 ? void 0 : optionalString(manifest, "version")) ?? optionalString(record3, "version"),
|
|
15355
15520
|
hooksFile: parsed?.ok === true ? { source: "plugin", path: hooksPath, hooks: parsed.value["hooks"] } : void 0,
|
|
15356
|
-
hooksText:
|
|
15521
|
+
hooksText: text8.ok ? text8.value : void 0
|
|
15357
15522
|
};
|
|
15358
15523
|
};
|
|
15359
15524
|
var PRECEDENCE2 = ["managed", "local", "project", "user"];
|
|
@@ -15383,8 +15548,8 @@ var schemaFinding = (file, problems, claude) => problems.length === 0 ? [] : [
|
|
|
15383
15548
|
)
|
|
15384
15549
|
];
|
|
15385
15550
|
var unreadableProjectFiles = (env) => settingsPaths(env).filter(({ scope: scope2 }) => scope2 === "project" || scope2 === "local").flatMap(({ path }) => {
|
|
15386
|
-
const
|
|
15387
|
-
return
|
|
15551
|
+
const text8 = env.runtime.readText(path);
|
|
15552
|
+
return text8.ok && text8.value.trim() !== "" && !parseJsonObject(text8.value).ok ? [path] : [];
|
|
15388
15553
|
});
|
|
15389
15554
|
var schemaFindings = (env, plugin, claude) => {
|
|
15390
15555
|
const label2 = claude === void 0 ? "?" : claude.join(".");
|
|
@@ -15470,15 +15635,15 @@ var TOP_INSTRUCTIONS = ["AGENTS.md", "CLAUDE.md", ".claude/CLAUDE.md", "CLAUDE.l
|
|
|
15470
15635
|
var RULES_DIR = ".claude/rules";
|
|
15471
15636
|
var SHOWN = 3;
|
|
15472
15637
|
var readProjectFile = (env, relative11) => {
|
|
15473
|
-
const
|
|
15474
|
-
return
|
|
15638
|
+
const text8 = env.runtime.readText(toAbsolutePath(join35(env.root, ...relative11.split("/"))));
|
|
15639
|
+
return text8.ok ? text8.value : void 0;
|
|
15475
15640
|
};
|
|
15476
15641
|
var instructionFiles = (env) => {
|
|
15477
15642
|
const listed4 = env.runtime.listDir(toAbsolutePath(join35(env.root, ...RULES_DIR.split("/"))));
|
|
15478
15643
|
const rules = listed4.ok ? listed4.value.filter((name) => name.endsWith(".md")).map((name) => `${RULES_DIR}/${name}`) : [];
|
|
15479
15644
|
return [...TOP_INSTRUCTIONS, ...rules].flatMap((path) => {
|
|
15480
|
-
const
|
|
15481
|
-
return
|
|
15645
|
+
const text8 = readProjectFile(env, path);
|
|
15646
|
+
return text8 === void 0 ? [] : [{ path, text: text8 }];
|
|
15482
15647
|
});
|
|
15483
15648
|
};
|
|
15484
15649
|
var shortList = (items) => items.length <= SHOWN ? items.join(", ") : `${items.slice(0, SHOWN).join(", ")} (+${String(items.length - SHOWN)})`;
|
|
@@ -15526,10 +15691,10 @@ var hiddenInstructions = (env, deny2) => {
|
|
|
15526
15691
|
];
|
|
15527
15692
|
};
|
|
15528
15693
|
var localFile = (env) => readProjectFile(env, "CLAUDE.local.md") === void 0 ? [] : [finding("instructions.local", "warn", "doctor.instructions.local", {}, "doctor.instructions.fix_local")];
|
|
15529
|
-
var lineCount = (
|
|
15694
|
+
var lineCount = (text8) => text8.split("\n").length - (text8.endsWith("\n") ? 1 : 0);
|
|
15530
15695
|
var oversized = (env) => ["AGENTS.md", "CLAUDE.md"].flatMap((file) => {
|
|
15531
|
-
const
|
|
15532
|
-
const lines3 =
|
|
15696
|
+
const text8 = readProjectFile(env, file);
|
|
15697
|
+
const lines3 = text8 === void 0 ? 0 : lineCount(text8);
|
|
15533
15698
|
if (lines3 <= INSTRUCTION_LINE_CEILING) return [];
|
|
15534
15699
|
return [
|
|
15535
15700
|
finding(
|
|
@@ -15542,13 +15707,13 @@ var oversized = (env) => ["AGENTS.md", "CLAUDE.md"].flatMap((file) => {
|
|
|
15542
15707
|
];
|
|
15543
15708
|
});
|
|
15544
15709
|
var MIN_RULE_WORDS = 6;
|
|
15545
|
-
var ruleLines = (
|
|
15710
|
+
var ruleLines = (text8) => text8.split("\n").map((raw) => withoutCarriageReturn(raw).trim()).filter((line2) => !/^(#|\||```|@|>)/.test(line2)).map(
|
|
15546
15711
|
(line2) => line2.replace(/^([-*+]|\d+[.)])\s+/, "").replace(/\s+/g, " ").toLowerCase()
|
|
15547
15712
|
).filter((line2) => line2.split(" ").length >= MIN_RULE_WORDS);
|
|
15548
15713
|
var duplicatedRules = (files) => {
|
|
15549
15714
|
const seen = /* @__PURE__ */ new Map();
|
|
15550
|
-
for (const { path, text:
|
|
15551
|
-
for (const line2 of ruleLines(
|
|
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));
|
|
15552
15717
|
}
|
|
15553
15718
|
return [...seen].filter(([, paths]) => paths.size > 1).map(([line2]) => line2);
|
|
15554
15719
|
};
|
|
@@ -15598,13 +15763,13 @@ var ceilingOf = (config) => {
|
|
|
15598
15763
|
const parsed = typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
|
|
15599
15764
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : AGENTS_MAX_LINES;
|
|
15600
15765
|
};
|
|
15601
|
-
var lineCount2 = (
|
|
15766
|
+
var lineCount2 = (text8) => text8.replace(/\r?\n$/, "").split(/\r?\n/).length;
|
|
15602
15767
|
var checkAgentsFile = ({ root, runtime }) => {
|
|
15603
|
-
const
|
|
15604
|
-
if (!
|
|
15768
|
+
const text8 = runtime.readText(toAbsolutePath(join37(root, AGENTS_FILE)));
|
|
15769
|
+
if (!text8.ok) return [];
|
|
15605
15770
|
const config = runtime.readText(toAbsolutePath(join37(root, CONFIG_FILE)));
|
|
15606
15771
|
const max = ceilingOf(config.ok ? config.value : void 0);
|
|
15607
|
-
const lines3 = lineCount2(
|
|
15772
|
+
const lines3 = lineCount2(text8.value);
|
|
15608
15773
|
const params2 = { file: AGENTS_FILE, lines: lines3, max };
|
|
15609
15774
|
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)];
|
|
15610
15775
|
};
|
|
@@ -15785,9 +15950,9 @@ var manualLines = (ctx, report2) => report2.checks.some((item2) => item2.status
|
|
|
15785
15950
|
var writeReport = (ctx, data, result) => {
|
|
15786
15951
|
const env = doctorEnvFrom(ctx);
|
|
15787
15952
|
const path = reportPath(env);
|
|
15788
|
-
const
|
|
15953
|
+
const text8 = reportMarkdown(env, ctx, toEnvelope("doctor", result));
|
|
15789
15954
|
const dir = ctx.runtime.mkdir(toAbsolutePath(dirname7(path)));
|
|
15790
|
-
const written = dir.ok ? ctx.runtime.writeAtomic(path,
|
|
15955
|
+
const written = dir.ok ? ctx.runtime.writeAtomic(path, text8) : dir;
|
|
15791
15956
|
if (!written.ok) return failureFrom(ctx.t, applyProblem({ path, detail: written.error }));
|
|
15792
15957
|
const human2 = [...result.human ?? [], ctx.t("doctor.report.written", { path })];
|
|
15793
15958
|
return { ...result, data: { ...data, reportPath: path }, human: human2 };
|
|
@@ -15818,6 +15983,100 @@ var doctorCommand = {
|
|
|
15818
15983
|
run: (ctx, args2) => Promise.resolve(runDoctor(ctx, args2.values["fix"] === true, args2.values["report"] === true))
|
|
15819
15984
|
};
|
|
15820
15985
|
|
|
15986
|
+
// src/gain/gain.ts
|
|
15987
|
+
import { join as join39 } from "node:path";
|
|
15988
|
+
var GAIN_SCHEMA = "finitemonkey/gain@1";
|
|
15989
|
+
var DAY_MS2 = 864e5;
|
|
15990
|
+
var SAVE_WINDOW_MS = DAY_MS2;
|
|
15991
|
+
var readLog = (runtime, paths) => {
|
|
15992
|
+
const text8 = runtime.readText(paths.log);
|
|
15993
|
+
if (!text8.ok) return [];
|
|
15994
|
+
return text8.value.split("\n").flatMap((line2) => {
|
|
15995
|
+
const parsed = parseJsonObject(line2);
|
|
15996
|
+
if (!parsed.ok || !isRecord(parsed.value)) return [];
|
|
15997
|
+
const { at, event } = parsed.value;
|
|
15998
|
+
const time = typeof at === "string" ? Date.parse(at) : Number.NaN;
|
|
15999
|
+
return typeof event === "string" && Number.isFinite(time) ? [{ at: time, event }] : [];
|
|
16000
|
+
});
|
|
16001
|
+
};
|
|
16002
|
+
var readGates = (runtime, paths) => {
|
|
16003
|
+
const listed4 = runtime.listDir(toAbsolutePath(join39(paths.dir, "evidence")));
|
|
16004
|
+
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);
|
|
16006
|
+
};
|
|
16007
|
+
var countsIn = (log, gates, from, to) => {
|
|
16008
|
+
const inWindow = (at) => at >= from && at < to;
|
|
16009
|
+
const lines3 = log.filter((line2) => inWindow(line2.at));
|
|
16010
|
+
const runs = gates.filter((entry2) => inWindow(Date.parse(entry2.at)));
|
|
16011
|
+
const passedAt = gates.filter((entry2) => entry2.status === "passed").map((entry2) => Date.parse(entry2.at));
|
|
16012
|
+
const blocks = lines3.filter((line2) => line2.event === "guard_block");
|
|
16013
|
+
return {
|
|
16014
|
+
blocks: blocks.length,
|
|
16015
|
+
saves: blocks.filter((block4) => passedAt.some((at) => at > block4.at && at - block4.at <= SAVE_WINDOW_MS)).length,
|
|
16016
|
+
releases: lines3.filter((line2) => line2.event === "person.allow").length,
|
|
16017
|
+
falsePositives: lines3.filter((line2) => line2.event === "person.allow").length + lines3.filter((line2) => line2.event === "guard_false_positive").length,
|
|
16018
|
+
gateRuns: runs.length,
|
|
16019
|
+
gateFailed: runs.filter((entry2) => entry2.status === "failed").length,
|
|
16020
|
+
gateUnverified: runs.filter((entry2) => entry2.status === "unverified").length,
|
|
16021
|
+
gateMs: runs.reduce((sum, entry2) => sum + (entry2.durationMs ?? 0), 0)
|
|
16022
|
+
};
|
|
16023
|
+
};
|
|
16024
|
+
var gainReport = (runtime, paths, days) => {
|
|
16025
|
+
const to = Date.parse(runtime.now());
|
|
16026
|
+
const from = to - days * DAY_MS2;
|
|
16027
|
+
const log = readLog(runtime, paths);
|
|
16028
|
+
const gates = readGates(runtime, paths);
|
|
16029
|
+
return {
|
|
16030
|
+
schema: GAIN_SCHEMA,
|
|
16031
|
+
days,
|
|
16032
|
+
from: new Date(from).toISOString(),
|
|
16033
|
+
to: new Date(to).toISOString(),
|
|
16034
|
+
current: countsIn(log, gates, from, to + 1),
|
|
16035
|
+
previous: countsIn(log, gates, from - days * DAY_MS2, from)
|
|
16036
|
+
};
|
|
16037
|
+
};
|
|
16038
|
+
|
|
16039
|
+
// src/commands/gain.ts
|
|
16040
|
+
var DEFAULT_DAYS = 30;
|
|
16041
|
+
var trend = (ctx, now, before) => now === before ? "" : ` (${ctx.t(now > before ? "gain.up" : "gain.down", { delta: Math.abs(now - before) })})`;
|
|
16042
|
+
var ROWS = [
|
|
16043
|
+
{ key: "gain.row.blocks", pick: (counts) => counts.blocks },
|
|
16044
|
+
{ key: "gain.row.saves", pick: (counts) => counts.saves },
|
|
16045
|
+
{ key: "gain.row.false_positives", pick: (counts) => counts.falsePositives },
|
|
16046
|
+
{ key: "gain.row.gate_runs", pick: (counts) => counts.gateRuns },
|
|
16047
|
+
{ key: "gain.row.gate_failed", pick: (counts) => counts.gateFailed },
|
|
16048
|
+
{ key: "gain.row.gate_unverified", pick: (counts) => counts.gateUnverified }
|
|
16049
|
+
];
|
|
16050
|
+
var linesOf = (ctx, report2) => [
|
|
16051
|
+
ctx.t("gain.title", { days: report2.days }),
|
|
16052
|
+
...ROWS.map(
|
|
16053
|
+
({ key, pick: pick4 }) => ` ${ctx.t(key, { count: pick4(report2.current) })}${trend(ctx, pick4(report2.current), pick4(report2.previous))}`
|
|
16054
|
+
),
|
|
16055
|
+
` ${ctx.t("gain.row.gate_time", { minutes: Math.round(report2.current.gateMs / 6e4) })}`,
|
|
16056
|
+
ctx.t("gain.footer")
|
|
16057
|
+
];
|
|
16058
|
+
var runGain = (ctx, rawDays) => {
|
|
16059
|
+
const days = rawDays === void 0 ? DEFAULT_DAYS : Number.parseInt(rawDays, 10);
|
|
16060
|
+
if (!Number.isInteger(days) || days < 1 || days > 365) {
|
|
16061
|
+
return failureFrom(
|
|
16062
|
+
ctx.t,
|
|
16063
|
+
usageProblem("invalid_arguments", "error.invalid_arguments", "finitemonkey gain --days 30", {
|
|
16064
|
+
detail: "--days 1-365"
|
|
16065
|
+
})
|
|
16066
|
+
);
|
|
16067
|
+
}
|
|
16068
|
+
const report2 = gainReport(ctx.runtime, fmPaths(findRepoRoot(ctx.runtime, ctx.cwd) ?? ctx.cwd), days);
|
|
16069
|
+
return success(report2, linesOf(ctx, report2));
|
|
16070
|
+
};
|
|
16071
|
+
var gainCommand = {
|
|
16072
|
+
path: ["gain"],
|
|
16073
|
+
summaryKey: "command.gain.summary",
|
|
16074
|
+
usage: "finitemonkey gain [--days 1-365]",
|
|
16075
|
+
status: "ready",
|
|
16076
|
+
options: { days: { type: "string" } },
|
|
16077
|
+
run: (ctx, args2) => Promise.resolve(runGain(ctx, stringOption(args2.values, "days")))
|
|
16078
|
+
};
|
|
16079
|
+
|
|
15821
16080
|
// src/gate/baseline-run.ts
|
|
15822
16081
|
var runBaseline = (options) => {
|
|
15823
16082
|
const ready = prepareGate({ ...options, noSkip: true });
|
|
@@ -16113,15 +16372,15 @@ var commitMsgCommand = {
|
|
|
16113
16372
|
usageProblem("invalid_arguments", "error.invalid_arguments", COMMIT_MSG_USAGE, { detail: "<message-file>" })
|
|
16114
16373
|
)
|
|
16115
16374
|
);
|
|
16116
|
-
const
|
|
16117
|
-
if (!
|
|
16375
|
+
const text8 = ctx.runtime.readText(toAbsolutePath(file, ctx.cwd));
|
|
16376
|
+
if (!text8.ok)
|
|
16118
16377
|
return Promise.resolve(
|
|
16119
16378
|
failureFrom(
|
|
16120
16379
|
ctx.t,
|
|
16121
16380
|
usageProblem("invalid_arguments", "error.invalid_arguments", COMMIT_MSG_USAGE, { detail: file })
|
|
16122
16381
|
)
|
|
16123
16382
|
);
|
|
16124
|
-
const failures = commitMessageProblems(
|
|
16383
|
+
const failures = commitMessageProblems(text8.value);
|
|
16125
16384
|
const fix = "git commit --amend, using type(scope): subject and no AI attribution lines";
|
|
16126
16385
|
return Promise.resolve(
|
|
16127
16386
|
checksResult(ctx.t, COMMIT_MSG_SCHEMA, "githook_failed", [{ id: "commit-messages", failures, fix }])
|
|
@@ -16259,11 +16518,11 @@ var mergeVerifyCommand = {
|
|
|
16259
16518
|
var REFLOG_LIMIT = 300;
|
|
16260
16519
|
var LIST_LIMIT = 50;
|
|
16261
16520
|
var SHA = /^[0-9a-f]{40,64}$/;
|
|
16262
|
-
var
|
|
16521
|
+
var linesOf2 = (text8) => text8.split("\n").filter((line2) => line2.trim() !== "");
|
|
16263
16522
|
var reflogLines = (runtime, repo) => {
|
|
16264
16523
|
const result = runtime.git(["reflog", "show", "--all", `-n${String(REFLOG_LIMIT)}`, "--format=%H %gD"], repo.root);
|
|
16265
16524
|
if (!result.ok) return [];
|
|
16266
|
-
return
|
|
16525
|
+
return linesOf2(result.stdout).flatMap((line2) => {
|
|
16267
16526
|
const [sha, ref] = line2.split(" ");
|
|
16268
16527
|
return sha === void 0 || ref === void 0 || !SHA.test(sha) || ref.startsWith("refs/finitemonkey/") ? [] : [{ sha, ref }];
|
|
16269
16528
|
});
|
|
@@ -16271,11 +16530,11 @@ var reflogLines = (runtime, repo) => {
|
|
|
16271
16530
|
var unreachable = (runtime, repo, shas) => {
|
|
16272
16531
|
if (shas.length === 0) return /* @__PURE__ */ new Set();
|
|
16273
16532
|
const result = runtime.git(["rev-list", ...shas, "--not", "--branches", "--tags", "--remotes"], repo.root);
|
|
16274
|
-
return new Set(result.ok ?
|
|
16533
|
+
return new Set(result.ok ? linesOf2(result.stdout) : []);
|
|
16275
16534
|
};
|
|
16276
16535
|
var fsckDangling = (runtime, repo) => {
|
|
16277
16536
|
const result = runtime.git(["fsck", "--no-reflogs", "--no-progress", "--dangling"], repo.root);
|
|
16278
|
-
const lines3 =
|
|
16537
|
+
const lines3 = linesOf2(result.stdout);
|
|
16279
16538
|
const pick4 = (kind) => lines3.flatMap((line2) => {
|
|
16280
16539
|
const [label2, type, sha] = line2.split(" ");
|
|
16281
16540
|
return label2 === "dangling" && type === kind && sha !== void 0 ? [sha] : [];
|
|
@@ -16286,7 +16545,7 @@ var describe = (runtime, repo, shas) => {
|
|
|
16286
16545
|
if (shas.length === 0) return /* @__PURE__ */ new Map();
|
|
16287
16546
|
const result = runtime.git(["log", "--no-walk=unsorted", "--format=%H%x09%cr%x09%s", ...shas], repo.root);
|
|
16288
16547
|
return new Map(
|
|
16289
|
-
|
|
16548
|
+
linesOf2(result.stdout).flatMap((line2) => {
|
|
16290
16549
|
const [sha, when, ...subject2] = line2.split(" ");
|
|
16291
16550
|
return sha === void 0 ? [] : [[sha, [when ?? "", subject2.join(" ")]]];
|
|
16292
16551
|
})
|
|
@@ -16380,7 +16639,7 @@ var rescueCommand = {
|
|
|
16380
16639
|
};
|
|
16381
16640
|
|
|
16382
16641
|
// src/git/undo.ts
|
|
16383
|
-
import { basename as basename3, dirname as dirname8, join as
|
|
16642
|
+
import { basename as basename3, dirname as dirname8, join as join40 } from "node:path";
|
|
16384
16643
|
var INDEX_SUBJECT = "finitemonkey index:";
|
|
16385
16644
|
var gitLine = (runtime, repo, args2) => {
|
|
16386
16645
|
const result = runtime.git(args2, repo.root);
|
|
@@ -16423,7 +16682,7 @@ var findSnapshot = (views, wanted) => {
|
|
|
16423
16682
|
};
|
|
16424
16683
|
var stampOf2 = (iso) => iso.replace(/[:.]/g, "-");
|
|
16425
16684
|
var worktreePath = (runtime, repo) => {
|
|
16426
|
-
const base =
|
|
16685
|
+
const base = join40(dirname8(repo.root), `${basename3(repo.root)}-undo-${stampOf2(runtime.now())}`);
|
|
16427
16686
|
return toAbsolutePath(runtime.stat(toAbsolutePath(base)) === void 0 ? base : `${base}-2`);
|
|
16428
16687
|
};
|
|
16429
16688
|
var intoWorktree = (runtime, repo, view2) => {
|
|
@@ -16548,7 +16807,7 @@ var GLOBAL_FLAGS = [
|
|
|
16548
16807
|
{ flag: "--help, -h", key: "flag.help" }
|
|
16549
16808
|
];
|
|
16550
16809
|
var exitCodeHelp = (ctx) => EXIT_CODE_NAMES.map((name) => ({ code: ExitCode[name], name, summary: ctx.t(exitCodeDocs[ExitCode[name]]) }));
|
|
16551
|
-
var pad = (
|
|
16810
|
+
var pad = (text8, width) => text8.padEnd(Math.max(width, text8.length + 1));
|
|
16552
16811
|
var describe2 = (ctx, spec2) => ({
|
|
16553
16812
|
name: commandName(spec2),
|
|
16554
16813
|
usage: spec2.usage,
|
|
@@ -16658,7 +16917,7 @@ var hookCommand = {
|
|
|
16658
16917
|
};
|
|
16659
16918
|
|
|
16660
16919
|
// src/commands/i18n-check.ts
|
|
16661
|
-
import { join as
|
|
16920
|
+
import { join as join41 } from "node:path";
|
|
16662
16921
|
|
|
16663
16922
|
// src/i18n/message.ts
|
|
16664
16923
|
var ARG = /^\s*([a-zA-Z][a-zA-Z0-9_]*)\s*(?:,\s*(plural|select)\s*,)?/;
|
|
@@ -16690,10 +16949,10 @@ var parseBranches = (body2, inPlural) => {
|
|
|
16690
16949
|
};
|
|
16691
16950
|
var parseNodes = (source, inPlural) => {
|
|
16692
16951
|
const nodes = [];
|
|
16693
|
-
let
|
|
16952
|
+
let text8 = "";
|
|
16694
16953
|
const flush2 = () => {
|
|
16695
|
-
if (
|
|
16696
|
-
|
|
16954
|
+
if (text8 !== "") nodes.push({ kind: "text", text: text8 });
|
|
16955
|
+
text8 = "";
|
|
16697
16956
|
};
|
|
16698
16957
|
for (let index = 0; index < source.length; index += 1) {
|
|
16699
16958
|
const char = source.charAt(index);
|
|
@@ -16703,7 +16962,7 @@ var parseNodes = (source, inPlural) => {
|
|
|
16703
16962
|
continue;
|
|
16704
16963
|
}
|
|
16705
16964
|
if (char !== "{") {
|
|
16706
|
-
|
|
16965
|
+
text8 += char;
|
|
16707
16966
|
continue;
|
|
16708
16967
|
}
|
|
16709
16968
|
const cursor = { source, index: index + 1 };
|
|
@@ -16711,18 +16970,18 @@ var parseNodes = (source, inPlural) => {
|
|
|
16711
16970
|
const inner = end < 0 ? void 0 : source.slice(index + 1, end);
|
|
16712
16971
|
const match = inner === void 0 ? null : ARG.exec(inner);
|
|
16713
16972
|
if (inner === void 0 || match === null) {
|
|
16714
|
-
|
|
16973
|
+
text8 += char;
|
|
16715
16974
|
continue;
|
|
16716
16975
|
}
|
|
16717
16976
|
const name = match[1] ?? "";
|
|
16718
16977
|
const type = match[2] === "plural" || match[2] === "select" ? match[2] : void 0;
|
|
16719
16978
|
if (type === void 0 && match[0].trim().length !== inner.trim().length) {
|
|
16720
|
-
|
|
16979
|
+
text8 += char;
|
|
16721
16980
|
continue;
|
|
16722
16981
|
}
|
|
16723
16982
|
const branches = type === void 0 ? void 0 : parseBranches(inner.slice(match[0].length), type === "plural");
|
|
16724
16983
|
if (type !== void 0 && branches === void 0) {
|
|
16725
|
-
|
|
16984
|
+
text8 += char;
|
|
16726
16985
|
continue;
|
|
16727
16986
|
}
|
|
16728
16987
|
flush2();
|
|
@@ -16845,8 +17104,8 @@ var RELEASE_HINTS = [
|
|
|
16845
17104
|
/\bFINITEMONKEY_[A-Z_]+/,
|
|
16846
17105
|
/\b[A-Z][A-Z0-9_]{2,}=/
|
|
16847
17106
|
];
|
|
16848
|
-
var releaseHints = (
|
|
16849
|
-
const match = pattern.exec(
|
|
17107
|
+
var releaseHints = (text8) => RELEASE_HINTS.flatMap((pattern) => {
|
|
17108
|
+
const match = pattern.exec(text8);
|
|
16850
17109
|
return match === null ? [] : [match[0]];
|
|
16851
17110
|
});
|
|
16852
17111
|
var hintProblems = (lang, catalog) => Object.keys(catalog).filter((key) => MODEL_FACING.test(key)).sort().flatMap((key) => {
|
|
@@ -16884,10 +17143,10 @@ var unreadable = (dir, file, detail) => ({
|
|
|
16884
17143
|
exitCode: ExitCode.NotConfigured
|
|
16885
17144
|
});
|
|
16886
17145
|
var readCatalog = (ctx, dir, lang) => {
|
|
16887
|
-
const file = toAbsolutePath(
|
|
16888
|
-
const
|
|
16889
|
-
if (!
|
|
16890
|
-
const parsed = parseJsonObject(
|
|
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);
|
|
16891
17150
|
return parsed.ok ? parsed : reject(unreadable(dir, file, parsed.error));
|
|
16892
17151
|
};
|
|
16893
17152
|
var loadCatalogs = (ctx, dir) => {
|
|
@@ -16907,8 +17166,8 @@ var describeProblem = (ctx, problem4) => {
|
|
|
16907
17166
|
return ` ${problem4.lang} ${problem4.key} ${kind}`;
|
|
16908
17167
|
};
|
|
16909
17168
|
var loadReview = (ctx, dir) => {
|
|
16910
|
-
const
|
|
16911
|
-
const parsed =
|
|
17169
|
+
const text8 = ctx.runtime.readText(toAbsolutePath(join41(dir, "review.json")));
|
|
17170
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
16912
17171
|
if (parsed?.ok !== true) return void 0;
|
|
16913
17172
|
const statuses = (lang) => {
|
|
16914
17173
|
const value = parsed.value[lang];
|
|
@@ -16948,7 +17207,7 @@ var i18nCheckCommand = {
|
|
|
16948
17207
|
};
|
|
16949
17208
|
|
|
16950
17209
|
// src/history/look-back.ts
|
|
16951
|
-
import { join as
|
|
17210
|
+
import { join as join42 } from "node:path";
|
|
16952
17211
|
|
|
16953
17212
|
// src/history/patterns.ts
|
|
16954
17213
|
import { posix as posix5 } from "node:path";
|
|
@@ -17053,8 +17312,8 @@ var addResults = (draft, blocks) => {
|
|
|
17053
17312
|
let any = false;
|
|
17054
17313
|
for (const block4 of blocks) {
|
|
17055
17314
|
if (!isRecord(block4) || block4["type"] !== "tool_result" || typeof block4["tool_use_id"] !== "string") continue;
|
|
17056
|
-
const
|
|
17057
|
-
draft.results.set(block4["tool_use_id"], { failed: block4["is_error"] === true || FAILED_EXIT.test(
|
|
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 });
|
|
17058
17317
|
any = true;
|
|
17059
17318
|
}
|
|
17060
17319
|
return any;
|
|
@@ -17084,7 +17343,7 @@ var parseTranscript = (jsonl) => {
|
|
|
17084
17343
|
// src/history/look-back.ts
|
|
17085
17344
|
var SAMPLES = 3;
|
|
17086
17345
|
var projectLogKey = (root) => root.replace(/[^A-Za-z0-9-]/g, "-");
|
|
17087
|
-
var projectLogDir = (runtime, root, env) =>
|
|
17346
|
+
var projectLogDir = (runtime, root, env) => join42(env["CLAUDE_CONFIG_DIR"] ?? join42(runtime.homeDir(), ".claude"), "projects", projectLogKey(root));
|
|
17088
17347
|
var lookBack = (runtime, root, env) => {
|
|
17089
17348
|
const dir = projectLogDir(runtime, root, env);
|
|
17090
17349
|
const listed4 = runtime.listDir(toAbsolutePath(dir));
|
|
@@ -17105,9 +17364,9 @@ var lookBack = (runtime, root, env) => {
|
|
|
17105
17364
|
};
|
|
17106
17365
|
let turns = 0;
|
|
17107
17366
|
for (const name of files) {
|
|
17108
|
-
const
|
|
17109
|
-
if (!
|
|
17110
|
-
for (const turn of parseTranscript(
|
|
17367
|
+
const text8 = runtime.readText(toAbsolutePath(join42(dir, name)));
|
|
17368
|
+
if (!text8.ok) continue;
|
|
17369
|
+
for (const turn of parseTranscript(text8.value)) {
|
|
17111
17370
|
turns += 1;
|
|
17112
17371
|
for (const hit3 of hitsIn(turn)) {
|
|
17113
17372
|
counts[hit3.pattern] += 1;
|
|
@@ -17155,7 +17414,7 @@ var walk2 = (a, b) => {
|
|
|
17155
17414
|
}
|
|
17156
17415
|
return ops;
|
|
17157
17416
|
};
|
|
17158
|
-
var splitLines = (
|
|
17417
|
+
var splitLines = (text8) => text8 === "" ? [] : text8.replace(/\r?\n$/, "").split(/\r?\n/);
|
|
17159
17418
|
var sharedPrefix = (a, b) => {
|
|
17160
17419
|
let count4 = 0;
|
|
17161
17420
|
while (count4 < a.length && count4 < b.length && a[count4] === b[count4]) count4 += 1;
|
|
@@ -17232,7 +17491,7 @@ var planData = (t, plan, mode, applied, backup) => ({
|
|
|
17232
17491
|
diff: planDiff(plan),
|
|
17233
17492
|
...backup === void 0 ? {} : { backup }
|
|
17234
17493
|
});
|
|
17235
|
-
var pad2 = (
|
|
17494
|
+
var pad2 = (text8, width) => text8.padEnd(width);
|
|
17236
17495
|
var planLines = (t, data, withDiff) => [
|
|
17237
17496
|
t("install.plan.title", { root: data.root, preset: data.preset }),
|
|
17238
17497
|
...data.actions.map(({ path, action }) => ` ${pad2(action, 13)}${path}`),
|
|
@@ -17357,7 +17616,7 @@ var applyAllows = (findings, allows, decided) => {
|
|
|
17357
17616
|
|
|
17358
17617
|
// src/look/node-capture.ts
|
|
17359
17618
|
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
17360
|
-
import { join as
|
|
17619
|
+
import { join as join43 } from "node:path";
|
|
17361
17620
|
|
|
17362
17621
|
// src/look/measure.ts
|
|
17363
17622
|
var measurePage = (doc) => {
|
|
@@ -17368,8 +17627,8 @@ var measurePage = (doc) => {
|
|
|
17368
17627
|
const walker = doc.createTreeWalker(doc.body, SHOW_TEXT);
|
|
17369
17628
|
const keys = /* @__PURE__ */ new Set();
|
|
17370
17629
|
for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
|
|
17371
|
-
const
|
|
17372
|
-
if (KEY2.test(
|
|
17630
|
+
const text8 = (node.textContent ?? "").trim();
|
|
17631
|
+
if (KEY2.test(text8) && !NOT_KEY.test(text8) || PLACEHOLDER2.test(text8)) keys.add(text8.slice(0, 80));
|
|
17373
17632
|
}
|
|
17374
17633
|
const small = [...doc.querySelectorAll("a, button, input, select, textarea, [role=button], [role=link]")].filter((element) => {
|
|
17375
17634
|
const box = element.getBoundingClientRect();
|
|
@@ -17402,8 +17661,8 @@ var slugOf = (route) => {
|
|
|
17402
17661
|
var watch = (page) => {
|
|
17403
17662
|
const errors = [];
|
|
17404
17663
|
const failed4 = [];
|
|
17405
|
-
const push = (list4,
|
|
17406
|
-
if (list4.length < MAX_EVENTS) list4.push(
|
|
17664
|
+
const push = (list4, text8) => {
|
|
17665
|
+
if (list4.length < MAX_EVENTS) list4.push(text8.slice(0, 200));
|
|
17407
17666
|
};
|
|
17408
17667
|
page.on("console", (message) => {
|
|
17409
17668
|
if (message.type() === "error" && !RESOURCE_ERROR.test(message.text())) push(errors, message.text());
|
|
@@ -17433,7 +17692,7 @@ var captureOne = async (browser, request, route, viewport) => {
|
|
|
17433
17692
|
const context = await browser.newContext({ viewport: { width: viewport, height: HEIGHT }, reducedMotion: "reduce" });
|
|
17434
17693
|
const page = await context.newPage();
|
|
17435
17694
|
const events = watch(page);
|
|
17436
|
-
const path =
|
|
17695
|
+
const path = join43(request.dir, `${slugOf(route)}-${String(viewport)}.png`);
|
|
17437
17696
|
const loadError = await loadAndShoot(page, `${request.baseUrl}${route}`, path, request.timeoutMs);
|
|
17438
17697
|
const measure = loadError === void 0 ? parseMeasure(await page.evaluate(MEASURE_SCRIPT)) : void 0;
|
|
17439
17698
|
await context.close();
|
|
@@ -17450,7 +17709,7 @@ var captureAll = async (browser, request) => {
|
|
|
17450
17709
|
|
|
17451
17710
|
// src/look/node-playwright.ts
|
|
17452
17711
|
import { createRequire } from "node:module";
|
|
17453
|
-
import { join as
|
|
17712
|
+
import { join as join44 } from "node:path";
|
|
17454
17713
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
17455
17714
|
var NAMES = ["playwright", "playwright-core"];
|
|
17456
17715
|
var isPlaywright = (value) => isRecord(value) && isRecord(value["chromium"]) && typeof value["chromium"]["launch"] === "function";
|
|
@@ -17465,7 +17724,7 @@ var tryLoad = async (base, name) => {
|
|
|
17465
17724
|
}
|
|
17466
17725
|
};
|
|
17467
17726
|
var loadPlaywright = async (root) => {
|
|
17468
|
-
for (const base of [
|
|
17727
|
+
for (const base of [join44(root, "package.json"), fileURLToPath3(import.meta.url)]) {
|
|
17469
17728
|
for (const name of NAMES) {
|
|
17470
17729
|
const found = await tryLoad(base, name);
|
|
17471
17730
|
if (found !== void 0) return found;
|
|
@@ -17486,8 +17745,8 @@ var launchBrowser = async (playwright) => {
|
|
|
17486
17745
|
|
|
17487
17746
|
// src/look/node-sheet.ts
|
|
17488
17747
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
17489
|
-
import { join as
|
|
17490
|
-
var escapeHtml = (
|
|
17748
|
+
import { join as join45 } from "node:path";
|
|
17749
|
+
var escapeHtml = (text8) => text8.replace(/[&<>"]/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[char] ?? char);
|
|
17491
17750
|
var card = (result) => {
|
|
17492
17751
|
const image = (() => {
|
|
17493
17752
|
try {
|
|
@@ -17509,7 +17768,7 @@ var contactSheet = async (browser, results, dir) => {
|
|
|
17509
17768
|
if (shown3.length === 0) return void 0;
|
|
17510
17769
|
const page = await browser.newPage({ viewport: { width: 1600, height: 900 } });
|
|
17511
17770
|
await page.setContent(`<!doctype html><style>${STYLE}</style>${shown3.map(card).join("")}`);
|
|
17512
|
-
const path =
|
|
17771
|
+
const path = join45(dir, "contact-sheet.png");
|
|
17513
17772
|
await page.screenshot({ path, fullPage: true });
|
|
17514
17773
|
await page.close();
|
|
17515
17774
|
return path;
|
|
@@ -17849,12 +18108,12 @@ var proseCheckCommand = {
|
|
|
17849
18108
|
};
|
|
17850
18109
|
|
|
17851
18110
|
// src/review/packet.ts
|
|
17852
|
-
import { join as
|
|
18111
|
+
import { join as join46 } from "node:path";
|
|
17853
18112
|
var REVIEW_STATE_FILE = ".finitemonkey/review/state.json";
|
|
17854
18113
|
var FULL_ROUNDS = 3;
|
|
17855
18114
|
var REVIEW_FILE_MAX_LINES = 200;
|
|
17856
18115
|
var LEDGER_MAX_LINES = 100;
|
|
17857
|
-
var statePath = (root) => toAbsolutePath(
|
|
18116
|
+
var statePath = (root) => toAbsolutePath(join46(root, REVIEW_STATE_FILE));
|
|
17858
18117
|
var readReviewState = (runtime, root) => {
|
|
17859
18118
|
const raw = runtime.readText(statePath(root));
|
|
17860
18119
|
const parsed = raw.ok ? parseJsonObject(raw.value) : void 0;
|
|
@@ -17866,7 +18125,7 @@ var git = (runtime, root, args2) => {
|
|
|
17866
18125
|
const result = runtime.git(args2, root);
|
|
17867
18126
|
return result.ok ? result.stdout : void 0;
|
|
17868
18127
|
};
|
|
17869
|
-
var lastLines = (
|
|
18128
|
+
var lastLines = (text8, max) => text8.split("\n").slice(-max).join("\n");
|
|
17870
18129
|
var section = (title, body2, empty) => `## ${title}
|
|
17871
18130
|
|
|
17872
18131
|
${body2 === void 0 || body2.trim() === "" ? empty : body2.trimEnd()}
|
|
@@ -17876,7 +18135,7 @@ var taskSections = (runtime, root) => {
|
|
|
17876
18135
|
const active = readState(runtime, paths).active;
|
|
17877
18136
|
if (active === void 0)
|
|
17878
18137
|
return [section("Contract", void 0, "(no active task)"), section("Evidence ledger", void 0, "(none)")];
|
|
17879
|
-
const contract = runtime.readText(toAbsolutePath(
|
|
18138
|
+
const contract = runtime.readText(toAbsolutePath(join46(root, taskRelative(active))));
|
|
17880
18139
|
const ledger = runtime.readText(paths.evidence(active));
|
|
17881
18140
|
return [
|
|
17882
18141
|
section("Contract", contract.ok ? contract.value : void 0, "(contract file missing)"),
|
|
@@ -17884,8 +18143,8 @@ var taskSections = (runtime, root) => {
|
|
|
17884
18143
|
];
|
|
17885
18144
|
};
|
|
17886
18145
|
var reviewFile = (runtime, root, base) => {
|
|
17887
|
-
const
|
|
17888
|
-
return
|
|
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");
|
|
17889
18148
|
};
|
|
17890
18149
|
var nextState = (previous, base, head) => {
|
|
17891
18150
|
if (previous?.base !== base) return { base, rounds: 1, lastHead: head };
|
|
@@ -17898,7 +18157,7 @@ var buildPacket = (runtime, root, base, head) => {
|
|
|
17898
18157
|
const range = `${from}...${head}`;
|
|
17899
18158
|
const diff = git(runtime, root, ["diff", "--no-color", "--no-ext-diff", range, "--", ".", ":(exclude).finitemonkey"]);
|
|
17900
18159
|
const owners = reviewFile(runtime, root, base);
|
|
17901
|
-
const
|
|
18160
|
+
const text8 = [
|
|
17902
18161
|
`# Review packet (round ${String(state.rounds)}, ${range})`,
|
|
17903
18162
|
"",
|
|
17904
18163
|
...state.rounds > FULL_ROUNDS ? ["Only the diff since the last review round is included.", ""] : [],
|
|
@@ -17906,10 +18165,10 @@ var buildPacket = (runtime, root, base, head) => {
|
|
|
17906
18165
|
...taskSections(runtime, root),
|
|
17907
18166
|
...owners === void 0 ? [] : [section("REVIEW.md (base branch)", owners, "")]
|
|
17908
18167
|
].join("\n");
|
|
17909
|
-
runtime.mkdir(toAbsolutePath(
|
|
18168
|
+
runtime.mkdir(toAbsolutePath(join46(root, ".finitemonkey", "review")));
|
|
17910
18169
|
runtime.writeAtomic(statePath(root), `${JSON.stringify(state, null, 2)}
|
|
17911
18170
|
`);
|
|
17912
|
-
return { round: state.rounds, base, head, range, text:
|
|
18171
|
+
return { round: state.rounds, base, head, range, text: text8 };
|
|
17913
18172
|
};
|
|
17914
18173
|
|
|
17915
18174
|
// src/commands/review.ts
|
|
@@ -17962,14 +18221,14 @@ var gitUnavailable = (detail) => ({
|
|
|
17962
18221
|
});
|
|
17963
18222
|
|
|
17964
18223
|
// src/commands/secrets-accept.ts
|
|
17965
|
-
var
|
|
18224
|
+
var DAY_MS3 = 864e5;
|
|
17966
18225
|
var daysOf = (values) => {
|
|
17967
18226
|
const raw = stringOption(values, "days");
|
|
17968
18227
|
if (raw === void 0) return EXCEPTION_MAX_DAYS;
|
|
17969
18228
|
const days = Number.parseInt(raw, 10);
|
|
17970
18229
|
return Number.isInteger(days) && days > 0 && days <= EXCEPTION_MAX_DAYS ? days : void 0;
|
|
17971
18230
|
};
|
|
17972
|
-
var plusDays = (iso, days) => new Date(Date.parse(iso) + days *
|
|
18231
|
+
var plusDays = (iso, days) => new Date(Date.parse(iso) + days * DAY_MS3).toISOString();
|
|
17973
18232
|
var failure3 = (ctx, problem4) => failureFrom(ctx.t, problem4);
|
|
17974
18233
|
var inputProblem = (ctx, id, reason, days) => {
|
|
17975
18234
|
const fix = "finitemonkey secrets accept <id> --reason <why> [--days 1-90] [--false-positive]";
|
|
@@ -18061,7 +18320,7 @@ var secretsAcceptCommand = {
|
|
|
18061
18320
|
};
|
|
18062
18321
|
|
|
18063
18322
|
// src/secrets/runbooks.ts
|
|
18064
|
-
import { dirname as dirname9, join as
|
|
18323
|
+
import { dirname as dirname9, join as join47 } from "node:path";
|
|
18065
18324
|
var PROVIDER_OF_RULE = {
|
|
18066
18325
|
"aws-access-token": "aws",
|
|
18067
18326
|
"aws-secret-access-key": "aws",
|
|
@@ -18083,7 +18342,7 @@ var RUNBOOKS_URL = "https://github.com/gabrielvieiraf/finitemonkey/blob/main/run
|
|
|
18083
18342
|
var runbookProvider = (rule) => PROVIDER_OF_RULE[rule] ?? "generic";
|
|
18084
18343
|
var runbookLink = (runtime, rule) => {
|
|
18085
18344
|
const file = `${runbookProvider(rule)}.md`;
|
|
18086
|
-
const local = toAbsolutePath(
|
|
18345
|
+
const local = toAbsolutePath(join47(dirname9(dirname9(runtime.selfPath)), "runbooks", file));
|
|
18087
18346
|
return runtime.stat(local)?.kind === "file" ? local : `${RUNBOOKS_URL}/${file}`;
|
|
18088
18347
|
};
|
|
18089
18348
|
|
|
@@ -18444,8 +18703,206 @@ var secretsScanCommand = {
|
|
|
18444
18703
|
}
|
|
18445
18704
|
};
|
|
18446
18705
|
|
|
18706
|
+
// src/statusline/gate-state.ts
|
|
18707
|
+
var GATE_HOOKS = /* @__PURE__ */ new Set(["Stop", "SubagentStop"]);
|
|
18708
|
+
var TAIL_BYTES = 64 * 1024;
|
|
18709
|
+
var lastGateFailure = (runtime, paths) => {
|
|
18710
|
+
const log = runtime.readText(paths.log);
|
|
18711
|
+
if (!log.ok) return void 0;
|
|
18712
|
+
const lines3 = log.value.slice(-TAIL_BYTES).split("\n").reverse();
|
|
18713
|
+
for (const line2 of lines3) {
|
|
18714
|
+
if (!line2.includes(HOOK_FAILURE_EVENT)) continue;
|
|
18715
|
+
const parsed = parseJsonObject(line2);
|
|
18716
|
+
if (!parsed.ok || !isRecord(parsed.value)) continue;
|
|
18717
|
+
const { event, hook, at } = parsed.value;
|
|
18718
|
+
if (event === HOOK_FAILURE_EVENT && typeof hook === "string" && GATE_HOOKS.has(hook) && typeof at === "string")
|
|
18719
|
+
return at;
|
|
18720
|
+
}
|
|
18721
|
+
return void 0;
|
|
18722
|
+
};
|
|
18723
|
+
var gateLight = (runtime, paths) => {
|
|
18724
|
+
const status = readGateStatus(runtime, paths);
|
|
18725
|
+
const failure4 = lastGateFailure(runtime, paths);
|
|
18726
|
+
const greenSince = status?.status === "passed" ? Date.parse(status.at) : Number.NEGATIVE_INFINITY;
|
|
18727
|
+
if (failure4 !== void 0 && Date.parse(failure4) >= greenSince) return "did_not_run";
|
|
18728
|
+
if (status === void 0) return "none";
|
|
18729
|
+
switch (status.status) {
|
|
18730
|
+
case "passed":
|
|
18731
|
+
return "green";
|
|
18732
|
+
case "failed":
|
|
18733
|
+
case "not_done":
|
|
18734
|
+
return "red";
|
|
18735
|
+
case "incomplete":
|
|
18736
|
+
return "incomplete";
|
|
18737
|
+
case "not_run":
|
|
18738
|
+
return "did_not_run";
|
|
18739
|
+
}
|
|
18740
|
+
};
|
|
18741
|
+
|
|
18742
|
+
// src/statusline/input.ts
|
|
18743
|
+
var text7 = (value) => typeof value === "string" && value !== "" ? value : void 0;
|
|
18744
|
+
var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
18745
|
+
var usedTokens = (usage) => {
|
|
18746
|
+
if (!isRecord(usage)) return void 0;
|
|
18747
|
+
return (num(usage["input_tokens"]) ?? 0) + (num(usage["cache_creation_input_tokens"]) ?? 0) + (num(usage["cache_read_input_tokens"]) ?? 0);
|
|
18748
|
+
};
|
|
18749
|
+
var contextPercent = (record3) => {
|
|
18750
|
+
const window = record3["context_window"];
|
|
18751
|
+
if (!isRecord(window)) return void 0;
|
|
18752
|
+
const direct = window["used_percentage"];
|
|
18753
|
+
if (typeof direct === "number" && Number.isFinite(direct)) return Math.round(direct);
|
|
18754
|
+
const size = window["context_window_size"];
|
|
18755
|
+
const used = usedTokens(window["current_usage"]);
|
|
18756
|
+
return typeof size === "number" && size > 0 && used !== void 0 ? Math.round(used / size * 100) : void 0;
|
|
18757
|
+
};
|
|
18758
|
+
var parseStatusInput = (raw) => {
|
|
18759
|
+
const parsed = parseJsonObject(raw.trim() === "" ? "{}" : raw);
|
|
18760
|
+
const record3 = parsed.ok ? parsed.value : {};
|
|
18761
|
+
const workspace = isRecord(record3["workspace"]) ? record3["workspace"] : {};
|
|
18762
|
+
return {
|
|
18763
|
+
sessionId: text7(record3["session_id"]),
|
|
18764
|
+
dir: text7(workspace["project_dir"]) ?? text7(workspace["current_dir"]) ?? text7(record3["cwd"]),
|
|
18765
|
+
contextPercent: contextPercent(record3),
|
|
18766
|
+
raw
|
|
18767
|
+
};
|
|
18768
|
+
};
|
|
18769
|
+
|
|
18770
|
+
// src/statusline/render.ts
|
|
18771
|
+
var SEPARATOR2 = " | ";
|
|
18772
|
+
var segmentText = (t, segment) => [
|
|
18773
|
+
"fm",
|
|
18774
|
+
...segment.task === void 0 ? [] : [segment.task],
|
|
18775
|
+
...segment.gate === "none" ? [] : [t(`statusline.gate.${segment.gate}`)],
|
|
18776
|
+
...segment.contextPercent === void 0 ? [] : [t("statusline.context", { percent: segment.contextPercent })]
|
|
18777
|
+
].join(" · ");
|
|
18778
|
+
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);
|
|
18780
|
+
var fit = (line2, columns) => {
|
|
18781
|
+
if (columns === void 0) return line2;
|
|
18782
|
+
const visible = graphemes(line2.replace(ANSI3, ""));
|
|
18783
|
+
return visible.length <= columns ? line2 : `${visible.slice(0, Math.max(0, columns - 1)).join("")}…`;
|
|
18784
|
+
};
|
|
18785
|
+
var composeLine = (wrapped, segment, columns) => {
|
|
18786
|
+
const lines3 = wrapped === "" ? [] : wrapped.split(/\r?\n/);
|
|
18787
|
+
const last = lines3.pop();
|
|
18788
|
+
const joined = last === void 0 || last.trim() === "" ? segment : `${last}${SEPARATOR2}${segment}`;
|
|
18789
|
+
return [...lines3, joined].map((line2) => fit(line2, columns)).join("\n");
|
|
18790
|
+
};
|
|
18791
|
+
|
|
18792
|
+
// src/statusline/node-shell.ts
|
|
18793
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
18794
|
+
var killTree = (child2) => {
|
|
18795
|
+
const pid = child2.pid;
|
|
18796
|
+
if (pid === void 0) return;
|
|
18797
|
+
try {
|
|
18798
|
+
if (process.platform === "win32") {
|
|
18799
|
+
spawn4("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true }).unref();
|
|
18800
|
+
} else process.kill(-pid, "SIGKILL");
|
|
18801
|
+
} catch {
|
|
18802
|
+
child2.kill("SIGKILL");
|
|
18803
|
+
}
|
|
18804
|
+
};
|
|
18805
|
+
var nodeShell = (command, input, cwd, timeoutMs) => new Promise((resolve2) => {
|
|
18806
|
+
const child2 = spawn4(command, {
|
|
18807
|
+
cwd,
|
|
18808
|
+
shell: true,
|
|
18809
|
+
detached: process.platform !== "win32",
|
|
18810
|
+
windowsHide: true,
|
|
18811
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
18812
|
+
});
|
|
18813
|
+
let out = "";
|
|
18814
|
+
const timer = setTimeout(() => {
|
|
18815
|
+
killTree(child2);
|
|
18816
|
+
resolve2({ text: void 0, timedOut: true });
|
|
18817
|
+
}, timeoutMs);
|
|
18818
|
+
child2.stdout.on("data", (chunk) => {
|
|
18819
|
+
out += chunk.toString("utf8");
|
|
18820
|
+
});
|
|
18821
|
+
child2.once("error", () => {
|
|
18822
|
+
clearTimeout(timer);
|
|
18823
|
+
resolve2({ text: void 0, timedOut: false });
|
|
18824
|
+
});
|
|
18825
|
+
child2.once("close", () => {
|
|
18826
|
+
clearTimeout(timer);
|
|
18827
|
+
resolve2({ text: out, timedOut: false });
|
|
18828
|
+
});
|
|
18829
|
+
child2.stdin.on("error", () => void 0);
|
|
18830
|
+
child2.stdin.end(input);
|
|
18831
|
+
});
|
|
18832
|
+
|
|
18833
|
+
// src/statusline/wrap.ts
|
|
18834
|
+
var WRAP_CACHE = "statusline.json";
|
|
18835
|
+
var KEEP3 = 20;
|
|
18836
|
+
var cached2 = (runtime, paths) => {
|
|
18837
|
+
const raw = readJsonFile(runtime, paths.cache(WRAP_CACHE))?.["sessions"];
|
|
18838
|
+
return isRecord(raw) ? Object.fromEntries(Object.entries(raw).filter((entry2) => typeof entry2[1] === "string")) : {};
|
|
18839
|
+
};
|
|
18840
|
+
var remember = (runtime, paths, session, text8) => {
|
|
18841
|
+
const sessions = Object.entries({ ...cached2(runtime, paths), [session]: text8 }).slice(-KEEP3);
|
|
18842
|
+
writeJsonFile(runtime, paths.cache(WRAP_CACHE), { sessions: Object.fromEntries(sessions) });
|
|
18843
|
+
};
|
|
18844
|
+
var runWrapped = async (runtime, paths, request, shell = nodeShell) => {
|
|
18845
|
+
const result = await shell(request.command, request.input, request.cwd, request.timeoutMs);
|
|
18846
|
+
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;
|
|
18850
|
+
}
|
|
18851
|
+
return paths === void 0 ? "" : cached2(runtime, paths)[request.session] ?? "";
|
|
18852
|
+
};
|
|
18853
|
+
|
|
18854
|
+
// src/statusline/run.ts
|
|
18855
|
+
var STDIN_MS = 300;
|
|
18856
|
+
var WRAP_MS = 700;
|
|
18857
|
+
var wrapOf = (ctx, root) => {
|
|
18858
|
+
const text8 = ctx.runtime.readText(toAbsolutePath(`${root}/${LOCK_FILE}`));
|
|
18859
|
+
const lock = text8.ok ? parseLock(text8.value) : void 0;
|
|
18860
|
+
if (!lock?.ok) return void 0;
|
|
18861
|
+
return lock.value.entries.find((entry2) => entry2.added?.statusLine !== void 0)?.added?.statusLine?.wrap ?? void 0;
|
|
18862
|
+
};
|
|
18863
|
+
var columnsOf = (ctx) => {
|
|
18864
|
+
const columns = Number.parseInt(ctx.env["COLUMNS"] ?? "", 10);
|
|
18865
|
+
return Number.isInteger(columns) && columns > 0 ? columns : void 0;
|
|
18866
|
+
};
|
|
18867
|
+
var lineFor = async (ctx, input) => {
|
|
18868
|
+
const start = input.dir === void 0 ? ctx.cwd : toAbsolutePath(input.dir, ctx.cwd);
|
|
18869
|
+
const root = findGitRoot(start, ctx.runtime);
|
|
18870
|
+
const paths = root === void 0 ? void 0 : fmPaths(root);
|
|
18871
|
+
const wrap = root === void 0 ? void 0 : wrapOf(ctx, root);
|
|
18872
|
+
const wrapped = wrap === void 0 || root === void 0 ? "" : await runWrapped(ctx.runtime, paths, {
|
|
18873
|
+
command: wrap,
|
|
18874
|
+
input: input.raw,
|
|
18875
|
+
cwd: root,
|
|
18876
|
+
timeoutMs: WRAP_MS,
|
|
18877
|
+
session: input.sessionId ?? "none"
|
|
18878
|
+
});
|
|
18879
|
+
const segment = segmentText(ctx.t, {
|
|
18880
|
+
task: paths === void 0 ? void 0 : readState(ctx.runtime, paths).active,
|
|
18881
|
+
gate: paths === void 0 ? "none" : gateLight(ctx.runtime, paths),
|
|
18882
|
+
contextPercent: input.contextPercent
|
|
18883
|
+
});
|
|
18884
|
+
return composeLine(wrapped, segment, columnsOf(ctx));
|
|
18885
|
+
};
|
|
18886
|
+
var renderStatusLine = async (ctx) => {
|
|
18887
|
+
try {
|
|
18888
|
+
const raw = await withDeadline(readAll(ctx.io.stdin), STDIN_MS, ctx.runtime.startTimer).catch(() => "");
|
|
18889
|
+
return await lineFor(ctx, parseStatusInput(raw));
|
|
18890
|
+
} catch {
|
|
18891
|
+
return "fm";
|
|
18892
|
+
}
|
|
18893
|
+
};
|
|
18894
|
+
|
|
18895
|
+
// src/commands/statusline.ts
|
|
18896
|
+
var statuslineCommand = {
|
|
18897
|
+
path: ["statusline"],
|
|
18898
|
+
summaryKey: "command.statusline.summary",
|
|
18899
|
+
usage: "finitemonkey statusline",
|
|
18900
|
+
status: "ready",
|
|
18901
|
+
run: async (ctx) => success({}, [await renderStatusLine(ctx)])
|
|
18902
|
+
};
|
|
18903
|
+
|
|
18447
18904
|
// src/task/create.ts
|
|
18448
|
-
import { join as
|
|
18905
|
+
import { join as join48 } from "node:path";
|
|
18449
18906
|
|
|
18450
18907
|
// src/core/yaml/write.ts
|
|
18451
18908
|
var PLAIN_SAFE = /^[A-Za-z_./][A-Za-z0-9_./ ()-]*$/;
|
|
@@ -18534,10 +18991,10 @@ var explicitChecks = (draft) => [
|
|
|
18534
18991
|
var draftChecks = (runtime, paths, draft) => {
|
|
18535
18992
|
const disk = {
|
|
18536
18993
|
read: (relative11) => {
|
|
18537
|
-
const
|
|
18538
|
-
return
|
|
18994
|
+
const text8 = runtime.readText(toAbsolutePath(join48(paths.root, relative11)));
|
|
18995
|
+
return text8.ok ? text8.value : void 0;
|
|
18539
18996
|
},
|
|
18540
|
-
exists: (relative11) => runtime.stat(toAbsolutePath(
|
|
18997
|
+
exists: (relative11) => runtime.stat(toAbsolutePath(join48(paths.root, relative11))) !== void 0
|
|
18541
18998
|
};
|
|
18542
18999
|
const detected = detectChecks(disk);
|
|
18543
19000
|
const base = draft.runs.length > 0 || draft.tests !== void 0 ? explicitChecks(draft) : detected;
|
|
@@ -18552,7 +19009,7 @@ var draftChecks = (runtime, paths, draft) => {
|
|
|
18552
19009
|
{ id: "merge", kind: "merge" },
|
|
18553
19010
|
{ id: "callers", kind: "callers" },
|
|
18554
19011
|
...draft.paths.length > 0 || draft.budget !== void 0 ? [{ id: "diff", kind: "diff" }] : [],
|
|
18555
|
-
...runtime.stat(toAbsolutePath(
|
|
19012
|
+
...runtime.stat(toAbsolutePath(join48(paths.root, LOOK_CONFIG))) === void 0 ? [] : [{ id: "look", kind: "look" }],
|
|
18556
19013
|
...draft.git.length > 0 ? [{ id: "git", kind: "git", require: draft.git }] : [],
|
|
18557
19014
|
{ id: "pr", kind: "pr", requireOpen: draft.git.includes("pr_open") },
|
|
18558
19015
|
...draft.manual.map((description, index) => ({
|
|
@@ -18578,19 +19035,19 @@ var createTask = (runtime, paths, draft) => {
|
|
|
18578
19035
|
decisionsNeeded: [],
|
|
18579
19036
|
implicit: false
|
|
18580
19037
|
};
|
|
18581
|
-
const
|
|
19038
|
+
const text8 = stringifyYaml(contractYaml(shape));
|
|
18582
19039
|
const valid = validateContract(contractYaml(shape));
|
|
18583
19040
|
if (!valid.ok) return reject({ kind: "invalid", issues: valid.error });
|
|
18584
|
-
const wrote = runtime.writeText(paths.task(draft.id),
|
|
19041
|
+
const wrote = runtime.writeText(paths.task(draft.id), text8);
|
|
18585
19042
|
if (!wrote.ok) return reject({ kind: "write", detail: wrote.error });
|
|
18586
19043
|
const lock = readTrust(runtime, paths);
|
|
18587
19044
|
writeTrust(runtime, paths, {
|
|
18588
19045
|
...lock,
|
|
18589
19046
|
contracts: { ...lock.contracts, [draft.id]: contractHash(valid.value) },
|
|
18590
|
-
approved: { ...lock.approved, [draft.id]:
|
|
19047
|
+
approved: { ...lock.approved, [draft.id]: text8 }
|
|
18591
19048
|
});
|
|
18592
19049
|
writeState(runtime, paths, { ...readState(runtime, paths), active: draft.id });
|
|
18593
|
-
return accept({ contract: valid.value, text:
|
|
19050
|
+
return accept({ contract: valid.value, text: text8 });
|
|
18594
19051
|
};
|
|
18595
19052
|
|
|
18596
19053
|
// src/commands/task.ts
|
|
@@ -18741,7 +19198,7 @@ var taskDoneCommand = {
|
|
|
18741
19198
|
};
|
|
18742
19199
|
|
|
18743
19200
|
// src/install/githook-block.ts
|
|
18744
|
-
var withoutHookBlock = (
|
|
19201
|
+
var withoutHookBlock = (text8) => withoutManagedBlock(text8, { begin: BLOCK_BEGIN, end: BLOCK_END });
|
|
18745
19202
|
|
|
18746
19203
|
// src/install/uninstall.ts
|
|
18747
19204
|
var blockless = (kind, current2) => {
|
|
@@ -18750,8 +19207,8 @@ var blockless = (kind, current2) => {
|
|
|
18750
19207
|
};
|
|
18751
19208
|
var structural = (entry2, current2) => {
|
|
18752
19209
|
if (entry2.kind === "gitignore" || entry2.kind === "markdown" || entry2.kind === "githook") {
|
|
18753
|
-
const
|
|
18754
|
-
return
|
|
19210
|
+
const text8 = blockless(entry2.kind, current2);
|
|
19211
|
+
return text8.trim() === "" && !entry2.existed ? void 0 : text8;
|
|
18755
19212
|
}
|
|
18756
19213
|
const parsed = parseSettings(current2);
|
|
18757
19214
|
if (!parsed.ok || entry2.added === void 0) return current2;
|
|
@@ -18832,7 +19289,7 @@ var dataOf3 = (root, plan, applied) => ({
|
|
|
18832
19289
|
kept: plan.items.filter((item2) => item2.action.startsWith("keep")).map(({ path }) => path),
|
|
18833
19290
|
outside: OUTSIDE.map(({ command }) => command)
|
|
18834
19291
|
});
|
|
18835
|
-
var
|
|
19292
|
+
var linesOf3 = (ctx, data) => [
|
|
18836
19293
|
ctx.t("install.uninstall.title", { root: data.root }),
|
|
18837
19294
|
...data.actions.map(({ path, action }) => ` ${action.padEnd(15)}${path}`),
|
|
18838
19295
|
...data.purge ? [ctx.t("install.uninstall.purge")] : [ctx.t("install.uninstall.keeps_state")],
|
|
@@ -18853,16 +19310,16 @@ var runUninstall = (ctx, args2) => {
|
|
|
18853
19310
|
const options = { purge: args2.values["purge"] === true, keepGenerated: args2.values["keep-generated"] === true };
|
|
18854
19311
|
const plan = planRemoval(project, lock.value, options);
|
|
18855
19312
|
const preview = dataOf3(project.root, plan, false);
|
|
18856
|
-
if (ctx.dryRun) return success(preview, [...
|
|
19313
|
+
if (ctx.dryRun) return success(preview, [...linesOf3(ctx, preview), ctx.t("install.dry_run")]);
|
|
18857
19314
|
if (!ctx.yes) {
|
|
18858
19315
|
const next2 = fixFor(args2);
|
|
18859
19316
|
const problem4 = confirmProblem("uninstall", ctx.t("install.uninstall.question"), next2);
|
|
18860
|
-
return { ...failureFrom(ctx.t, { ...problem4, data: preview },
|
|
19317
|
+
return { ...failureFrom(ctx.t, { ...problem4, data: preview }, linesOf3(ctx, preview)), next: next2 };
|
|
18861
19318
|
}
|
|
18862
19319
|
const removed = applyRemoval(project, plan);
|
|
18863
19320
|
if (!removed.ok) return failureFrom(ctx.t, applyProblem(removed.error));
|
|
18864
19321
|
const data = dataOf3(project.root, plan, true);
|
|
18865
|
-
return success(data, [...
|
|
19322
|
+
return success(data, [...linesOf3(ctx, data), ctx.t("install.uninstall.done")]);
|
|
18866
19323
|
};
|
|
18867
19324
|
var uninstallCommand = {
|
|
18868
19325
|
path: ["uninstall"],
|
|
@@ -18942,6 +19399,7 @@ var REGISTRY = [
|
|
|
18942
19399
|
policyFalsePositiveCommand,
|
|
18943
19400
|
offCommand,
|
|
18944
19401
|
onCommand,
|
|
19402
|
+
gainCommand,
|
|
18945
19403
|
secretsIncidentCommand,
|
|
18946
19404
|
secretsLiveCommand,
|
|
18947
19405
|
proseCheckCommand,
|
|
@@ -18955,34 +19413,35 @@ var REGISTRY = [
|
|
|
18955
19413
|
ciGateCommand,
|
|
18956
19414
|
lookCommand,
|
|
18957
19415
|
reviewPacketCommand,
|
|
19416
|
+
statuslineCommand,
|
|
18958
19417
|
versionCommand,
|
|
18959
19418
|
helpCommand
|
|
18960
19419
|
];
|
|
18961
19420
|
|
|
18962
19421
|
// src/i18n/lang-settings.ts
|
|
18963
|
-
import { join as
|
|
19422
|
+
import { join as join49 } from "node:path";
|
|
18964
19423
|
var read3 = (runtime, path) => {
|
|
18965
|
-
const
|
|
18966
|
-
return
|
|
19424
|
+
const text8 = runtime.readText(toAbsolutePath(path));
|
|
19425
|
+
return text8.ok ? text8.value : void 0;
|
|
18967
19426
|
};
|
|
18968
19427
|
var configText = (tree, ...path) => {
|
|
18969
19428
|
const value = configAt(tree, ...path);
|
|
18970
19429
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
18971
19430
|
};
|
|
18972
|
-
var languageIn = (
|
|
18973
|
-
if (
|
|
18974
|
-
const parsed = parseJsonObject(
|
|
19431
|
+
var languageIn = (text8) => {
|
|
19432
|
+
if (text8 === void 0) return void 0;
|
|
19433
|
+
const parsed = parseJsonObject(text8);
|
|
18975
19434
|
const value = parsed.ok && isRecord(parsed.value) ? parsed.value["language"] : void 0;
|
|
18976
19435
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
18977
19436
|
};
|
|
18978
|
-
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ??
|
|
19437
|
+
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join49(runtime.homeDir(), ".claude");
|
|
18979
19438
|
var langSettings = (runtime, cwd, env, platform) => {
|
|
18980
19439
|
const root = findGitRoot(cwd, runtime) ?? cwd;
|
|
18981
|
-
const config = parseConfigYaml(read3(runtime,
|
|
19440
|
+
const config = parseConfigYaml(read3(runtime, join49(root, CONFIG_FILE)) ?? "");
|
|
18982
19441
|
const claudeFiles = [
|
|
18983
|
-
|
|
18984
|
-
|
|
18985
|
-
|
|
19442
|
+
join49(root, ".claude", "settings.local.json"),
|
|
19443
|
+
join49(root, ".claude", "settings.json"),
|
|
19444
|
+
join49(claudeDir(runtime, env), "settings.json")
|
|
18986
19445
|
];
|
|
18987
19446
|
return {
|
|
18988
19447
|
config: configText(config, "lang"),
|
|
@@ -19069,8 +19528,8 @@ var resultFromThrown = (thrown, t) => {
|
|
|
19069
19528
|
};
|
|
19070
19529
|
var block3 = (items) => items.length > 0 ? `${items.join("\n")}
|
|
19071
19530
|
` : "";
|
|
19072
|
-
var write2 = (sink,
|
|
19073
|
-
if (
|
|
19531
|
+
var write2 = (sink, text8) => {
|
|
19532
|
+
if (text8 !== "") sink.write(maskText(text8));
|
|
19074
19533
|
};
|
|
19075
19534
|
var render2 = (ctx, command, result) => {
|
|
19076
19535
|
if (result.raw !== void 0) {
|
|
@@ -19139,9 +19598,9 @@ import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
|
19139
19598
|
var UNKNOWN_VERSION = "0.0.0-unknown";
|
|
19140
19599
|
var VERSION_SOURCES = ["../package.json", "../.claude-plugin/plugin.json"];
|
|
19141
19600
|
var versionAt = (readText, moduleUrl, relative11) => {
|
|
19142
|
-
const
|
|
19143
|
-
if (!
|
|
19144
|
-
const parsed = parseJson(
|
|
19601
|
+
const text8 = readText(toAbsolutePath(fileURLToPath4(new URL(relative11, moduleUrl))));
|
|
19602
|
+
if (!text8.ok) return void 0;
|
|
19603
|
+
const parsed = parseJson(text8.value);
|
|
19145
19604
|
if (!parsed.ok || !isRecord(parsed.value)) return void 0;
|
|
19146
19605
|
const version = parsed.value["version"];
|
|
19147
19606
|
return typeof version === "string" ? version : void 0;
|