finitemonkey 0.1.0-next.22 → 0.1.0-next.24
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/README.es.md +1 -0
- package/README.md +1 -0
- package/README.pt-BR.md +1 -0
- package/dist/finitemonkey.mjs +1110 -635
- 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,36 +943,122 @@ 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)}
|
|
871
951
|
`;
|
|
872
952
|
|
|
953
|
+
// src/git/project-config.ts
|
|
954
|
+
import { join as join2 } from "node:path";
|
|
955
|
+
var CONFIG_FILE2 = ".finitemonkey/config.yml";
|
|
956
|
+
var unquote = (value) => value.trim().replace(/^(["'])(.*)\1$/s, "$2");
|
|
957
|
+
var stripComment = (line2) => {
|
|
958
|
+
let quote;
|
|
959
|
+
for (let index = 0; index < line2.length; index += 1) {
|
|
960
|
+
const char = line2.charAt(index);
|
|
961
|
+
if (quote === void 0 && (char === '"' || char === "'")) quote = char;
|
|
962
|
+
else if (char === quote) quote = void 0;
|
|
963
|
+
else if (quote === void 0 && char === "#" && (index === 0 || /\s/.test(line2.charAt(index - 1)))) {
|
|
964
|
+
return line2.slice(0, index);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
return line2;
|
|
968
|
+
};
|
|
969
|
+
var flowList = (value) => {
|
|
970
|
+
const trimmed = value.trim();
|
|
971
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
|
|
972
|
+
return trimmed.slice(1, -1).split(",").map(unquote).filter((item2) => item2 !== "");
|
|
973
|
+
};
|
|
974
|
+
var keyValue = (body2) => {
|
|
975
|
+
const match = /^("[^"]*"|'[^']*'|[^:]+?)\s*:(?:\s+(.*))?$/.exec(body2);
|
|
976
|
+
if (match === null) return void 0;
|
|
977
|
+
return { key: unquote(match[1] ?? ""), value: match[2] ?? "" };
|
|
978
|
+
};
|
|
979
|
+
var addItem = (frame, item2) => {
|
|
980
|
+
if (frame.key === void 0) return;
|
|
981
|
+
const parent = frame.tree[frame.key];
|
|
982
|
+
if (Array.isArray(parent)) parent.push(item2);
|
|
983
|
+
else frame.tree[frame.key] = [item2];
|
|
984
|
+
};
|
|
985
|
+
var placeLine = (stack, indent, body2) => {
|
|
986
|
+
while (stack.length > 1 && (stack[stack.length - 1]?.indent ?? 0) >= indent && !body2.startsWith("- ")) stack.pop();
|
|
987
|
+
const top = stack[stack.length - 1];
|
|
988
|
+
if (top === void 0) return;
|
|
989
|
+
if (body2.startsWith("- ")) {
|
|
990
|
+
addItem(top, unquote(body2.slice(2)));
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
const pair = keyValue(body2);
|
|
994
|
+
if (pair === void 0) return;
|
|
995
|
+
const parentTree = top.key === void 0 ? top.tree : ensureTree(top.tree, top.key);
|
|
996
|
+
if (pair.value === "") {
|
|
997
|
+
stack.push({ indent, tree: parentTree, key: pair.key });
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
parentTree[pair.key] = flowList(pair.value) ?? unquote(pair.value);
|
|
1001
|
+
};
|
|
1002
|
+
var ensureTree = (tree, key) => {
|
|
1003
|
+
const existing = tree[key];
|
|
1004
|
+
if (existing !== void 0 && typeof existing !== "string" && !Array.isArray(existing)) return existing;
|
|
1005
|
+
const fresh3 = {};
|
|
1006
|
+
tree[key] = fresh3;
|
|
1007
|
+
return fresh3;
|
|
1008
|
+
};
|
|
1009
|
+
var parseConfigYaml = (text8) => {
|
|
1010
|
+
const root = {};
|
|
1011
|
+
const stack = [{ indent: -1, tree: root, key: void 0 }];
|
|
1012
|
+
for (const raw of text8.split(/\r?\n/)) {
|
|
1013
|
+
const line2 = stripComment(raw).trimEnd();
|
|
1014
|
+
if (line2.trim() === "" || line2.trim() === "---") continue;
|
|
1015
|
+
const indent = line2.length - line2.trimStart().length;
|
|
1016
|
+
placeLine(stack, indent, line2.trim());
|
|
1017
|
+
}
|
|
1018
|
+
return root;
|
|
1019
|
+
};
|
|
1020
|
+
var isTree = (node) => typeof node === "object" && !Array.isArray(node);
|
|
1021
|
+
var configAt = (tree, ...path) => path.reduce((node, key) => isTree(node) ? node[key] : void 0, tree);
|
|
1022
|
+
var configList = (tree, ...path) => {
|
|
1023
|
+
const node = configAt(tree, ...path);
|
|
1024
|
+
if (typeof node === "string") return node === "" ? [] : [node];
|
|
1025
|
+
return Array.isArray(node) ? node.filter((item2) => typeof item2 === "string") : [];
|
|
1026
|
+
};
|
|
1027
|
+
var configMap = (tree, ...path) => {
|
|
1028
|
+
const node = configAt(tree, ...path);
|
|
1029
|
+
if (!isTree(node)) return {};
|
|
1030
|
+
return Object.fromEntries(
|
|
1031
|
+
Object.entries(node).filter((entry2) => typeof entry2[1] === "string")
|
|
1032
|
+
);
|
|
1033
|
+
};
|
|
1034
|
+
var loadProjectConfig = (runtime, root) => {
|
|
1035
|
+
const text8 = runtime.readText(toAbsolutePath(join2(root, CONFIG_FILE2)));
|
|
1036
|
+
return text8.ok ? parseConfigYaml(text8.value) : {};
|
|
1037
|
+
};
|
|
1038
|
+
|
|
873
1039
|
// src/install/managed-block.ts
|
|
874
|
-
var lineBreak = (
|
|
1040
|
+
var lineBreak = (text8) => text8.includes("\r\n") ? "\r\n" : "\n";
|
|
875
1041
|
var blockRange = (lines3, markers) => {
|
|
876
1042
|
const start = lines3.findIndex((line2) => line2.trimEnd() === markers.begin);
|
|
877
1043
|
if (start < 0) return void 0;
|
|
878
1044
|
const end = lines3.findIndex((line2, index) => index > start && line2.trimEnd() === markers.end);
|
|
879
1045
|
return end < 0 ? void 0 : { start, end };
|
|
880
1046
|
};
|
|
881
|
-
var withManagedBlock = (
|
|
882
|
-
const eol =
|
|
1047
|
+
var withManagedBlock = (text8, markers, body2) => {
|
|
1048
|
+
const eol = text8 === void 0 ? "\n" : lineBreak(text8);
|
|
883
1049
|
const block4 = [markers.begin, ...body2, markers.end];
|
|
884
|
-
if (
|
|
885
|
-
const lines3 =
|
|
1050
|
+
if (text8 === void 0 || text8 === "") return `${block4.join(eol)}${eol}`;
|
|
1051
|
+
const lines3 = text8.split(eol);
|
|
886
1052
|
const range = blockRange(lines3, markers);
|
|
887
1053
|
if (range !== void 0) return [...lines3.slice(0, range.start), ...block4, ...lines3.slice(range.end + 1)].join(eol);
|
|
888
|
-
const separator =
|
|
889
|
-
return `${
|
|
1054
|
+
const separator = text8.endsWith(eol) ? "" : eol;
|
|
1055
|
+
return `${text8}${separator}${block4.join(eol)}${eol}`;
|
|
890
1056
|
};
|
|
891
|
-
var withoutManagedBlock = (
|
|
892
|
-
const eol = lineBreak(
|
|
893
|
-
const lines3 =
|
|
1057
|
+
var withoutManagedBlock = (text8, markers) => {
|
|
1058
|
+
const eol = lineBreak(text8);
|
|
1059
|
+
const lines3 = text8.split(eol);
|
|
894
1060
|
const range = blockRange(lines3, markers);
|
|
895
|
-
if (range === void 0) return
|
|
1061
|
+
if (range === void 0) return text8;
|
|
896
1062
|
return [...lines3.slice(0, range.start), ...lines3.slice(range.end + 1)].join(eol);
|
|
897
1063
|
};
|
|
898
1064
|
|
|
@@ -989,48 +1155,7 @@ var configTemplate = (preset, lang) => [
|
|
|
989
1155
|
`lang: ${lang}`,
|
|
990
1156
|
""
|
|
991
1157
|
].join("\n");
|
|
992
|
-
var hasMarker = (
|
|
993
|
-
|
|
994
|
-
// src/install/hook-entries.ts
|
|
995
|
-
var INSTALLED_HOOKS = [
|
|
996
|
-
{ event: "SessionStart", timeout: 30, guard: false },
|
|
997
|
-
{ event: "UserPromptSubmit", timeout: 10, guard: true },
|
|
998
|
-
{ event: "PreToolUse", timeout: 10, guard: true },
|
|
999
|
-
{ event: "PostToolUse", timeout: 30, guard: false },
|
|
1000
|
-
{ event: "Stop", timeout: 600, guard: false },
|
|
1001
|
-
{ event: "SubagentStart", timeout: 30, guard: false },
|
|
1002
|
-
{ event: "SubagentStop", timeout: 600, guard: false },
|
|
1003
|
-
{ event: "PreCompact", timeout: 30, guard: false },
|
|
1004
|
-
{ event: "ConfigChange", timeout: 10, guard: true }
|
|
1005
|
-
];
|
|
1006
|
-
var PROJECT_BIN = `\${CLAUDE_PROJECT_DIR}/${LOCAL_BIN}`;
|
|
1007
|
-
var VENDORED_BIN = `\${CLAUDE_PROJECT_DIR}/${VENDOR_BIN}`;
|
|
1008
|
-
var isScript = (path) => /\.(c|m)?js$/i.test(path);
|
|
1009
|
-
var execForm = (runner, event) => {
|
|
1010
|
-
if (runner.kind === "local") return { command: "node", args: [PROJECT_BIN, "hook", event] };
|
|
1011
|
-
if (runner.kind === "vendored") return { command: "node", args: [VENDORED_BIN, "hook", event] };
|
|
1012
|
-
if (isScript(runner.path)) return { command: "node", args: [runner.path, "hook", event] };
|
|
1013
|
-
return { command: runner.path, args: ["hook", event] };
|
|
1014
|
-
};
|
|
1015
|
-
var hookHandler = (runner, spec2, failClosedSupported) => {
|
|
1016
|
-
const failure4 = spec2.guard && failClosedSupported ? { onFailure: "block" } : {};
|
|
1017
|
-
const { command, args: args2 } = execForm(runner, spec2.event);
|
|
1018
|
-
return { type: "command", command, args: args2, timeout: spec2.timeout, ...failure4 };
|
|
1019
|
-
};
|
|
1020
|
-
var OWN_SHELL = /(^|[\s/"'])finitemonkey(@[^\s"]+|\.mjs|\.cmd|\.exe)?"?\s+hook\s/;
|
|
1021
|
-
var OWN_SCRIPT = /finitemonkey(\.mjs|\.cmd|\.exe)?$/;
|
|
1022
|
-
var stringsOf = (value) => Array.isArray(value) ? value.filter((item2) => typeof item2 === "string") : [];
|
|
1023
|
-
var isOwnHandler = (handler) => {
|
|
1024
|
-
if (!isRecord(handler)) return false;
|
|
1025
|
-
const command = handler["command"];
|
|
1026
|
-
if (typeof command !== "string") return false;
|
|
1027
|
-
const args2 = stringsOf(handler["args"]);
|
|
1028
|
-
if (args2.length === 0) return OWN_SHELL.test(`${command} `);
|
|
1029
|
-
const hookAt = args2.indexOf("hook");
|
|
1030
|
-
if (hookAt < 0) return false;
|
|
1031
|
-
const target2 = hookAt === 0 ? command : args2[hookAt - 1] ?? "";
|
|
1032
|
-
return OWN_SCRIPT.test(target2);
|
|
1033
|
-
};
|
|
1158
|
+
var hasMarker = (text8) => text8.includes(GENERATED_MARKER);
|
|
1034
1159
|
|
|
1035
1160
|
// src/install/skill-template.ts
|
|
1036
1161
|
var cliCommand = (runner, args2) => {
|
|
@@ -1095,7 +1220,7 @@ var lefthookBody = (runner) => [
|
|
|
1095
1220
|
` run: ${cliCommand(runner, "push check {1}")}`,
|
|
1096
1221
|
" use_stdin: true"
|
|
1097
1222
|
];
|
|
1098
|
-
var definesHook = (
|
|
1223
|
+
var definesHook = (text8) => GIT_HOOK_NAMES.some((hook) => new RegExp(`^${hook}:`, "m").test(withoutManagedBlock(text8, HOOK_MARKERS)));
|
|
1099
1224
|
var lefthookItem = (project, runner, path) => {
|
|
1100
1225
|
const before = project.read(path);
|
|
1101
1226
|
const after = withManagedBlock(before, HOOK_MARKERS, lefthookBody(runner));
|
|
@@ -1120,8 +1245,8 @@ var preCommitBody = (runner, indent) => {
|
|
|
1120
1245
|
];
|
|
1121
1246
|
};
|
|
1122
1247
|
var TOP_KEY = /^[A-Za-z_][\w-]*:/;
|
|
1123
|
-
var reposIsLast = (
|
|
1124
|
-
const keys = withoutManagedBlock(
|
|
1248
|
+
var reposIsLast = (text8) => {
|
|
1249
|
+
const keys = withoutManagedBlock(text8, HOOK_MARKERS).split(/\r?\n/).filter((line2) => TOP_KEY.test(line2));
|
|
1125
1250
|
return keys.at(-1)?.startsWith("repos:") === true;
|
|
1126
1251
|
};
|
|
1127
1252
|
var preCommitItem = (project, runner) => {
|
|
@@ -1145,94 +1270,6 @@ var gitHookItems = (project, runner) => {
|
|
|
1145
1270
|
|
|
1146
1271
|
// src/policy/policy.ts
|
|
1147
1272
|
import { join as join3 } from "node:path";
|
|
1148
|
-
|
|
1149
|
-
// src/git/project-config.ts
|
|
1150
|
-
import { join as join2 } from "node:path";
|
|
1151
|
-
var CONFIG_FILE2 = ".finitemonkey/config.yml";
|
|
1152
|
-
var unquote = (value) => value.trim().replace(/^(["'])(.*)\1$/s, "$2");
|
|
1153
|
-
var stripComment = (line2) => {
|
|
1154
|
-
let quote;
|
|
1155
|
-
for (let index = 0; index < line2.length; index += 1) {
|
|
1156
|
-
const char = line2.charAt(index);
|
|
1157
|
-
if (quote === void 0 && (char === '"' || char === "'")) quote = char;
|
|
1158
|
-
else if (char === quote) quote = void 0;
|
|
1159
|
-
else if (quote === void 0 && char === "#" && (index === 0 || /\s/.test(line2.charAt(index - 1)))) {
|
|
1160
|
-
return line2.slice(0, index);
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
return line2;
|
|
1164
|
-
};
|
|
1165
|
-
var flowList = (value) => {
|
|
1166
|
-
const trimmed = value.trim();
|
|
1167
|
-
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
|
|
1168
|
-
return trimmed.slice(1, -1).split(",").map(unquote).filter((item2) => item2 !== "");
|
|
1169
|
-
};
|
|
1170
|
-
var keyValue = (body2) => {
|
|
1171
|
-
const match = /^("[^"]*"|'[^']*'|[^:]+?)\s*:(?:\s+(.*))?$/.exec(body2);
|
|
1172
|
-
if (match === null) return void 0;
|
|
1173
|
-
return { key: unquote(match[1] ?? ""), value: match[2] ?? "" };
|
|
1174
|
-
};
|
|
1175
|
-
var addItem = (frame, item2) => {
|
|
1176
|
-
if (frame.key === void 0) return;
|
|
1177
|
-
const parent = frame.tree[frame.key];
|
|
1178
|
-
if (Array.isArray(parent)) parent.push(item2);
|
|
1179
|
-
else frame.tree[frame.key] = [item2];
|
|
1180
|
-
};
|
|
1181
|
-
var placeLine = (stack, indent, body2) => {
|
|
1182
|
-
while (stack.length > 1 && (stack[stack.length - 1]?.indent ?? 0) >= indent && !body2.startsWith("- ")) stack.pop();
|
|
1183
|
-
const top = stack[stack.length - 1];
|
|
1184
|
-
if (top === void 0) return;
|
|
1185
|
-
if (body2.startsWith("- ")) {
|
|
1186
|
-
addItem(top, unquote(body2.slice(2)));
|
|
1187
|
-
return;
|
|
1188
|
-
}
|
|
1189
|
-
const pair = keyValue(body2);
|
|
1190
|
-
if (pair === void 0) return;
|
|
1191
|
-
const parentTree = top.key === void 0 ? top.tree : ensureTree(top.tree, top.key);
|
|
1192
|
-
if (pair.value === "") {
|
|
1193
|
-
stack.push({ indent, tree: parentTree, key: pair.key });
|
|
1194
|
-
return;
|
|
1195
|
-
}
|
|
1196
|
-
parentTree[pair.key] = flowList(pair.value) ?? unquote(pair.value);
|
|
1197
|
-
};
|
|
1198
|
-
var ensureTree = (tree, key) => {
|
|
1199
|
-
const existing = tree[key];
|
|
1200
|
-
if (existing !== void 0 && typeof existing !== "string" && !Array.isArray(existing)) return existing;
|
|
1201
|
-
const fresh3 = {};
|
|
1202
|
-
tree[key] = fresh3;
|
|
1203
|
-
return fresh3;
|
|
1204
|
-
};
|
|
1205
|
-
var parseConfigYaml = (text7) => {
|
|
1206
|
-
const root = {};
|
|
1207
|
-
const stack = [{ indent: -1, tree: root, key: void 0 }];
|
|
1208
|
-
for (const raw of text7.split(/\r?\n/)) {
|
|
1209
|
-
const line2 = stripComment(raw).trimEnd();
|
|
1210
|
-
if (line2.trim() === "" || line2.trim() === "---") continue;
|
|
1211
|
-
const indent = line2.length - line2.trimStart().length;
|
|
1212
|
-
placeLine(stack, indent, line2.trim());
|
|
1213
|
-
}
|
|
1214
|
-
return root;
|
|
1215
|
-
};
|
|
1216
|
-
var isTree = (node) => typeof node === "object" && !Array.isArray(node);
|
|
1217
|
-
var configAt = (tree, ...path) => path.reduce((node, key) => isTree(node) ? node[key] : void 0, tree);
|
|
1218
|
-
var configList = (tree, ...path) => {
|
|
1219
|
-
const node = configAt(tree, ...path);
|
|
1220
|
-
if (typeof node === "string") return node === "" ? [] : [node];
|
|
1221
|
-
return Array.isArray(node) ? node.filter((item2) => typeof item2 === "string") : [];
|
|
1222
|
-
};
|
|
1223
|
-
var configMap = (tree, ...path) => {
|
|
1224
|
-
const node = configAt(tree, ...path);
|
|
1225
|
-
if (!isTree(node)) return {};
|
|
1226
|
-
return Object.fromEntries(
|
|
1227
|
-
Object.entries(node).filter((entry2) => typeof entry2[1] === "string")
|
|
1228
|
-
);
|
|
1229
|
-
};
|
|
1230
|
-
var loadProjectConfig = (runtime, root) => {
|
|
1231
|
-
const text7 = runtime.readText(toAbsolutePath(join2(root, CONFIG_FILE2)));
|
|
1232
|
-
return text7.ok ? parseConfigYaml(text7.value) : {};
|
|
1233
|
-
};
|
|
1234
|
-
|
|
1235
|
-
// src/policy/policy.ts
|
|
1236
1273
|
var POLICY_FILE = ".finitemonkey/policy.yml";
|
|
1237
1274
|
var GUARD_MODES = ["off", "shadow", "active"];
|
|
1238
1275
|
var LEVELS = ["light", "standard", "strict"];
|
|
@@ -1247,8 +1284,8 @@ var DEFAULT_POLICY = {
|
|
|
1247
1284
|
guards: {}
|
|
1248
1285
|
};
|
|
1249
1286
|
var pick = (options, value, fallback) => options.find((option) => option === value) ?? fallback;
|
|
1250
|
-
var parsePolicy = (
|
|
1251
|
-
const tree = parseConfigYaml(
|
|
1287
|
+
var parsePolicy = (text8) => {
|
|
1288
|
+
const tree = parseConfigYaml(text8);
|
|
1252
1289
|
const raw = configAt(tree, "promoteAfter");
|
|
1253
1290
|
const promoteAfter = typeof raw === "string" ? Number.parseInt(raw, 10) : Number.NaN;
|
|
1254
1291
|
const guards = Object.fromEntries(
|
|
@@ -1267,23 +1304,82 @@ var parsePolicy = (text7) => {
|
|
|
1267
1304
|
};
|
|
1268
1305
|
var policyPath = (root) => toAbsolutePath(join3(root, POLICY_FILE));
|
|
1269
1306
|
var loadPolicy = (runtime, root) => {
|
|
1270
|
-
const
|
|
1271
|
-
return
|
|
1272
|
-
};
|
|
1307
|
+
const text8 = runtime.readText(policyPath(root));
|
|
1308
|
+
return text8.ok ? parsePolicy(text8.value) : DEFAULT_POLICY;
|
|
1309
|
+
};
|
|
1310
|
+
|
|
1311
|
+
// src/review/agents.ts
|
|
1312
|
+
var REVIEW_AGENTS = ["reviewer", "verifier"];
|
|
1313
|
+
var FORBIDDEN_TOOLS = ["Edit", "Write", "MultiEdit", "NotebookEdit", "Agent", "Task"];
|
|
1314
|
+
var reviewAgentOf = (agentType) => REVIEW_AGENTS.find((name) => agentType === name || agentType.endsWith(`:${name}`));
|
|
1315
|
+
var REVIEWER_BODY = [
|
|
1316
|
+
"# Reviewer",
|
|
1317
|
+
"",
|
|
1318
|
+
"You review a change you did not write. Your input is the review packet in your prompt: the diff, the task contract, the evidence ledger and, when present, the code owners' REVIEW.md. You never see the conversation that produced the change, and you must not ask for it.",
|
|
1319
|
+
"",
|
|
1320
|
+
"1. Read the diff first, then open the files it touches when you need context.",
|
|
1321
|
+
"2. Judge the change against the contract: does each acceptance check have evidence for the current tree?",
|
|
1322
|
+
"3. Write numbered findings in three groups: must fix, should fix, nice to have. For each: what it is, the problem, the fix, and what happens if it is not fixed.",
|
|
1323
|
+
"4. Every must-fix finding carries a reproduction command and the expected result. A finding without one goes under should fix.",
|
|
1324
|
+
"5. End with a verdict: approve, approve with changes, or request changes.",
|
|
1325
|
+
"",
|
|
1326
|
+
"You cannot edit files or start other agents. Report; do not repair."
|
|
1327
|
+
].join("\n");
|
|
1328
|
+
var VERIFIER_BODY = [
|
|
1329
|
+
"# Verifier",
|
|
1330
|
+
"",
|
|
1331
|
+
"You check claims with commands, not with opinions. Your input is the review packet and, when there is one, the reviewer's must-fix findings with their reproduction commands. You never see the conversation that produced the change.",
|
|
1332
|
+
"",
|
|
1333
|
+
"1. Run each reproduction command in a throwaway worktree on the base commit and on the head commit (`git worktree add <tmp> <rev>`), never in the person's working tree.",
|
|
1334
|
+
"2. Record both exit codes. Fails only on head: the finding stands. Fails on both: it was already there. Fails on neither: not reproduced.",
|
|
1335
|
+
"3. Report one line per finding with the command and both results, then remove the worktrees.",
|
|
1336
|
+
"",
|
|
1337
|
+
"You cannot edit files or start other agents. Report; do not repair."
|
|
1338
|
+
].join("\n");
|
|
1339
|
+
var VERIFIER = {
|
|
1340
|
+
name: "verifier",
|
|
1341
|
+
description: "Runs reproduction commands from a review on the base and head commits in throwaway worktrees and reports which findings are real. Read-only.",
|
|
1342
|
+
tools: ["Read", "Grep", "Glob", "Bash"],
|
|
1343
|
+
model: "sonnet",
|
|
1344
|
+
maxTurns: 60,
|
|
1345
|
+
body: VERIFIER_BODY
|
|
1346
|
+
};
|
|
1347
|
+
var PLAYWRIGHT_SERVER = "playwright";
|
|
1348
|
+
var withPlaywright = (spec2) => ({
|
|
1349
|
+
...spec2,
|
|
1350
|
+
tools: [...spec2.tools, `mcp__${PLAYWRIGHT_SERVER}`],
|
|
1351
|
+
mcpServers: [PLAYWRIGHT_SERVER]
|
|
1352
|
+
});
|
|
1353
|
+
var agentMarkdown = (spec2, marker) => [
|
|
1354
|
+
"---",
|
|
1355
|
+
`name: ${spec2.name}`,
|
|
1356
|
+
`description: ${spec2.description}`,
|
|
1357
|
+
`tools: ${spec2.tools.join(", ")}`,
|
|
1358
|
+
`disallowedTools: ${FORBIDDEN_TOOLS.join(", ")}`,
|
|
1359
|
+
`model: ${spec2.model}`,
|
|
1360
|
+
"effort: high",
|
|
1361
|
+
`maxTurns: ${String(spec2.maxTurns)}`,
|
|
1362
|
+
...spec2.mcpServers === void 0 ? [] : ["mcpServers:", ...spec2.mcpServers.map((name) => ` - ${name}`)],
|
|
1363
|
+
"---",
|
|
1364
|
+
"",
|
|
1365
|
+
...marker === void 0 ? [] : [`<!-- ${marker} -->`, ""],
|
|
1366
|
+
spec2.body,
|
|
1367
|
+
""
|
|
1368
|
+
].join("\n");
|
|
1273
1369
|
|
|
1274
1370
|
// src/install/agents-block.ts
|
|
1275
1371
|
var MARKDOWN_MARKERS = { begin: MD_BLOCK_BEGIN, end: MD_BLOCK_END };
|
|
1276
1372
|
var IMPORT_LINE = /^@AGENTS\.md[ \t]*$/m;
|
|
1277
|
-
var withAgentsBlock = (
|
|
1278
|
-
|
|
1373
|
+
var withAgentsBlock = (text8, runner, lang) => withManagedBlock(
|
|
1374
|
+
text8,
|
|
1279
1375
|
MARKDOWN_MARKERS,
|
|
1280
1376
|
TEMPLATES[lang].agentsBlock({
|
|
1281
1377
|
gate: cliCommand(runner, "gate --fast --json"),
|
|
1282
1378
|
doctor: cliCommand(runner, "doctor --json")
|
|
1283
1379
|
})
|
|
1284
1380
|
);
|
|
1285
|
-
var withAgentsImport = (
|
|
1286
|
-
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);
|
|
1287
1383
|
|
|
1288
1384
|
// src/install/ask-floor.ts
|
|
1289
1385
|
var DEFAULT_BRANCHES = ["main", "master"];
|
|
@@ -1359,7 +1455,7 @@ var ruleFor = (raw) => {
|
|
|
1359
1455
|
};
|
|
1360
1456
|
var claudeignoreRules = (texts2) => [
|
|
1361
1457
|
...new Set(
|
|
1362
|
-
texts2.flatMap((
|
|
1458
|
+
texts2.flatMap((text8) => (text8 ?? "").split("\n")).map(ruleFor).filter((rule) => rule !== void 0)
|
|
1363
1459
|
)
|
|
1364
1460
|
];
|
|
1365
1461
|
|
|
@@ -1392,12 +1488,12 @@ import { join as join4 } from "node:path";
|
|
|
1392
1488
|
|
|
1393
1489
|
// src/install/json-text.ts
|
|
1394
1490
|
var DEFAULT_STYLE = { indent: " ", finalNewline: true };
|
|
1395
|
-
var styleOf = (
|
|
1396
|
-
const indented = /\n([ \t]+)\S/.exec(
|
|
1397
|
-
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") };
|
|
1398
1494
|
};
|
|
1399
1495
|
var renderJson = (value, style) => `${JSON.stringify(value, null, style.indent)}${style.finalNewline ? "\n" : ""}`;
|
|
1400
|
-
var parseSettings = (
|
|
1496
|
+
var parseSettings = (text8) => text8 === void 0 || text8.trim() === "" ? { ok: true, value: {} } : parseJsonObject(text8);
|
|
1401
1497
|
var asList = (value) => Array.isArray(value) ? value : [];
|
|
1402
1498
|
var asRecord = (value) => isRecord(value) ? value : {};
|
|
1403
1499
|
var setKey = (record3, key, value) => {
|
|
@@ -1434,8 +1530,8 @@ var WORKFLOW_DIRS = [
|
|
|
1434
1530
|
{ dir: "openspec", name: "openspec" }
|
|
1435
1531
|
];
|
|
1436
1532
|
var fromPackageJson = (project) => {
|
|
1437
|
-
const
|
|
1438
|
-
const parsed =
|
|
1533
|
+
const text8 = project.read("package.json");
|
|
1534
|
+
const parsed = text8 === void 0 ? void 0 : parseJsonObject(text8);
|
|
1439
1535
|
const field = parsed?.ok === true ? parsed.value["packageManager"] : void 0;
|
|
1440
1536
|
if (typeof field !== "string") return void 0;
|
|
1441
1537
|
return PACKAGE_MANAGERS.find(({ name }) => field.startsWith(`${name}@`))?.name;
|
|
@@ -1451,9 +1547,9 @@ var allHandlers = (settings) => {
|
|
|
1451
1547
|
const hooks = settings["hooks"];
|
|
1452
1548
|
return isRecord(hooks) ? Object.keys(hooks).flatMap((event) => handlersOf(settings, event)) : [];
|
|
1453
1549
|
};
|
|
1454
|
-
var settingsInfo = (
|
|
1455
|
-
if (
|
|
1456
|
-
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);
|
|
1457
1553
|
if (!parsed.ok) return { exists: true, valid: false, userHooks: 0, ownHooks: 0 };
|
|
1458
1554
|
const handlers = allHandlers(parsed.value);
|
|
1459
1555
|
const own = handlers.filter(isOwnHandler).length;
|
|
@@ -1467,8 +1563,8 @@ var pluginAuthorities = (settings, source) => {
|
|
|
1467
1563
|
return LOOP_PLUGINS.includes(name) ? [{ kind: "loop-plugin", source, name }] : [];
|
|
1468
1564
|
});
|
|
1469
1565
|
};
|
|
1470
|
-
var scopeAuthorities = (
|
|
1471
|
-
const parsed =
|
|
1566
|
+
var scopeAuthorities = (text8, source) => {
|
|
1567
|
+
const parsed = text8 === void 0 ? void 0 : parseJsonObject(text8);
|
|
1472
1568
|
if (parsed?.ok !== true) return [];
|
|
1473
1569
|
const stops = ["Stop", "SubagentStop"].flatMap(
|
|
1474
1570
|
(event) => handlersOf(parsed.value, event).filter((handler) => !isOwnHandler(handler)).map((handler) => {
|
|
@@ -1482,8 +1578,8 @@ var userSettingsText = (project, env) => {
|
|
|
1482
1578
|
const home = env["CLAUDE_CONFIG_DIR"] ?? env["HOME"] ?? env["USERPROFILE"];
|
|
1483
1579
|
if (home === void 0) return void 0;
|
|
1484
1580
|
const file = env["CLAUDE_CONFIG_DIR"] === void 0 ? join4(home, ".claude", "settings.json") : join4(home, "settings.json");
|
|
1485
|
-
const
|
|
1486
|
-
return
|
|
1581
|
+
const text8 = project.runtime.readText(toAbsolutePath(file));
|
|
1582
|
+
return text8.ok ? text8.value : void 0;
|
|
1487
1583
|
};
|
|
1488
1584
|
var detectAuthorities = (project, env) => [
|
|
1489
1585
|
...scopeAuthorities(project.read(SETTINGS_FILE), "project"),
|
|
@@ -1503,8 +1599,8 @@ var chooseRunner = (project, bin) => {
|
|
|
1503
1599
|
// src/install/gitignore-block.ts
|
|
1504
1600
|
var MARKERS = { begin: BLOCK_BEGIN, end: BLOCK_END };
|
|
1505
1601
|
var body = () => [`${STATE_DIR}/*`, ...COMMITTED_STATE.map((path) => `!${STATE_DIR}/${path}`)];
|
|
1506
|
-
var withBlock = (
|
|
1507
|
-
var withoutBlock = (
|
|
1602
|
+
var withBlock = (text8) => withManagedBlock(text8, MARKERS, body());
|
|
1603
|
+
var withoutBlock = (text8) => withoutManagedBlock(text8, MARKERS);
|
|
1508
1604
|
|
|
1509
1605
|
// src/core/semver.ts
|
|
1510
1606
|
var SEMVER = /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
@@ -1547,7 +1643,7 @@ var pendingMigrations = (migrations, from, to) => {
|
|
|
1547
1643
|
};
|
|
1548
1644
|
var migrateItem = (item2, previous, steps) => {
|
|
1549
1645
|
if (steps.length === 0 || item2.before === void 0) return item2;
|
|
1550
|
-
const migrated = steps.reduce((
|
|
1646
|
+
const migrated = steps.reduce((text8, step3) => step3(text8), item2.after);
|
|
1551
1647
|
if (migrated === item2.after) return item2;
|
|
1552
1648
|
const recorded = previous.entries.find((entry2) => entry2.path === item2.path)?.hash;
|
|
1553
1649
|
if (recorded !== hashText(item2.before)) {
|
|
@@ -1639,6 +1735,15 @@ var withPermissions = (settings, lists) => {
|
|
|
1639
1735
|
var missing = (wanted, existing) => wanted.filter((rule) => !existing.includes(rule));
|
|
1640
1736
|
var withoutAdded = (existing, added) => existing.filter((rule) => typeof rule !== "string" || !added.includes(rule));
|
|
1641
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
|
+
};
|
|
1642
1747
|
var mergeSettings = (current2, wanted, recorded) => {
|
|
1643
1748
|
const schema = Object.keys(current2).length === 0;
|
|
1644
1749
|
const base = schema ? { $schema: SETTINGS_SCHEMA_URL } : current2;
|
|
@@ -1662,7 +1767,18 @@ var mergeSettings = (current2, wanted, recorded) => {
|
|
|
1662
1767
|
if (marketplace) settings = withEntry(settings, "extraKnownMarketplaces", MARKETPLACE_KEY, MARKETPLACE_ENTRY);
|
|
1663
1768
|
if (plugin) settings = withEntry(settings, "enabledPlugins", PLUGIN_ID, true);
|
|
1664
1769
|
if (language !== void 0) settings = { ...settings, language };
|
|
1665
|
-
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
|
+
};
|
|
1666
1782
|
return { settings, added: language === void 0 ? added : { ...added, language } };
|
|
1667
1783
|
};
|
|
1668
1784
|
var sameAttribution = (value) => JSON.stringify(value) === JSON.stringify(ATTRIBUTION_OFF);
|
|
@@ -1682,15 +1798,20 @@ var unmergeSettings = (current2, added, recorded) => {
|
|
|
1682
1798
|
if (added.language !== void 0 && settings["language"] === added.language) {
|
|
1683
1799
|
settings = setKey(settings, "language", void 0);
|
|
1684
1800
|
}
|
|
1801
|
+
settings = restoreStatusLine(settings, added);
|
|
1685
1802
|
if (added.schema && Object.keys(settings).length === 1 && settings["$schema"] === SETTINGS_SCHEMA_URL) return {};
|
|
1686
1803
|
return settings;
|
|
1687
1804
|
};
|
|
1688
1805
|
var ownHandlersIn = (settings) => Object.values(asRecord(settings["hooks"])).flatMap((groups) => asList(groups)).flatMap((group) => asList(asRecord(group)["hooks"])).filter(isOwnHandler).length;
|
|
1689
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
|
+
|
|
1690
1811
|
// src/install/vendor.ts
|
|
1691
|
-
var isBundle = (
|
|
1692
|
-
var isOwnPackage = (
|
|
1693
|
-
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);
|
|
1694
1815
|
return parsed.ok && parsed.value["name"] === "finitemonkey";
|
|
1695
1816
|
};
|
|
1696
1817
|
var vendoredPackage = (version) => `${JSON.stringify({ name: "finitemonkey", version, private: true, type: "module" }, null, 2)}
|
|
@@ -1721,9 +1842,9 @@ var detect = (project, gitRoot2, input) => ({
|
|
|
1721
1842
|
location: locationOf(project.root, input.env)
|
|
1722
1843
|
});
|
|
1723
1844
|
var readPreviousLock = (project) => {
|
|
1724
|
-
const
|
|
1725
|
-
if (
|
|
1726
|
-
const parsed = parseLock(
|
|
1845
|
+
const text8 = project.read(LOCK_FILE);
|
|
1846
|
+
if (text8 === void 0) return accept(void 0);
|
|
1847
|
+
const parsed = parseLock(text8);
|
|
1727
1848
|
return parsed.ok ? accept(parsed.value) : reject({ kind: "lock", detail: parsed.error });
|
|
1728
1849
|
};
|
|
1729
1850
|
var actionFor = (before, after, existing) => {
|
|
@@ -1739,7 +1860,8 @@ var joinAdded = (previous, next2) => previous === void 0 ? next2 : {
|
|
|
1739
1860
|
blockReads: previous.blockReads || next2.blockReads,
|
|
1740
1861
|
marketplace: previous.marketplace || next2.marketplace,
|
|
1741
1862
|
plugin: previous.plugin || next2.plugin,
|
|
1742
|
-
...(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 }
|
|
1743
1865
|
};
|
|
1744
1866
|
var wantedHooks = (detection) => Object.fromEntries(
|
|
1745
1867
|
INSTALLED_HOOKS.map((spec2) => [spec2.event, hookHandler(detection.runner, spec2, detection.claude.failClosed)])
|
|
@@ -1755,7 +1877,8 @@ var settingsItem = (project, detection, input, previous) => {
|
|
|
1755
1877
|
ask: ASK_RULES,
|
|
1756
1878
|
attributionOff: input.preset === "gabriel",
|
|
1757
1879
|
marketplace: input.marketplace,
|
|
1758
|
-
language: input.claudeLanguage
|
|
1880
|
+
language: input.claudeLanguage,
|
|
1881
|
+
statusLine: { entry: ownStatusLine(detection.runner), wrap: existingStatusLine(project, current2.value, input.env) }
|
|
1759
1882
|
};
|
|
1760
1883
|
const merged2 = mergeSettings(current2.value, wanted, previous?.handlers ?? []);
|
|
1761
1884
|
const after = renderJson(merged2.settings, before === void 0 ? DEFAULT_STYLE : styleOf(before));
|
|
@@ -1770,7 +1893,7 @@ var settingsItem = (project, detection, input, previous) => {
|
|
|
1770
1893
|
});
|
|
1771
1894
|
};
|
|
1772
1895
|
var markdownItems = (project, runner, lang) => [
|
|
1773
|
-
{ path: AGENTS_FILE, render: (
|
|
1896
|
+
{ path: AGENTS_FILE, render: (text8) => withAgentsBlock(text8, runner, lang) },
|
|
1774
1897
|
{ path: CLAUDE_FILE, render: withAgentsImport }
|
|
1775
1898
|
].map(({ path, render: render3 }) => {
|
|
1776
1899
|
const before = project.read(path);
|
|
@@ -1795,11 +1918,19 @@ var generatedItem = (project, file, previous) => {
|
|
|
1795
1918
|
}
|
|
1796
1919
|
return { ...base, action: actionFor(before, template, "update"), after: template };
|
|
1797
1920
|
};
|
|
1798
|
-
var
|
|
1921
|
+
var PLAYWRIGHT_VERIFIER = ".claude/agents/verifier.md";
|
|
1922
|
+
var playwrightVerifier = (project) => {
|
|
1923
|
+
const wanted = configAt(parseConfigYaml(project.read(CONFIG_FILE) ?? ""), "review", "playwright") === "true";
|
|
1924
|
+
if (!wanted) return [];
|
|
1925
|
+
const template = agentMarkdown(withPlaywright(VERIFIER), GENERATED_MARKER);
|
|
1926
|
+
return [{ path: PLAYWRIGHT_VERIFIER, template, isOurs: (text8) => text8.includes(GENERATED_MARKER) }];
|
|
1927
|
+
};
|
|
1928
|
+
var generatedFiles = (project, input, runner, vendor) => {
|
|
1799
1929
|
const own = [
|
|
1800
1930
|
{ path: CONFIG_FILE, template: configTemplate(input.preset, input.lang), isOurs: hasMarker },
|
|
1801
1931
|
{ path: VERIFY_SKILL, template: verifySkillTemplate(runner, input.lang), isOurs: isOwnSkill },
|
|
1802
|
-
{ path: POLICY_FILE, template: policyTemplate(input.lang), isOurs: hasMarker }
|
|
1932
|
+
{ path: POLICY_FILE, template: policyTemplate(input.lang), isOurs: hasMarker },
|
|
1933
|
+
...playwrightVerifier(project)
|
|
1803
1934
|
];
|
|
1804
1935
|
if (!vendor) return accept(own);
|
|
1805
1936
|
if (input.bundle === void 0) return reject({ kind: "bundle", detail: "the running finitemonkey is not a bundle" });
|
|
@@ -1810,7 +1941,7 @@ var buildPlan = (project, detection, input, previous, migrations = MIGRATIONS) =
|
|
|
1810
1941
|
const settings = settingsItem(project, detection, input, entryFor(previous, SETTINGS_FILE));
|
|
1811
1942
|
if (!settings.ok) return settings;
|
|
1812
1943
|
const vendor = detection.runner.kind === "vendored" || entryFor(previous, VENDOR_BIN) !== void 0;
|
|
1813
|
-
const files = generatedFiles(input, detection.runner, vendor);
|
|
1944
|
+
const files = generatedFiles(project, input, detection.runner, vendor);
|
|
1814
1945
|
if (!files.ok) return files;
|
|
1815
1946
|
const generated = files.value.map((file) => generatedItem(project, file, entryFor(previous, file.path)));
|
|
1816
1947
|
return accept({
|
|
@@ -1837,9 +1968,9 @@ var needsWrite = (plan) => hasChanges(plan) || plan.previous === void 0;
|
|
|
1837
1968
|
// src/install/apply.ts
|
|
1838
1969
|
var stamp = (iso) => iso.replace(/[:.]/g, "-");
|
|
1839
1970
|
var ensureParent = (project, relative11) => project.runtime.mkdir(project.abs(dirname3(relative11)));
|
|
1840
|
-
var writeText = (project, relative11,
|
|
1971
|
+
var writeText = (project, relative11, text8) => {
|
|
1841
1972
|
const parent = ensureParent(project, relative11);
|
|
1842
|
-
const written = parent.ok ? project.runtime.writeAtomic(project.abs(relative11),
|
|
1973
|
+
const written = parent.ok ? project.runtime.writeAtomic(project.abs(relative11), text8) : parent;
|
|
1843
1974
|
return written.ok ? accept(void 0) : reject({ path: relative11, detail: written.error });
|
|
1844
1975
|
};
|
|
1845
1976
|
var restore = (project, items) => {
|
|
@@ -1987,8 +2118,8 @@ var projectAt = (root, runtime) => {
|
|
|
1987
2118
|
runtime,
|
|
1988
2119
|
abs,
|
|
1989
2120
|
read: (relative11) => {
|
|
1990
|
-
const
|
|
1991
|
-
return
|
|
2121
|
+
const text8 = runtime.readText(abs(relative11));
|
|
2122
|
+
return text8.ok ? text8.value : void 0;
|
|
1992
2123
|
},
|
|
1993
2124
|
exists: (relative11) => runtime.stat(abs(relative11)) !== void 0,
|
|
1994
2125
|
isDir: (relative11) => runtime.stat(abs(relative11))?.kind === "dir"
|
|
@@ -2061,8 +2192,8 @@ var invalidPreset = (raw) => problem("invalid_arguments", "error.invalid_preset"
|
|
|
2061
2192
|
});
|
|
2062
2193
|
var selfBundle = (ctx) => {
|
|
2063
2194
|
if (!isBundlePath(ctx.runtime.selfPath)) return void 0;
|
|
2064
|
-
const
|
|
2065
|
-
return
|
|
2195
|
+
const text8 = ctx.runtime.readText(ctx.runtime.selfPath);
|
|
2196
|
+
return text8.ok ? text8.value : void 0;
|
|
2066
2197
|
};
|
|
2067
2198
|
var INHERITED = /* @__PURE__ */ new Set(["default", "config", "claude-settings"]);
|
|
2068
2199
|
var planInputFor = (ctx, preset, bin, marketplace) => ({
|
|
@@ -2166,13 +2297,13 @@ var isPlaceholder = (value) => {
|
|
|
2166
2297
|
var isTestOrLockPath = (path) => path !== void 0 && (LOCKFILES.test(toPosixPath(path)) || TEST_PATH.test(toPosixPath(path)));
|
|
2167
2298
|
|
|
2168
2299
|
// src/secrets/entropy.ts
|
|
2169
|
-
var shannonEntropy = (
|
|
2170
|
-
if (
|
|
2300
|
+
var shannonEntropy = (text8) => {
|
|
2301
|
+
if (text8.length === 0) return 0;
|
|
2171
2302
|
const counts = /* @__PURE__ */ new Map();
|
|
2172
|
-
for (const char of
|
|
2303
|
+
for (const char of text8) counts.set(char, (counts.get(char) ?? 0) + 1);
|
|
2173
2304
|
let entropy = 0;
|
|
2174
2305
|
for (const count4 of counts.values()) {
|
|
2175
|
-
const p = count4 /
|
|
2306
|
+
const p = count4 / text8.length;
|
|
2176
2307
|
entropy -= p * Math.log2(p);
|
|
2177
2308
|
}
|
|
2178
2309
|
return entropy;
|
|
@@ -2180,7 +2311,7 @@ var shannonEntropy = (text7) => {
|
|
|
2180
2311
|
|
|
2181
2312
|
// src/secrets/fingerprint.ts
|
|
2182
2313
|
import { createHash as createHash2 } from "node:crypto";
|
|
2183
|
-
var sha256Hex = (
|
|
2314
|
+
var sha256Hex = (text8) => createHash2("sha256").update(text8).digest("hex");
|
|
2184
2315
|
var fingerprintOf = (secret) => sha256Hex(`finitemonkey:secret:${secret}`).slice(0, 16);
|
|
2185
2316
|
var ALNUM = /[A-Za-z0-9]/g;
|
|
2186
2317
|
var MIN_TAIL_LENGTH = 12;
|
|
@@ -2190,9 +2321,9 @@ var tailOf = (secret) => {
|
|
|
2190
2321
|
};
|
|
2191
2322
|
|
|
2192
2323
|
// src/secrets/lines.ts
|
|
2193
|
-
var lineLocator = (
|
|
2324
|
+
var lineLocator = (text8) => {
|
|
2194
2325
|
const starts = [0];
|
|
2195
|
-
for (let index =
|
|
2326
|
+
for (let index = text8.indexOf("\n"); index !== -1; index = text8.indexOf("\n", index + 1)) starts.push(index + 1);
|
|
2196
2327
|
return (offset) => {
|
|
2197
2328
|
let low = 0;
|
|
2198
2329
|
let high = starts.length - 1;
|
|
@@ -2486,12 +2617,12 @@ var hasRandomRun = (secret, length) => (secret.match(new RegExp(`[A-Za-z0-9]{${S
|
|
|
2486
2617
|
);
|
|
2487
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);
|
|
2488
2619
|
var NAME_CHAR = /[A-Za-z0-9_.-]/;
|
|
2489
|
-
var nameAround = (
|
|
2620
|
+
var nameAround = (text8, at) => {
|
|
2490
2621
|
let start = at;
|
|
2491
|
-
while (start > 0 && NAME_CHAR.test(
|
|
2622
|
+
while (start > 0 && NAME_CHAR.test(text8.charAt(start - 1))) start -= 1;
|
|
2492
2623
|
let end = at;
|
|
2493
|
-
while (end <
|
|
2494
|
-
return
|
|
2624
|
+
while (end < text8.length && NAME_CHAR.test(text8.charAt(end))) end += 1;
|
|
2625
|
+
return text8.slice(start, end).toLowerCase();
|
|
2495
2626
|
};
|
|
2496
2627
|
var ignoredName = (rule, match) => rule.ignoredNames.length > 0 && rule.ignoredNames.includes(nameAround(match.input, match.index));
|
|
2497
2628
|
var spanOf = (rule, match) => {
|
|
@@ -2501,9 +2632,9 @@ var spanOf = (rule, match) => {
|
|
|
2501
2632
|
const secret = match.input.slice(start, end);
|
|
2502
2633
|
return accepted(rule, secret) ? { rule, start, end, secret } : void 0;
|
|
2503
2634
|
};
|
|
2504
|
-
var ruleSpans = (rule,
|
|
2635
|
+
var ruleSpans = (rule, text8) => {
|
|
2505
2636
|
const spans = [];
|
|
2506
|
-
for (const match of
|
|
2637
|
+
for (const match of text8.matchAll(rule.pattern)) {
|
|
2507
2638
|
const span = spanOf(rule, match);
|
|
2508
2639
|
if (span !== void 0) spans.push(span);
|
|
2509
2640
|
}
|
|
@@ -2529,31 +2660,31 @@ var toFinding = (span, locate2) => {
|
|
|
2529
2660
|
tail: tailOf(span.secret)
|
|
2530
2661
|
};
|
|
2531
2662
|
};
|
|
2532
|
-
var scanText = (
|
|
2533
|
-
if (
|
|
2534
|
-
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);
|
|
2535
2666
|
if (rules.length === 0) return [];
|
|
2536
2667
|
const taken = [];
|
|
2537
2668
|
for (const rule of rules) {
|
|
2538
|
-
for (const span of ruleSpans(rule,
|
|
2669
|
+
for (const span of ruleSpans(rule, text8)) if (!overlaps(taken, span)) taken.push(span);
|
|
2539
2670
|
}
|
|
2540
2671
|
if (taken.length === 0) return [];
|
|
2541
|
-
const locate2 = lineLocator(
|
|
2672
|
+
const locate2 = lineLocator(text8);
|
|
2542
2673
|
return taken.sort((a, b) => a.start - b.start).map((span) => toFinding(span, locate2));
|
|
2543
2674
|
};
|
|
2544
2675
|
|
|
2545
2676
|
// src/mask/mask.ts
|
|
2546
2677
|
var maskToken = (finding4) => `«secret:${finding4.ruleId}:${finding4.tail}»`;
|
|
2547
|
-
var maskText = (
|
|
2548
|
-
const findings = scanText(
|
|
2549
|
-
if (findings.length === 0) return
|
|
2678
|
+
var maskText = (text8) => {
|
|
2679
|
+
const findings = scanText(text8, { rules: CREDENTIAL_RULES });
|
|
2680
|
+
if (findings.length === 0) return text8;
|
|
2550
2681
|
let out = "";
|
|
2551
2682
|
let cursor = 0;
|
|
2552
2683
|
for (const finding4 of findings) {
|
|
2553
|
-
out +=
|
|
2684
|
+
out += text8.slice(cursor, finding4.start) + maskToken(finding4);
|
|
2554
2685
|
cursor = finding4.end;
|
|
2555
2686
|
}
|
|
2556
|
-
return out +
|
|
2687
|
+
return out + text8.slice(cursor);
|
|
2557
2688
|
};
|
|
2558
2689
|
var maskDeep = (value) => {
|
|
2559
2690
|
if (typeof value === "string") return maskText(value);
|
|
@@ -2771,14 +2902,14 @@ var toolVersion = (env, command) => {
|
|
|
2771
2902
|
return result.ok ? result.stdout.trim().split("\n")[0] ?? "" : "not found";
|
|
2772
2903
|
};
|
|
2773
2904
|
var lastHookFailures = (env) => {
|
|
2774
|
-
const
|
|
2775
|
-
if (!
|
|
2776
|
-
return
|
|
2905
|
+
const text8 = env.runtime.readText(fmPaths(env.root).log);
|
|
2906
|
+
if (!text8.ok) return [];
|
|
2907
|
+
return text8.value.split("\n").filter((line2) => {
|
|
2777
2908
|
const parsed = line2.trim() === "" ? void 0 : parseJsonObject(line2);
|
|
2778
2909
|
return parsed?.ok === true && isRecord(parsed.value) && parsed.value["event"] === HOOK_FAILURE_EVENT;
|
|
2779
2910
|
}).slice(-MAX_FAILURES);
|
|
2780
2911
|
};
|
|
2781
|
-
var withoutHome = (
|
|
2912
|
+
var withoutHome = (text8, home) => home.length > 1 ? text8.split(home).join("~") : text8;
|
|
2782
2913
|
var reportMarkdown = (env, ctx, envelope) => {
|
|
2783
2914
|
const failures = lastHookFailures(env);
|
|
2784
2915
|
const lines3 = [
|
|
@@ -2814,9 +2945,9 @@ var YIELD_STDOUT = `${JSON.stringify({ continue: true })}
|
|
|
2814
2945
|
`;
|
|
2815
2946
|
var projectOwnsHooks = (ctx) => {
|
|
2816
2947
|
const root = ctx.env["CLAUDE_PROJECT_DIR"] ?? ctx.cwd;
|
|
2817
|
-
const
|
|
2818
|
-
if (!
|
|
2819
|
-
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);
|
|
2820
2951
|
return parsed.ok && ownHandlersIn(parsed.value) > 0;
|
|
2821
2952
|
};
|
|
2822
2953
|
|
|
@@ -2850,8 +2981,8 @@ var settingsPaths = (env) => {
|
|
|
2850
2981
|
];
|
|
2851
2982
|
};
|
|
2852
2983
|
var readSettings = (env) => settingsPaths(env).flatMap(({ scope: scope2, path }) => {
|
|
2853
|
-
const
|
|
2854
|
-
const parsed =
|
|
2984
|
+
const text8 = env.runtime.readText(path);
|
|
2985
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
2855
2986
|
return parsed?.ok === true ? [{ scope: scope2, path, settings: parsed.value }] : [];
|
|
2856
2987
|
});
|
|
2857
2988
|
var scopesWith = (all, test) => all.filter(({ settings }) => test(settings)).map(({ scope: scope2 }) => scope2);
|
|
@@ -3137,7 +3268,7 @@ var editsOf = (input) => {
|
|
|
3137
3268
|
});
|
|
3138
3269
|
};
|
|
3139
3270
|
var applyEdits = (before, edits) => edits.reduce(
|
|
3140
|
-
(
|
|
3271
|
+
(text8, edit) => edit.all ? text8.split(edit.oldText).join(edit.newText) : text8.replace(edit.oldText, () => edit.newText),
|
|
3141
3272
|
before
|
|
3142
3273
|
);
|
|
3143
3274
|
|
|
@@ -3157,7 +3288,7 @@ var familyOf = (base) => {
|
|
|
3157
3288
|
if (found === void 0) throw new Error(`no reason family for ${base}`);
|
|
3158
3289
|
return found;
|
|
3159
3290
|
};
|
|
3160
|
-
var capReason = (
|
|
3291
|
+
var capReason = (text8) => text8.length <= REASON_LIMIT ? text8 : `${text8.slice(0, REASON_LIMIT - 1)}…`;
|
|
3161
3292
|
var modelReason = (t, base, params2 = {}) => {
|
|
3162
3293
|
const claim = `${base}.claim`;
|
|
3163
3294
|
const requires = `${base}.requires`;
|
|
@@ -3184,9 +3315,9 @@ var textRecord = (value) => isRecord(value) ? Object.fromEntries(
|
|
|
3184
3315
|
Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
|
|
3185
3316
|
) : {};
|
|
3186
3317
|
var readJsonFile = (runtime, path) => {
|
|
3187
|
-
const
|
|
3188
|
-
if (!
|
|
3189
|
-
const parsed = parseJsonObject(
|
|
3318
|
+
const text8 = runtime.readText(path);
|
|
3319
|
+
if (!text8.ok) return void 0;
|
|
3320
|
+
const parsed = parseJsonObject(text8.value);
|
|
3190
3321
|
return parsed.ok ? parsed.value : void 0;
|
|
3191
3322
|
};
|
|
3192
3323
|
var writeJsonFile = (runtime, path, value) => runtime.writeText(path, `${JSON.stringify({ schemaVersion: 1, ...value }, null, 2)}
|
|
@@ -3299,7 +3430,7 @@ var SHELL_TOOLS = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
|
|
|
3299
3430
|
import { posix } from "node:path";
|
|
3300
3431
|
|
|
3301
3432
|
// src/shell/scan.ts
|
|
3302
|
-
var cursorOf = (
|
|
3433
|
+
var cursorOf = (text8) => ({ text: text8, pos: 0, incomplete: false, subs: [] });
|
|
3303
3434
|
var peek = (cursor, offset = 0) => cursor.text.charAt(cursor.pos + offset);
|
|
3304
3435
|
var atEnd = (cursor) => cursor.pos >= cursor.text.length;
|
|
3305
3436
|
var skipQuoted = (cursor, quote) => {
|
|
@@ -3514,8 +3645,8 @@ var nextToken = (cursor, tokens, pending) => {
|
|
|
3514
3645
|
tokens.push({ kind: "op", text: op });
|
|
3515
3646
|
if (op === "\n") flushHeredocs(cursor, tokens, pending);
|
|
3516
3647
|
};
|
|
3517
|
-
var lexBash = (
|
|
3518
|
-
const cursor = cursorOf(
|
|
3648
|
+
var lexBash = (text8) => {
|
|
3649
|
+
const cursor = cursorOf(text8.replace(/\r\n/g, "\n"));
|
|
3519
3650
|
const tokens = [];
|
|
3520
3651
|
const pending = [];
|
|
3521
3652
|
skipBlank(cursor);
|
|
@@ -3663,9 +3794,9 @@ var next = (cursor, tokens, cmd) => {
|
|
|
3663
3794
|
}
|
|
3664
3795
|
const op = OPERATORS2.find((candidate) => cursor.text.startsWith(candidate, cursor.pos));
|
|
3665
3796
|
if (op !== void 0 || cmd && peek(cursor) === "&") {
|
|
3666
|
-
const
|
|
3667
|
-
cursor.pos +=
|
|
3668
|
-
tokens.push({ kind: "op", text:
|
|
3797
|
+
const text8 = op ?? "&";
|
|
3798
|
+
cursor.pos += text8.length;
|
|
3799
|
+
tokens.push({ kind: "op", text: text8 });
|
|
3669
3800
|
return;
|
|
3670
3801
|
}
|
|
3671
3802
|
if ((peek(cursor) === "&" || peek(cursor) === ".") && peek(cursor, 1) === " " && commandStart(tokens)) {
|
|
@@ -3674,8 +3805,8 @@ var next = (cursor, tokens, cmd) => {
|
|
|
3674
3805
|
}
|
|
3675
3806
|
tokens.push(word(cursor, cmd));
|
|
3676
3807
|
};
|
|
3677
|
-
var lexPowerShell = (
|
|
3678
|
-
const cursor = cursorOf(
|
|
3808
|
+
var lexPowerShell = (text8, cmd = false) => {
|
|
3809
|
+
const cursor = cursorOf(text8.replace(/\r\n/g, "\n"));
|
|
3679
3810
|
const tokens = [];
|
|
3680
3811
|
const skip = () => {
|
|
3681
3812
|
while (peek(cursor) === " " || peek(cursor) === " ") cursor.pos += 1;
|
|
@@ -3878,7 +4009,7 @@ var MAX_DEPTH = 8;
|
|
|
3878
4009
|
var ASSIGNMENT2 = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/s;
|
|
3879
4010
|
var PS_ENV = /^\$env:([A-Za-z_][A-Za-z0-9_]*)=?(.*)$/is;
|
|
3880
4011
|
var CHANGES_DIR = /* @__PURE__ */ new Set(["cd", "pushd", "set-location", "push-location"]);
|
|
3881
|
-
var lex = (
|
|
4012
|
+
var lex = (text8, language) => language === "bash" ? lexBash(text8) : lexPowerShell(text8, language === "cmd");
|
|
3882
4013
|
var isWindowsAbsolute = (path) => /^[A-Za-z]:\//.test(path);
|
|
3883
4014
|
var joinDir = (current2, target2) => {
|
|
3884
4015
|
const next2 = toPosixPath(target2);
|
|
@@ -3949,21 +4080,21 @@ var visit = (raw, language, context, depth, collector) => {
|
|
|
3949
4080
|
emit(result.stage, raw, language, context, collector);
|
|
3950
4081
|
for (const extra of result.extra) emit(extra, raw, language, context, collector);
|
|
3951
4082
|
};
|
|
3952
|
-
var collect = (
|
|
4083
|
+
var collect = (text8, language, context, depth, collector) => {
|
|
3953
4084
|
if (depth > MAX_DEPTH) {
|
|
3954
4085
|
collector.incomplete = true;
|
|
3955
4086
|
return;
|
|
3956
4087
|
}
|
|
3957
|
-
const lexed = lex(
|
|
4088
|
+
const lexed = lex(text8, language);
|
|
3958
4089
|
if (lexed.incomplete) collector.incomplete = true;
|
|
3959
4090
|
for (const substitution of lexed.substitutions) {
|
|
3960
4091
|
collect(substitution, language, { ...context }, depth + 1, collector);
|
|
3961
4092
|
}
|
|
3962
4093
|
for (const raw of rawCommands(lexed.tokens)) visit(raw, language, context, depth, collector);
|
|
3963
4094
|
};
|
|
3964
|
-
var normalizeCommand = (
|
|
4095
|
+
var normalizeCommand = (text8, dialect = "bash") => {
|
|
3965
4096
|
const collector = { out: [], incomplete: false };
|
|
3966
|
-
collect(
|
|
4097
|
+
collect(text8, dialect, { env: {}, dir: void 0, via: [] }, 0, collector);
|
|
3967
4098
|
return { commands: collector.out, incomplete: collector.incomplete };
|
|
3968
4099
|
};
|
|
3969
4100
|
var dialectOf = (toolName3) => toolName3 === "PowerShell" ? "powershell" : "bash";
|
|
@@ -3975,7 +4106,7 @@ var segmentsOf = (command) => normalizeCommand(command).commands.map((item2) =>
|
|
|
3975
4106
|
|
|
3976
4107
|
// src/guards/shell.ts
|
|
3977
4108
|
var HEREDOC = /^<<[-<]?$/;
|
|
3978
|
-
var parseShell = (
|
|
4109
|
+
var parseShell = (text8) => normalizeCommand(text8).commands.map((command) => ({
|
|
3979
4110
|
env: command.env,
|
|
3980
4111
|
argv: command.program === "" ? [] : [command.program, ...command.args],
|
|
3981
4112
|
redirects: command.redirects.filter((redirect) => !HEREDOC.test(redirect.op)).map((redirect) => redirect.target)
|
|
@@ -4080,6 +4211,15 @@ var bypassGuard = (input) => {
|
|
|
4080
4211
|
);
|
|
4081
4212
|
};
|
|
4082
4213
|
|
|
4214
|
+
// src/guards/review/hook.ts
|
|
4215
|
+
var FORBIDDEN = new Set(FORBIDDEN_TOOLS);
|
|
4216
|
+
var reviewAgentGuard = (input) => {
|
|
4217
|
+
const agent = reviewAgentOf(textField(input.payload.raw, "agent_type") ?? "");
|
|
4218
|
+
const tool = textField(input.payload.raw, "tool_name") ?? "";
|
|
4219
|
+
if (agent === void 0 || !FORBIDDEN.has(tool)) return { kind: "allow" };
|
|
4220
|
+
return { kind: "block", reason: input.t("review.guard.read_only", { agent, tool }) };
|
|
4221
|
+
};
|
|
4222
|
+
|
|
4083
4223
|
// src/guards/self/protected.ts
|
|
4084
4224
|
import { relative as relative2 } from "node:path";
|
|
4085
4225
|
var normalize = (path) => toPosixPath(path).replace(/^\.\//, "");
|
|
@@ -4128,9 +4268,9 @@ var writtenPaths = (command) => {
|
|
|
4128
4268
|
if (name === "git" && GIT_WRITERS.has(operands2[0] ?? "")) return [...command.redirects, ...operands2.slice(1)];
|
|
4129
4269
|
return command.redirects;
|
|
4130
4270
|
};
|
|
4131
|
-
var mentioned = (
|
|
4132
|
-
if (/\.finitemonkey[\\/]allow/.test(
|
|
4133
|
-
if (/\.finitemonkey|\.gitleaksignore/.test(
|
|
4271
|
+
var mentioned = (text8) => {
|
|
4272
|
+
if (/\.finitemonkey[\\/]allow/.test(text8)) return "allow_store";
|
|
4273
|
+
if (/\.finitemonkey|\.gitleaksignore/.test(text8)) return "policy";
|
|
4134
4274
|
return void 0;
|
|
4135
4275
|
};
|
|
4136
4276
|
var bashSelfTargets = (root, commandLine) => parseShell(commandLine).flatMap(
|
|
@@ -4334,8 +4474,8 @@ var compareConfig = (path, before, after, accepted2) => {
|
|
|
4334
4474
|
// src/guards/tests/hook.ts
|
|
4335
4475
|
var localPath = (root, path) => toPosixPath(relative3(root, toAbsolutePath(path, root)));
|
|
4336
4476
|
var read = (input, path) => {
|
|
4337
|
-
const
|
|
4338
|
-
return
|
|
4477
|
+
const text8 = input.runtime.readText(path);
|
|
4478
|
+
return text8.ok ? text8.value : void 0;
|
|
4339
4479
|
};
|
|
4340
4480
|
var proposed = (tool, args2, before) => {
|
|
4341
4481
|
if (tool === "Write") return textField(args2, "content") ?? "";
|
|
@@ -4410,7 +4550,7 @@ var contextOutput = (event, additionalContext, systemMessage) => ({
|
|
|
4410
4550
|
hookSpecificOutput: { hookEventName: event, additionalContext },
|
|
4411
4551
|
...systemMessage === void 0 ? {} : { systemMessage }
|
|
4412
4552
|
});
|
|
4413
|
-
var human = (
|
|
4553
|
+
var human = (text8) => `finitemonkey: ${text8}`;
|
|
4414
4554
|
|
|
4415
4555
|
// src/look/reads.ts
|
|
4416
4556
|
var READS_FILE = "look-reads.json";
|
|
@@ -4472,8 +4612,8 @@ var parseMarkers = (prompt) => {
|
|
|
4472
4612
|
// src/policy/pause.ts
|
|
4473
4613
|
var PAUSE_FILE = "pause.json";
|
|
4474
4614
|
var readPause = (runtime, paths) => {
|
|
4475
|
-
const
|
|
4476
|
-
const parsed =
|
|
4615
|
+
const text8 = runtime.readText(paths.cache(PAUSE_FILE));
|
|
4616
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
4477
4617
|
if (parsed?.ok !== true || !isRecord(parsed.value)) return void 0;
|
|
4478
4618
|
const { sessionId, since, by } = parsed.value;
|
|
4479
4619
|
if (typeof sessionId !== "string" || typeof since !== "string") return void 0;
|
|
@@ -4528,9 +4668,9 @@ var countIn = (value) => {
|
|
|
4528
4668
|
if (Array.isArray(value)) return value.reduce((sum, item2) => sum + countIn(item2), 0);
|
|
4529
4669
|
return isRecord(value) ? Object.values(value).reduce((sum, item2) => sum + countIn(item2), 0) : 0;
|
|
4530
4670
|
};
|
|
4531
|
-
var censusOf = (
|
|
4532
|
-
if (
|
|
4533
|
-
const parsed = parseJsonObject(
|
|
4671
|
+
var censusOf = (text8) => {
|
|
4672
|
+
if (text8 === void 0) return { hooks: 0, disabled: false };
|
|
4673
|
+
const parsed = parseJsonObject(text8);
|
|
4534
4674
|
if (!parsed.ok) return void 0;
|
|
4535
4675
|
return { hooks: countIn(parsed.value["hooks"]), disabled: parsed.value["disableAllHooks"] === true };
|
|
4536
4676
|
};
|
|
@@ -4540,8 +4680,8 @@ var settingsFiles = (paths, env) => [
|
|
|
4540
4680
|
...env["HOME"] === void 0 ? [] : [toAbsolutePath(join13(env["HOME"], ".claude", "settings.json"))]
|
|
4541
4681
|
];
|
|
4542
4682
|
var read2 = (runtime, path) => {
|
|
4543
|
-
const
|
|
4544
|
-
return
|
|
4683
|
+
const text8 = runtime.readText(path);
|
|
4684
|
+
return text8.ok ? text8.value : void 0;
|
|
4545
4685
|
};
|
|
4546
4686
|
var known = (runtime, paths) => {
|
|
4547
4687
|
const raw = readJsonFile(runtime, paths.cache(CONFIG_FILE3))?.["hooks"];
|
|
@@ -4596,9 +4736,9 @@ var LOCKFILES2 = [
|
|
|
4596
4736
|
];
|
|
4597
4737
|
var packageManager = (files) => LOCKFILES2.find(([file]) => files.exists(file))?.[1] ?? "npm";
|
|
4598
4738
|
var scriptChecks = (files) => {
|
|
4599
|
-
const
|
|
4600
|
-
if (
|
|
4601
|
-
const parsed = parseJsonObject(
|
|
4739
|
+
const text8 = files.read("package.json");
|
|
4740
|
+
if (text8 === void 0) return [];
|
|
4741
|
+
const parsed = parseJsonObject(text8);
|
|
4602
4742
|
const scripts = parsed.ok ? parsed.value["scripts"] : void 0;
|
|
4603
4743
|
if (!isRecord(scripts)) return [];
|
|
4604
4744
|
const pm = packageManager(files);
|
|
@@ -4688,16 +4828,16 @@ var unescapeDouble = (body2) => {
|
|
|
4688
4828
|
}
|
|
4689
4829
|
return accept(out);
|
|
4690
4830
|
};
|
|
4691
|
-
var quotedEnd = (
|
|
4692
|
-
const quote =
|
|
4693
|
-
for (let index = start + 1; index <
|
|
4694
|
-
const char =
|
|
4831
|
+
var quotedEnd = (text8, start) => {
|
|
4832
|
+
const quote = text8[start];
|
|
4833
|
+
for (let index = start + 1; index < text8.length; index += 1) {
|
|
4834
|
+
const char = text8[index];
|
|
4695
4835
|
if (quote === '"' && char === "\\") {
|
|
4696
4836
|
index += 1;
|
|
4697
4837
|
continue;
|
|
4698
4838
|
}
|
|
4699
4839
|
if (char !== quote) continue;
|
|
4700
|
-
if (quote === "'" &&
|
|
4840
|
+
if (quote === "'" && text8[index + 1] === "'") {
|
|
4701
4841
|
index += 1;
|
|
4702
4842
|
continue;
|
|
4703
4843
|
}
|
|
@@ -4705,20 +4845,20 @@ var quotedEnd = (text7, start) => {
|
|
|
4705
4845
|
}
|
|
4706
4846
|
return -1;
|
|
4707
4847
|
};
|
|
4708
|
-
var parseQuoted = (
|
|
4709
|
-
const end = quotedEnd(
|
|
4710
|
-
if (end !==
|
|
4711
|
-
const body2 =
|
|
4712
|
-
return
|
|
4848
|
+
var parseQuoted = (text8) => {
|
|
4849
|
+
const end = quotedEnd(text8, 0);
|
|
4850
|
+
if (end !== text8.length - 1) return reject(`unterminated or trailing text after quoted string`);
|
|
4851
|
+
const body2 = text8.slice(1, -1);
|
|
4852
|
+
return text8.startsWith('"') ? unescapeDouble(body2) : accept(body2.replace(/''/g, "'"));
|
|
4713
4853
|
};
|
|
4714
4854
|
var NUMBER = /^-?(0|[1-9]\d*)(\.\d+)?([eE][-+]?\d+)?$/;
|
|
4715
4855
|
var UNSUPPORTED = /^[&*!|>%@`]/;
|
|
4716
|
-
var parsePlain = (
|
|
4717
|
-
if (UNSUPPORTED.test(
|
|
4718
|
-
if (
|
|
4719
|
-
if (
|
|
4720
|
-
if (NUMBER.test(
|
|
4721
|
-
return accept(
|
|
4856
|
+
var parsePlain = (text8) => {
|
|
4857
|
+
if (UNSUPPORTED.test(text8)) return reject(`unsupported YAML syntax "${text8.slice(0, 1)}"`);
|
|
4858
|
+
if (text8 === "true" || text8 === "false") return accept(text8 === "true");
|
|
4859
|
+
if (text8 === "null" || text8 === "~") return accept(null);
|
|
4860
|
+
if (NUMBER.test(text8)) return accept(Number(text8));
|
|
4861
|
+
return accept(text8);
|
|
4722
4862
|
};
|
|
4723
4863
|
|
|
4724
4864
|
// src/core/yaml/flow.ts
|
|
@@ -4745,17 +4885,17 @@ var splitTop = (body2) => {
|
|
|
4745
4885
|
if (last.trim() !== "" || parts.length > 0) parts.push(last);
|
|
4746
4886
|
return accept(parts.map((part2) => part2.trim()));
|
|
4747
4887
|
};
|
|
4748
|
-
var splitKey = (
|
|
4888
|
+
var splitKey = (text8) => {
|
|
4749
4889
|
let index = 0;
|
|
4750
|
-
if (
|
|
4751
|
-
index = quotedEnd(
|
|
4890
|
+
if (text8.startsWith('"') || text8.startsWith("'")) {
|
|
4891
|
+
index = quotedEnd(text8, 0) + 1;
|
|
4752
4892
|
if (index <= 0) return void 0;
|
|
4753
4893
|
}
|
|
4754
|
-
for (; index <
|
|
4755
|
-
const char =
|
|
4756
|
-
if (char === "#" &&
|
|
4757
|
-
if (char === ":" && (index + 1 ===
|
|
4758
|
-
return { key:
|
|
4894
|
+
for (; index < text8.length; index += 1) {
|
|
4895
|
+
const char = text8[index];
|
|
4896
|
+
if (char === "#" && text8[index - 1] === " ") return void 0;
|
|
4897
|
+
if (char === ":" && (index + 1 === text8.length || text8[index + 1] === " ")) {
|
|
4898
|
+
return { key: text8.slice(0, index).trim(), rest: text8.slice(index + 1).trim() };
|
|
4759
4899
|
}
|
|
4760
4900
|
}
|
|
4761
4901
|
return void 0;
|
|
@@ -4784,39 +4924,39 @@ var flowSeq = (parts) => {
|
|
|
4784
4924
|
}
|
|
4785
4925
|
return accept(out);
|
|
4786
4926
|
};
|
|
4787
|
-
var parseValue = (
|
|
4788
|
-
if (
|
|
4789
|
-
const first =
|
|
4790
|
-
if (first === '"' || first === "'") return parseQuoted(
|
|
4927
|
+
var parseValue = (text8) => {
|
|
4928
|
+
if (text8 === "") return accept(null);
|
|
4929
|
+
const first = text8[0] ?? "";
|
|
4930
|
+
if (first === '"' || first === "'") return parseQuoted(text8);
|
|
4791
4931
|
const close2 = OPEN[first];
|
|
4792
|
-
if (close2 === void 0) return parsePlain(
|
|
4793
|
-
if (!
|
|
4794
|
-
const parts = splitTop(
|
|
4932
|
+
if (close2 === void 0) return parsePlain(text8);
|
|
4933
|
+
if (!text8.endsWith(close2)) return reject(`flow collection must end on the same line with "${close2}"`);
|
|
4934
|
+
const parts = splitTop(text8.slice(1, -1));
|
|
4795
4935
|
if (!parts.ok) return parts;
|
|
4796
4936
|
return first === "[" ? flowSeq(parts.value) : flowMap(parts.value);
|
|
4797
4937
|
};
|
|
4798
4938
|
|
|
4799
4939
|
// src/core/yaml/parse.ts
|
|
4800
4940
|
var BOM = new RegExp(`^${String.fromCharCode(65279)}`);
|
|
4801
|
-
var stripComment2 = (
|
|
4802
|
-
for (let index = 0; index <
|
|
4803
|
-
const char =
|
|
4941
|
+
var stripComment2 = (text8) => {
|
|
4942
|
+
for (let index = 0; index < text8.length; index += 1) {
|
|
4943
|
+
const char = text8[index];
|
|
4804
4944
|
if (char === '"' || char === "'") {
|
|
4805
|
-
const end = quotedEnd(
|
|
4806
|
-
if (end < 0) return
|
|
4945
|
+
const end = quotedEnd(text8, index);
|
|
4946
|
+
if (end < 0) return text8;
|
|
4807
4947
|
index = end;
|
|
4808
|
-
} else if (char === "#" && (index === 0 ||
|
|
4948
|
+
} else if (char === "#" && (index === 0 || text8[index - 1] === " ")) return text8.slice(0, index);
|
|
4809
4949
|
}
|
|
4810
|
-
return
|
|
4950
|
+
return text8;
|
|
4811
4951
|
};
|
|
4812
4952
|
var toLines = (source) => {
|
|
4813
4953
|
const lines3 = [];
|
|
4814
4954
|
const raw = source.replace(BOM, "").split(/\r?\n/);
|
|
4815
4955
|
for (const [index, full] of raw.entries()) {
|
|
4816
|
-
const
|
|
4817
|
-
const body2 =
|
|
4956
|
+
const text8 = stripComment2(full).trimEnd();
|
|
4957
|
+
const body2 = text8.trimStart();
|
|
4818
4958
|
if (body2 === "" || body2 === "---" && lines3.length === 0) continue;
|
|
4819
|
-
const indentText =
|
|
4959
|
+
const indentText = text8.slice(0, text8.length - body2.length);
|
|
4820
4960
|
if (indentText.includes(" ")) return reject({ line: index + 1, message: "tabs are not allowed for indentation" });
|
|
4821
4961
|
if (body2 === "---" || body2 === "...")
|
|
4822
4962
|
return reject({ line: index + 1, message: "only one YAML document is allowed" });
|
|
@@ -4825,11 +4965,11 @@ var toLines = (source) => {
|
|
|
4825
4965
|
return accept(lines3);
|
|
4826
4966
|
};
|
|
4827
4967
|
var fail = (line2, message) => reject({ line: line2?.number ?? 0, message });
|
|
4828
|
-
var scalar = (line2,
|
|
4829
|
-
const value = parseValue(
|
|
4968
|
+
var scalar = (line2, text8) => {
|
|
4969
|
+
const value = parseValue(text8);
|
|
4830
4970
|
return value.ok ? value : fail(line2, value.error);
|
|
4831
4971
|
};
|
|
4832
|
-
var isItem = (
|
|
4972
|
+
var isItem = (text8) => text8 === "-" || text8.startsWith("- ");
|
|
4833
4973
|
var child = (lines3, index, parent, sameIndentSeq) => {
|
|
4834
4974
|
const next2 = lines3[index];
|
|
4835
4975
|
if (next2 !== void 0 && next2.indent > parent.indent) return block(lines3, index, next2.indent);
|
|
@@ -5072,8 +5212,8 @@ var validateContract = (value, implicit = false) => {
|
|
|
5072
5212
|
// src/task/load.ts
|
|
5073
5213
|
var contractHash = (contract) => sha256Hex(JSON.stringify(contract.acceptance.map((check2) => [check2.id, check2.kind, check2.run ?? ""])));
|
|
5074
5214
|
var taskRelative = (id) => `.finitemonkey/tasks/${id}.yml`;
|
|
5075
|
-
var parseContract = (id,
|
|
5076
|
-
const yaml = parseYaml(
|
|
5215
|
+
var parseContract = (id, text8) => {
|
|
5216
|
+
const yaml = parseYaml(text8);
|
|
5077
5217
|
if (!yaml.ok)
|
|
5078
5218
|
return reject({ kind: "invalid", id, issues: [`line ${String(yaml.error.line)}: ${yaml.error.message}`] });
|
|
5079
5219
|
const valid = validateContract(yaml.value);
|
|
@@ -5164,29 +5304,29 @@ var gitAt = (runtime, cwd) => {
|
|
|
5164
5304
|
}
|
|
5165
5305
|
};
|
|
5166
5306
|
};
|
|
5167
|
-
var gitRoot = (
|
|
5168
|
-
const top =
|
|
5307
|
+
var gitRoot = (git2) => {
|
|
5308
|
+
const top = git2.out(["rev-parse", "--show-toplevel"])?.trim();
|
|
5169
5309
|
return top === void 0 || top === "" ? void 0 : toAbsolutePath(top);
|
|
5170
5310
|
};
|
|
5171
5311
|
var nonEmpty = (value) => value === "" ? void 0 : value;
|
|
5172
|
-
var headSha = (
|
|
5173
|
-
var currentBranch = (
|
|
5174
|
-
const name =
|
|
5312
|
+
var headSha = (git2) => git2.out(["rev-parse", "--verify", "-q", "HEAD"])?.trim();
|
|
5313
|
+
var currentBranch = (git2) => {
|
|
5314
|
+
const name = git2.out(["rev-parse", "--abbrev-ref", "HEAD"])?.trim();
|
|
5175
5315
|
return name === void 0 || name === "HEAD" || name === "" ? void 0 : name;
|
|
5176
5316
|
};
|
|
5177
|
-
var defaultRemoteBranch = (
|
|
5178
|
-
var resolveBase = (
|
|
5179
|
-
if (declared !== void 0 &&
|
|
5317
|
+
var defaultRemoteBranch = (git2) => nonEmpty(git2.out(["rev-parse", "--abbrev-ref", "origin/HEAD"])?.trim());
|
|
5318
|
+
var resolveBase = (git2, declared) => {
|
|
5319
|
+
if (declared !== void 0 && git2.out(["rev-parse", "--verify", "-q", `${declared}^{commit}`]) !== void 0) {
|
|
5180
5320
|
return declared;
|
|
5181
5321
|
}
|
|
5182
|
-
const remote = defaultRemoteBranch(
|
|
5183
|
-
const merged2 = remote === void 0 ? void 0 :
|
|
5184
|
-
return nonEmpty(merged2) ?? nonEmpty(headSha(
|
|
5322
|
+
const remote = defaultRemoteBranch(git2);
|
|
5323
|
+
const merged2 = remote === void 0 ? void 0 : git2.out(["merge-base", "HEAD", remote])?.trim();
|
|
5324
|
+
return nonEmpty(merged2) ?? nonEmpty(headSha(git2)) ?? EMPTY_TREE;
|
|
5185
5325
|
};
|
|
5186
|
-
var nulFields = (
|
|
5187
|
-
var untracked = (
|
|
5188
|
-
var parseNameStatus = (
|
|
5189
|
-
const fields = nulFields(
|
|
5326
|
+
var nulFields = (text8) => text8.split("\0").filter((part2) => part2 !== "");
|
|
5327
|
+
var untracked = (git2) => nulFields(git2.out(["ls-files", "--others", "--exclude-standard", "-z", "--", ".", EXCLUDE_FM]) ?? "");
|
|
5328
|
+
var parseNameStatus = (text8) => {
|
|
5329
|
+
const fields = nulFields(text8);
|
|
5190
5330
|
const changes = [];
|
|
5191
5331
|
for (let index = 0; index < fields.length; index += 1) {
|
|
5192
5332
|
const code = fields[index] ?? "";
|
|
@@ -5204,21 +5344,21 @@ var parseNameStatus = (text7) => {
|
|
|
5204
5344
|
}
|
|
5205
5345
|
return changes;
|
|
5206
5346
|
};
|
|
5207
|
-
var changedFiles = (
|
|
5347
|
+
var changedFiles = (git2, base) => {
|
|
5208
5348
|
const tracked = parseNameStatus(
|
|
5209
|
-
|
|
5349
|
+
git2.out(["diff", "--name-status", "-M", "-z", "--no-color", base, "--", ".", EXCLUDE_FM]) ?? ""
|
|
5210
5350
|
);
|
|
5211
5351
|
const known2 = new Set(tracked.map((change) => change.path));
|
|
5212
|
-
const fresh3 = untracked(
|
|
5352
|
+
const fresh3 = untracked(git2).filter((path) => !known2.has(path)).map((path) => ({ status: "A", path }));
|
|
5213
5353
|
return [...tracked, ...fresh3];
|
|
5214
5354
|
};
|
|
5215
|
-
var fileAt = (
|
|
5216
|
-
var treeHash = (
|
|
5217
|
-
const head = headSha(
|
|
5218
|
-
const diff =
|
|
5355
|
+
var fileAt = (git2, rev, path) => rev === EMPTY_TREE ? void 0 : git2.out(["show", `${rev}:${path}`]);
|
|
5356
|
+
var treeHash = (git2) => {
|
|
5357
|
+
const head = headSha(git2) ?? EMPTY_TREE;
|
|
5358
|
+
const diff = git2.run(["diff", "HEAD", "--binary", "--no-ext-diff", "--no-color", "--", ".", EXCLUDE_FM]);
|
|
5219
5359
|
if (!diff.found || diff.exitCode !== 0 && head !== EMPTY_TREE) return void 0;
|
|
5220
|
-
const files = untracked(
|
|
5221
|
-
const hashes = files.length === 0 ? "" :
|
|
5360
|
+
const files = untracked(git2);
|
|
5361
|
+
const hashes = files.length === 0 ? "" : git2.out(["hash-object", "--", ...files]) ?? "";
|
|
5222
5362
|
const digest = sha256Hex([head, diff.stdout, files.join("\0"), hashes].join("\n--fm--\n"));
|
|
5223
5363
|
return `w${digest.slice(0, 16)}`;
|
|
5224
5364
|
};
|
|
@@ -5237,29 +5377,29 @@ var parseAddedLines = (diff) => {
|
|
|
5237
5377
|
}
|
|
5238
5378
|
return added;
|
|
5239
5379
|
};
|
|
5240
|
-
var addedLines = (
|
|
5241
|
-
var numstat = (
|
|
5242
|
-
const rows = (
|
|
5380
|
+
var addedLines = (git2, base) => parseAddedLines(git2.out(["diff", "-U0", "--no-color", "--no-ext-diff", base, "--", ".", EXCLUDE_FM]) ?? "");
|
|
5381
|
+
var numstat = (git2, base) => {
|
|
5382
|
+
const rows = (git2.out(["diff", "--numstat", "--no-color", base, "--", ".", EXCLUDE_FM]) ?? "").split("\n").filter((row) => row.trim() !== "");
|
|
5243
5383
|
const lines3 = rows.reduce((sum, row) => {
|
|
5244
5384
|
const [plus = "0", minus = "0"] = row.split(" ");
|
|
5245
5385
|
return sum + (Number(plus) || 0) + (Number(minus) || 0);
|
|
5246
5386
|
}, 0);
|
|
5247
5387
|
return { files: rows.length, lines: lines3 };
|
|
5248
5388
|
};
|
|
5249
|
-
var dirtyPaths = (
|
|
5250
|
-
var aheadOfUpstream = (
|
|
5251
|
-
const count4 =
|
|
5389
|
+
var dirtyPaths = (git2) => nulFields(git2.out(["status", "--porcelain", "-z", "--", ".", EXCLUDE_FM]) ?? "").map((entry2) => entry2.slice(3));
|
|
5390
|
+
var aheadOfUpstream = (git2) => {
|
|
5391
|
+
const count4 = git2.out(["rev-list", "--count", "@{u}..HEAD"])?.trim();
|
|
5252
5392
|
return count4 === void 0 ? void 0 : Number(count4);
|
|
5253
5393
|
};
|
|
5254
5394
|
|
|
5255
5395
|
// src/gate/diff-shape.ts
|
|
5256
5396
|
var untrackedLines = (runtime, root, changes) => changes.filter((change) => change.status === "A").reduce((sum, change) => {
|
|
5257
|
-
const
|
|
5258
|
-
return sum + (
|
|
5397
|
+
const text8 = runtime.readText(toAbsolutePath(join14(root, change.path)));
|
|
5398
|
+
return sum + (text8.ok ? text8.value.split("\n").length : 0);
|
|
5259
5399
|
}, 0);
|
|
5260
|
-
var diffShape = (runtime,
|
|
5400
|
+
var diffShape = (runtime, git2, base, changes) => ({
|
|
5261
5401
|
files: changes.map((change) => change.path),
|
|
5262
|
-
lines: numstat(
|
|
5402
|
+
lines: numstat(git2, base).lines + untrackedLines(runtime, git2.cwd, changes)
|
|
5263
5403
|
});
|
|
5264
5404
|
|
|
5265
5405
|
// src/gate/pr-parse.ts
|
|
@@ -5278,8 +5418,8 @@ var ciChecks = (value) => Array.isArray(value) ? value.flatMap((item2) => {
|
|
|
5278
5418
|
const name = optionalString(item2, "name") ?? optionalString(item2, "context") ?? "?";
|
|
5279
5419
|
return [{ name, state: ciState(item2) }];
|
|
5280
5420
|
}) : [];
|
|
5281
|
-
var parsePrView = (
|
|
5282
|
-
const parsed = parseJsonObject(
|
|
5421
|
+
var parsePrView = (text8) => {
|
|
5422
|
+
const parsed = parseJsonObject(text8);
|
|
5283
5423
|
if (!parsed.ok) return void 0;
|
|
5284
5424
|
const record3 = parsed.value;
|
|
5285
5425
|
const number2 = record3["number"];
|
|
@@ -5319,11 +5459,11 @@ var cached = (runtime, paths, branch, ttlMs) => {
|
|
|
5319
5459
|
const view2 = raw["view"];
|
|
5320
5460
|
return typeof view2 === "string" ? parsePrView(view2) : void 0;
|
|
5321
5461
|
};
|
|
5322
|
-
var fetchView = (runtime,
|
|
5323
|
-
const auth = ghAuthenticated(runtime,
|
|
5462
|
+
var fetchView = (runtime, git2, branch) => {
|
|
5463
|
+
const auth = ghAuthenticated(runtime, git2.cwd);
|
|
5324
5464
|
if (auth === "timeout") return { kind: "network", detail: "gh auth status timed out" };
|
|
5325
5465
|
if (auth === "no") return { kind: "no_gh" };
|
|
5326
|
-
const view2 = gh(runtime,
|
|
5466
|
+
const view2 = gh(runtime, git2.cwd, ["pr", "view", branch, "--json", PR_FIELDS]);
|
|
5327
5467
|
if (view2.timedOut) return { kind: "network", detail: "gh pr view timed out" };
|
|
5328
5468
|
if (view2.exitCode !== 0) {
|
|
5329
5469
|
if (/no (open )?pull requests? found|no git remotes/i.test(view2.stderr)) return { kind: "none", branch };
|
|
@@ -5332,20 +5472,20 @@ var fetchView = (runtime, git, branch) => {
|
|
|
5332
5472
|
const pr = parsePrView(view2.stdout);
|
|
5333
5473
|
return pr === void 0 ? { kind: "network", detail: "gh returned unexpected JSON" } : { kind: "fetched", branch, pr, view: view2.stdout };
|
|
5334
5474
|
};
|
|
5335
|
-
var prStatus = (runtime,
|
|
5336
|
-
const branch = currentBranch(
|
|
5475
|
+
var prStatus = (runtime, git2, paths, ttlMs = PR_CACHE_TTL_MS) => {
|
|
5476
|
+
const branch = currentBranch(git2);
|
|
5337
5477
|
if (branch === void 0) return { kind: "no_branch" };
|
|
5338
5478
|
const hit3 = cached(runtime, paths, branch, ttlMs);
|
|
5339
|
-
if (hit3 !== void 0) return { kind: "found", branch, pr: hit3, localHead: headSha(
|
|
5340
|
-
const status = fetchView(runtime,
|
|
5479
|
+
if (hit3 !== void 0) return { kind: "found", branch, pr: hit3, localHead: headSha(git2) };
|
|
5480
|
+
const status = fetchView(runtime, git2, branch);
|
|
5341
5481
|
if (status.kind !== "fetched") return status;
|
|
5342
5482
|
writeJsonFile(runtime, paths.cache("pr.json"), { fetchedAt: nowMs(runtime), branch, view: status.view });
|
|
5343
|
-
return { kind: "found", branch, pr: status.pr, localHead: headSha(
|
|
5483
|
+
return { kind: "found", branch, pr: status.pr, localHead: headSha(git2) };
|
|
5344
5484
|
};
|
|
5345
|
-
var conflictFiles = (
|
|
5485
|
+
var conflictFiles = (git2, base) => {
|
|
5346
5486
|
for (const target2 of [`origin/${base}`, base]) {
|
|
5347
|
-
if (
|
|
5348
|
-
const result =
|
|
5487
|
+
if (git2.out(["rev-parse", "--verify", "-q", `${target2}^{commit}`]) === void 0) continue;
|
|
5488
|
+
const result = git2.run(["merge-tree", "--write-tree", "--name-only", "--no-messages", target2, "HEAD"]);
|
|
5349
5489
|
if (result.found && result.exitCode === 1) return parseMergeTreeConflicts(result.stdout);
|
|
5350
5490
|
if (result.found && result.exitCode === 0) return [];
|
|
5351
5491
|
}
|
|
@@ -5460,20 +5600,20 @@ var applySize = (contract, verdict) => {
|
|
|
5460
5600
|
// src/gate/prepare.ts
|
|
5461
5601
|
var diskFiles = (runtime, root) => ({
|
|
5462
5602
|
read: (relative11) => {
|
|
5463
|
-
const
|
|
5464
|
-
return
|
|
5603
|
+
const text8 = runtime.readText(toAbsolutePath(join15(root, relative11)));
|
|
5604
|
+
return text8.ok ? text8.value : void 0;
|
|
5465
5605
|
},
|
|
5466
5606
|
exists: (relative11) => runtime.stat(toAbsolutePath(join15(root, relative11))) !== void 0
|
|
5467
5607
|
});
|
|
5468
|
-
var baseFiles = (
|
|
5469
|
-
read: (relative11) => base === EMPTY_TREE ? disk.read(relative11) : fileAt(
|
|
5608
|
+
var baseFiles = (git2, base, disk) => ({
|
|
5609
|
+
read: (relative11) => base === EMPTY_TREE ? disk.read(relative11) : fileAt(git2, base, relative11),
|
|
5470
5610
|
exists: disk.exists
|
|
5471
5611
|
});
|
|
5472
|
-
var loadContract = (options, paths,
|
|
5612
|
+
var loadContract = (options, paths, git2, base) => {
|
|
5473
5613
|
const disk = diskFiles(options.runtime, paths.root);
|
|
5474
5614
|
const id = activeTaskId(options.runtime, paths, options.taskId);
|
|
5475
|
-
if (id === void 0) return accept(loadImplicit(options.runtime, paths, baseFiles(
|
|
5476
|
-
return loadTask(options.runtime, paths, { disk: disk.read, base: (relative11) => fileAt(
|
|
5615
|
+
if (id === void 0) return accept(loadImplicit(options.runtime, paths, baseFiles(git2, base, disk)));
|
|
5616
|
+
return loadTask(options.runtime, paths, { disk: disk.read, base: (relative11) => fileAt(git2, base, relative11) }, id);
|
|
5477
5617
|
};
|
|
5478
5618
|
var memo = (make) => {
|
|
5479
5619
|
let value;
|
|
@@ -5495,30 +5635,30 @@ var skipReason = (runtime, paths, loaded, changes, tree, sessionId) => {
|
|
|
5495
5635
|
const session = readSession(runtime, paths);
|
|
5496
5636
|
return session !== void 0 && session.sessionId === sessionId && session.startTree === tree ? "no_session_edits" : void 0;
|
|
5497
5637
|
};
|
|
5498
|
-
var measureSize = (runtime,
|
|
5638
|
+
var measureSize = (runtime, git2, base, changes, contract) => sizeVerdict(contract.size, diffShape(runtime, git2, base, changes), sizeConfig(loadProjectConfig(runtime, git2.cwd)));
|
|
5499
5639
|
var prepareGate = (options) => {
|
|
5500
5640
|
const probe = gitAt(options.runtime, options.cwd);
|
|
5501
5641
|
const root = gitRoot(probe);
|
|
5502
5642
|
if (root === void 0) {
|
|
5503
5643
|
return options.taskId === void 0 ? { kind: "skipped", reason: "no_repo" } : { kind: "failed", failure: { kind: "not_a_repo" } };
|
|
5504
5644
|
}
|
|
5505
|
-
const
|
|
5645
|
+
const git2 = gitAt(options.runtime, root);
|
|
5506
5646
|
const paths = fmPaths(root);
|
|
5507
5647
|
const trust2 = readTrust(options.runtime, paths);
|
|
5508
|
-
const firstBase = resolveBase(
|
|
5509
|
-
const loaded = loadContract(options, paths,
|
|
5648
|
+
const firstBase = resolveBase(git2, void 0);
|
|
5649
|
+
const loaded = loadContract(options, paths, git2, firstBase);
|
|
5510
5650
|
if (!loaded.ok) return { kind: "failed", failure: { kind: "contract", problem: loaded.error } };
|
|
5511
|
-
const base = resolveBase(
|
|
5512
|
-
const changes = changedFiles(
|
|
5513
|
-
const tree = treeHash(
|
|
5651
|
+
const base = resolveBase(git2, loaded.value.contract.base);
|
|
5652
|
+
const changes = changedFiles(git2, base);
|
|
5653
|
+
const tree = treeHash(git2);
|
|
5514
5654
|
if (tree === void 0) return { kind: "failed", failure: { kind: "no_tree_hash" } };
|
|
5515
5655
|
const skip = options.noSkip === true ? void 0 : skipReason(options.runtime, paths, loaded.value, changes, tree, options.sessionId);
|
|
5516
5656
|
if (skip !== void 0) return { kind: "skipped", reason: skip };
|
|
5517
|
-
const pr = memo(() => prStatus(options.runtime,
|
|
5518
|
-
const size = measureSize(options.runtime,
|
|
5657
|
+
const pr = memo(() => prStatus(options.runtime, git2, paths));
|
|
5658
|
+
const size = measureSize(options.runtime, git2, base, changes, loaded.value.contract);
|
|
5519
5659
|
const ctx = {
|
|
5520
5660
|
runtime: options.runtime,
|
|
5521
|
-
git,
|
|
5661
|
+
git: git2,
|
|
5522
5662
|
paths,
|
|
5523
5663
|
contract: applySize(loaded.value.contract, size),
|
|
5524
5664
|
size,
|
|
@@ -5582,11 +5722,11 @@ var trust = (input, paths, task) => {
|
|
|
5582
5722
|
});
|
|
5583
5723
|
if (ready.kind !== "ready" || ready.prepared.ctx.contract.id !== task) return false;
|
|
5584
5724
|
const lock = readTrust(runtime, paths);
|
|
5585
|
-
const
|
|
5725
|
+
const text8 = runtime.readText(paths.task(task));
|
|
5586
5726
|
return writeTrust(runtime, paths, {
|
|
5587
5727
|
...lock,
|
|
5588
5728
|
contracts: { ...lock.contracts, [task]: ready.prepared.loaded.hash },
|
|
5589
|
-
approved: task === IMPLICIT_ID || !
|
|
5729
|
+
approved: task === IMPLICIT_ID || !text8.ok ? lock.approved : { ...lock.approved, [task]: text8.value }
|
|
5590
5730
|
}).ok;
|
|
5591
5731
|
};
|
|
5592
5732
|
var apply = (input, paths, markers) => {
|
|
@@ -5638,8 +5778,8 @@ var promptRelease = (input) => {
|
|
|
5638
5778
|
var PRS_CACHE_MS = 10 * 6e4;
|
|
5639
5779
|
var PRS_TIMEOUT_MS = 3e3;
|
|
5640
5780
|
var PRS_AUTH_TIMEOUT_MS = 2e3;
|
|
5641
|
-
var parsePrList = (
|
|
5642
|
-
const parsed = parseJson(
|
|
5781
|
+
var parsePrList = (text8) => {
|
|
5782
|
+
const parsed = parseJson(text8);
|
|
5643
5783
|
if (!parsed.ok || !Array.isArray(parsed.value)) return void 0;
|
|
5644
5784
|
return parsed.value.flatMap((item2) => {
|
|
5645
5785
|
if (!isRecord(item2) || typeof item2["number"] !== "number") return [];
|
|
@@ -6436,7 +6576,16 @@ var en_default = {
|
|
|
6436
6576
|
"reason.label.blocked": "blocked",
|
|
6437
6577
|
"reason.label.requires": "requires",
|
|
6438
6578
|
"reason.label.not": "does not count",
|
|
6439
|
-
"reason.human.generic": "Blocked a tool call; the reason went to Claude."
|
|
6579
|
+
"reason.human.generic": "Blocked a tool call; the reason went to Claude.",
|
|
6580
|
+
"review.command.packet": "Print the review packet for the reviewer and verifier agents: diff, task contract, evidence ledger and REVIEW.md, never the conversation",
|
|
6581
|
+
"review.guard.read_only": "blocked: the {agent} agent is read-only and cannot use {tool}. Report the finding; the main session makes the change.",
|
|
6582
|
+
"review.error.no_base": "the base {detail} is not a commit here",
|
|
6583
|
+
"command.statusline.summary": "Print the status line: active task, gate state and context use",
|
|
6584
|
+
"statusline.gate.green": "gate green",
|
|
6585
|
+
"statusline.gate.red": "gate red",
|
|
6586
|
+
"statusline.gate.incomplete": "gate incomplete",
|
|
6587
|
+
"statusline.gate.did_not_run": "gate did not run",
|
|
6588
|
+
"statusline.context": "ctx {percent}%"
|
|
6440
6589
|
};
|
|
6441
6590
|
|
|
6442
6591
|
// src/i18n/es.json
|
|
@@ -7085,7 +7234,16 @@ var es_default = {
|
|
|
7085
7234
|
"reason.label.blocked": "bloqueado",
|
|
7086
7235
|
"reason.label.requires": "exige",
|
|
7087
7236
|
"reason.label.not": "no cuenta",
|
|
7088
|
-
"reason.human.generic": "Bloqueé una llamada de herramienta; el motivo fue a Claude."
|
|
7237
|
+
"reason.human.generic": "Bloqueé una llamada de herramienta; el motivo fue a Claude.",
|
|
7238
|
+
"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",
|
|
7239
|
+
"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.",
|
|
7240
|
+
"review.error.no_base": "la base {detail} no es un commit aquí",
|
|
7241
|
+
"command.statusline.summary": "Imprime la línea de estado: tarea activa, estado de la compuerta y uso del contexto",
|
|
7242
|
+
"statusline.gate.green": "compuerta verde",
|
|
7243
|
+
"statusline.gate.red": "compuerta roja",
|
|
7244
|
+
"statusline.gate.incomplete": "compuerta incompleta",
|
|
7245
|
+
"statusline.gate.did_not_run": "la compuerta no se ejecutó",
|
|
7246
|
+
"statusline.context": "ctx {percent}%"
|
|
7089
7247
|
};
|
|
7090
7248
|
|
|
7091
7249
|
// src/i18n/pt-BR.json
|
|
@@ -7734,7 +7892,16 @@ var pt_BR_default = {
|
|
|
7734
7892
|
"reason.label.blocked": "bloqueado",
|
|
7735
7893
|
"reason.label.requires": "exige",
|
|
7736
7894
|
"reason.label.not": "não vale",
|
|
7737
|
-
"reason.human.generic": "Bloqueei uma chamada de ferramenta; o motivo foi para o Claude."
|
|
7895
|
+
"reason.human.generic": "Bloqueei uma chamada de ferramenta; o motivo foi para o Claude.",
|
|
7896
|
+
"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",
|
|
7897
|
+
"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.",
|
|
7898
|
+
"review.error.no_base": "a base {detail} não é um commit aqui",
|
|
7899
|
+
"command.statusline.summary": "Imprime a linha de status: tarefa ativa, estado do portão e uso do contexto",
|
|
7900
|
+
"statusline.gate.green": "portão verde",
|
|
7901
|
+
"statusline.gate.red": "portão vermelho",
|
|
7902
|
+
"statusline.gate.incomplete": "portão incompleto",
|
|
7903
|
+
"statusline.gate.did_not_run": "portão não rodou",
|
|
7904
|
+
"statusline.context": "ctx {percent}%"
|
|
7738
7905
|
};
|
|
7739
7906
|
|
|
7740
7907
|
// src/i18n/review.json
|
|
@@ -8384,7 +8551,16 @@ var review_default = {
|
|
|
8384
8551
|
"reason.label.blocked": "machine",
|
|
8385
8552
|
"reason.label.requires": "machine",
|
|
8386
8553
|
"reason.label.not": "machine",
|
|
8387
|
-
"reason.human.generic": "machine"
|
|
8554
|
+
"reason.human.generic": "machine",
|
|
8555
|
+
"review.command.packet": "machine",
|
|
8556
|
+
"review.guard.read_only": "machine",
|
|
8557
|
+
"review.error.no_base": "machine",
|
|
8558
|
+
"command.statusline.summary": "machine",
|
|
8559
|
+
"statusline.gate.green": "machine",
|
|
8560
|
+
"statusline.gate.red": "machine",
|
|
8561
|
+
"statusline.gate.incomplete": "machine",
|
|
8562
|
+
"statusline.gate.did_not_run": "machine",
|
|
8563
|
+
"statusline.context": "machine"
|
|
8388
8564
|
},
|
|
8389
8565
|
es: {
|
|
8390
8566
|
"command.init.summary": "machine",
|
|
@@ -9031,7 +9207,16 @@ var review_default = {
|
|
|
9031
9207
|
"reason.label.blocked": "machine",
|
|
9032
9208
|
"reason.label.requires": "machine",
|
|
9033
9209
|
"reason.label.not": "machine",
|
|
9034
|
-
"reason.human.generic": "machine"
|
|
9210
|
+
"reason.human.generic": "machine",
|
|
9211
|
+
"review.command.packet": "machine",
|
|
9212
|
+
"review.guard.read_only": "machine",
|
|
9213
|
+
"review.error.no_base": "machine",
|
|
9214
|
+
"command.statusline.summary": "machine",
|
|
9215
|
+
"statusline.gate.green": "machine",
|
|
9216
|
+
"statusline.gate.red": "machine",
|
|
9217
|
+
"statusline.gate.incomplete": "machine",
|
|
9218
|
+
"statusline.gate.did_not_run": "machine",
|
|
9219
|
+
"statusline.context": "machine"
|
|
9035
9220
|
}
|
|
9036
9221
|
};
|
|
9037
9222
|
|
|
@@ -9262,9 +9447,9 @@ var notRunLine = (t, runtime, paths) => {
|
|
|
9262
9447
|
var markSession = (input, paths) => {
|
|
9263
9448
|
const { runtime } = input;
|
|
9264
9449
|
const sessionId = input.payload.sessionId;
|
|
9265
|
-
const
|
|
9266
|
-
if (sessionId === void 0 || headSha(
|
|
9267
|
-
const tree = treeHash(
|
|
9450
|
+
const git2 = gitAt(runtime, paths.root);
|
|
9451
|
+
if (sessionId === void 0 || headSha(git2) === void 0) return;
|
|
9452
|
+
const tree = treeHash(git2);
|
|
9268
9453
|
if (tree === void 0) return;
|
|
9269
9454
|
writeSession(runtime, paths, { sessionId, startTree: tree, at: nowMs(runtime) });
|
|
9270
9455
|
startBaseline(runtime, paths, tree);
|
|
@@ -9321,10 +9506,10 @@ var pairs = (record3) => {
|
|
|
9321
9506
|
return names.filter((name) => typeof name === "string").map((name) => `${task}\0${name}`);
|
|
9322
9507
|
};
|
|
9323
9508
|
var callersRate = (runtime, paths) => {
|
|
9324
|
-
const
|
|
9509
|
+
const text8 = runtime.readText(paths.log);
|
|
9325
9510
|
const flagged = /* @__PURE__ */ new Set();
|
|
9326
9511
|
const released2 = /* @__PURE__ */ new Set();
|
|
9327
|
-
for (const line2 of
|
|
9512
|
+
for (const line2 of text8.ok ? text8.value.split("\n") : []) {
|
|
9328
9513
|
const parsed = line2.trim() === "" ? void 0 : parseJsonObject(line2);
|
|
9329
9514
|
if (parsed === void 0 || !parsed.ok || !isRecord(parsed.value)) continue;
|
|
9330
9515
|
const event = parsed.value["event"];
|
|
@@ -9349,7 +9534,7 @@ import { posix as posix2 } from "node:path";
|
|
|
9349
9534
|
var JS_SPECIFIER = /(?:\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)["']([^"']+)["']/g;
|
|
9350
9535
|
var JS_EXTENSION = /\.(?:[cm]?[jt]sx?)$/;
|
|
9351
9536
|
var stripModule = (path) => path.replace(JS_EXTENSION, "").replace(/\/index$/, "");
|
|
9352
|
-
var jsSpecifiers = (
|
|
9537
|
+
var jsSpecifiers = (text8) => [...text8.matchAll(JS_SPECIFIER)].map((match) => match[1] ?? "");
|
|
9353
9538
|
var jsImports = (callerPath, callerText, origin) => {
|
|
9354
9539
|
const target2 = stripModule(origin);
|
|
9355
9540
|
return jsSpecifiers(callerText).some((specifier) => {
|
|
@@ -9451,9 +9636,9 @@ var languageOf = (path) => {
|
|
|
9451
9636
|
const extension = extname(path).toLowerCase();
|
|
9452
9637
|
return LANGUAGES2.find((language) => language.extensions.includes(extension));
|
|
9453
9638
|
};
|
|
9454
|
-
var definitionsIn = (language,
|
|
9639
|
+
var definitionsIn = (language, text8) => {
|
|
9455
9640
|
const names = /* @__PURE__ */ new Set();
|
|
9456
|
-
for (const line2 of
|
|
9641
|
+
for (const line2 of text8.split(/\r?\n/)) {
|
|
9457
9642
|
for (const pattern of language.definitions) {
|
|
9458
9643
|
const name = pattern.exec(line2)?.[1];
|
|
9459
9644
|
if (name !== void 0 && !KEYWORDS.has(name)) names.add(name);
|
|
@@ -9466,26 +9651,26 @@ var definesName = (language, line2, name) => language.definitions.some((pattern)
|
|
|
9466
9651
|
// src/gate/callers/scan.ts
|
|
9467
9652
|
var MAX_SYMBOLS = 200;
|
|
9468
9653
|
var current = (runtime, root, path) => {
|
|
9469
|
-
const
|
|
9470
|
-
return
|
|
9654
|
+
const text8 = runtime.readText(toAbsolutePath(join16(root, path)));
|
|
9655
|
+
return text8.ok ? text8.value : "";
|
|
9471
9656
|
};
|
|
9472
9657
|
var keyOf = (language, name) => `${language.id}\0${name}`;
|
|
9473
|
-
var redefinedNames = (runtime,
|
|
9658
|
+
var redefinedNames = (runtime, git2, changes) => {
|
|
9474
9659
|
const keys = /* @__PURE__ */ new Set();
|
|
9475
9660
|
for (const change of changes) {
|
|
9476
9661
|
const language = languageOf(change.path);
|
|
9477
9662
|
if (language === void 0 || change.status === "D") continue;
|
|
9478
|
-
for (const name of definitionsIn(language, current(runtime,
|
|
9663
|
+
for (const name of definitionsIn(language, current(runtime, git2.cwd, change.path))) keys.add(keyOf(language, name));
|
|
9479
9664
|
}
|
|
9480
9665
|
return keys;
|
|
9481
9666
|
};
|
|
9482
|
-
var removedSymbols = (runtime,
|
|
9483
|
-
const redefined = redefinedNames(runtime,
|
|
9667
|
+
var removedSymbols = (runtime, git2, base, changes) => {
|
|
9668
|
+
const redefined = redefinedNames(runtime, git2, changes);
|
|
9484
9669
|
const found = /* @__PURE__ */ new Map();
|
|
9485
9670
|
for (const change of changes) {
|
|
9486
9671
|
const language = languageOf(change.oldPath ?? change.path);
|
|
9487
9672
|
if (language === void 0 || change.status === "A") continue;
|
|
9488
|
-
const before = definitionsIn(language, fileAt(
|
|
9673
|
+
const before = definitionsIn(language, fileAt(git2, base, change.oldPath ?? change.path) ?? "");
|
|
9489
9674
|
const origins = [change.path, ...change.oldPath === void 0 ? [] : [change.oldPath]];
|
|
9490
9675
|
for (const name of before) {
|
|
9491
9676
|
const key = keyOf(language, name);
|
|
@@ -9501,21 +9686,21 @@ var parseGrep = (output) => output.split("\n").filter((row) => row !== "").flatM
|
|
|
9501
9686
|
const number2 = Number(line2);
|
|
9502
9687
|
return file === "" || !Number.isInteger(number2) ? [] : [{ file: toPosixPath(file), line: number2, text: rest.join("\0") }];
|
|
9503
9688
|
});
|
|
9504
|
-
var grep = (
|
|
9689
|
+
var grep = (git2, names) => {
|
|
9505
9690
|
if (names.length === 0) return [];
|
|
9506
9691
|
const patterns = names.flatMap((name) => ["-e", name]);
|
|
9507
9692
|
return parseGrep(
|
|
9508
|
-
|
|
9693
|
+
git2.out(["grep", "-n", "-z", "-w", "-I", "--untracked", "--no-color", ...patterns, "--", ".", EXCLUDE_FM]) ?? ""
|
|
9509
9694
|
);
|
|
9510
9695
|
};
|
|
9511
|
-
var mentions = (
|
|
9512
|
-
var callSites = (runtime,
|
|
9513
|
-
const hits = grep(
|
|
9696
|
+
var mentions = (text8, name) => new RegExp(`(?<![\\w$])${name.replace(/[$?!]/g, "\\$&")}(?![\\w$])`).test(text8);
|
|
9697
|
+
var callSites = (runtime, git2, symbols) => {
|
|
9698
|
+
const hits = grep(git2, [...new Set(symbols.map((symbol) => symbol.name))]);
|
|
9514
9699
|
const texts2 = /* @__PURE__ */ new Map();
|
|
9515
9700
|
const textOf4 = (file) => {
|
|
9516
|
-
const
|
|
9517
|
-
texts2.set(file,
|
|
9518
|
-
return
|
|
9701
|
+
const cached3 = texts2.get(file) ?? current(runtime, git2.cwd, file);
|
|
9702
|
+
texts2.set(file, cached3);
|
|
9703
|
+
return cached3;
|
|
9519
9704
|
};
|
|
9520
9705
|
return symbols.flatMap(
|
|
9521
9706
|
(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 }))
|
|
@@ -9583,31 +9768,31 @@ var ZERO = [
|
|
|
9583
9768
|
/^ℹ tests 0$/m,
|
|
9584
9769
|
/No tests found/
|
|
9585
9770
|
];
|
|
9586
|
-
var pyTotal = (
|
|
9587
|
-
const line2 = /^=+ (.*) in [\d.]+s.*=+$/m.exec(
|
|
9771
|
+
var pyTotal = (text8) => {
|
|
9772
|
+
const line2 = /^=+ (.*) in [\d.]+s.*=+$/m.exec(text8)?.[1];
|
|
9588
9773
|
if (line2 === void 0) return void 0;
|
|
9589
9774
|
return [...line2.matchAll(/(\d+) (passed|failed|error|errors|skipped|xfailed|xpassed)/g)].reduce(
|
|
9590
9775
|
(sum, match) => sum + Number(match[1] ?? "0"),
|
|
9591
9776
|
0
|
|
9592
9777
|
);
|
|
9593
9778
|
};
|
|
9594
|
-
var counted = (
|
|
9779
|
+
var counted = (text8) => {
|
|
9595
9780
|
for (const counter of COUNTERS) {
|
|
9596
|
-
const total = counter.total.exec(
|
|
9781
|
+
const total = counter.total.exec(text8)?.[1];
|
|
9597
9782
|
if (total === void 0) continue;
|
|
9598
|
-
const failed4 = counter.failed?.exec(
|
|
9783
|
+
const failed4 = counter.failed?.exec(text8)?.[1];
|
|
9599
9784
|
return { total: Number(total), ...failed4 === void 0 ? {} : { failed: Number(failed4) } };
|
|
9600
9785
|
}
|
|
9601
|
-
const py = pyTotal(
|
|
9786
|
+
const py = pyTotal(text8);
|
|
9602
9787
|
return py === void 0 ? {} : { total: py };
|
|
9603
9788
|
};
|
|
9604
9789
|
var parseTestSummary = (output) => {
|
|
9605
|
-
const
|
|
9790
|
+
const text8 = output.replace(ANSI2, "");
|
|
9606
9791
|
const failures = [
|
|
9607
|
-
...new Set(FAILURE_LINES.flatMap((pattern) => [...
|
|
9792
|
+
...new Set(FAILURE_LINES.flatMap((pattern) => [...text8.matchAll(pattern)].map((match) => (match[1] ?? "").trim())))
|
|
9608
9793
|
].filter((name) => name !== "" && !/^failing tests:?$/i.test(name));
|
|
9609
|
-
if (ZERO.some((pattern) => pattern.test(
|
|
9610
|
-
return { ...counted(
|
|
9794
|
+
if (ZERO.some((pattern) => pattern.test(text8))) return { total: 0, failures };
|
|
9795
|
+
return { ...counted(text8), failures };
|
|
9611
9796
|
};
|
|
9612
9797
|
|
|
9613
9798
|
// src/gate/checks/command.ts
|
|
@@ -9618,8 +9803,8 @@ var REWRITERS = /^\s*(rtk)\b/;
|
|
|
9618
9803
|
var TRIVIAL = /* @__PURE__ */ new Set(["echo", "true", ":", "printf", "exit", "sleep", "cat", "yes", "pwd", "ls", "date", "env"]);
|
|
9619
9804
|
var TAIL = 160;
|
|
9620
9805
|
var MAX_FAILURES2 = 5;
|
|
9621
|
-
var tail = (
|
|
9622
|
-
const lines3 =
|
|
9806
|
+
var tail = (text8) => {
|
|
9807
|
+
const lines3 = text8.split(/\r?\n/).filter((line2) => line2.trim() !== "");
|
|
9623
9808
|
const last = lines3[lines3.length - 1]?.trim() ?? "";
|
|
9624
9809
|
return last.length > TAIL ? `${last.slice(0, TAIL)}…` : last;
|
|
9625
9810
|
};
|
|
@@ -9833,8 +10018,8 @@ var findingNote = (value) => {
|
|
|
9833
10018
|
];
|
|
9834
10019
|
};
|
|
9835
10020
|
var readSummary = (ctx) => {
|
|
9836
|
-
const
|
|
9837
|
-
const parsed =
|
|
10021
|
+
const text8 = ctx.runtime.readText(summaryPath(ctx.paths, ctx.treeHash));
|
|
10022
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
9838
10023
|
if (!parsed?.ok) return void 0;
|
|
9839
10024
|
const { at, captures, findings, ignoredAllows } = parsed.value;
|
|
9840
10025
|
if (typeof at !== "string" || !Array.isArray(captures) || !Array.isArray(findings)) return void 0;
|
|
@@ -9903,8 +10088,8 @@ var SKIP_FILES = /\.(md|mdx|txt|rst|lock|snap|svg|png|jpg|gif|json)$|(^|\/)(CHAN
|
|
|
9903
10088
|
var PLACEHOLDER = /\b(TODO|FIXME|XXX)\b|not implemented|NotImplementedError|unimplemented!|todo!\(|lorem ipsum/i;
|
|
9904
10089
|
var findPlaceholders = (lines3) => lines3.filter((line2) => !isTestFile(line2.path) && !SKIP_FILES.test(line2.path) && PLACEHOLDER.test(line2.text));
|
|
9905
10090
|
var untrackedAdded = (ctx) => ctx.changes.filter((change) => change.status === "A" && !SKIP_FILES.test(change.path)).flatMap((change) => {
|
|
9906
|
-
const
|
|
9907
|
-
return
|
|
10091
|
+
const text8 = ctx.runtime.readText(toAbsolutePath(join20(ctx.paths.root, change.path)));
|
|
10092
|
+
return text8.ok ? text8.value.split("\n").map((line2, index) => ({ path: change.path, line: index + 1, text: line2 })) : [];
|
|
9908
10093
|
});
|
|
9909
10094
|
var placeholdersCheck = (_check, ctx) => {
|
|
9910
10095
|
const tracked = addedLines(ctx.git, ctx.base);
|
|
@@ -9963,7 +10148,7 @@ var prOutcome = (check2, ctx) => {
|
|
|
9963
10148
|
|
|
9964
10149
|
// src/prose/preset.ts
|
|
9965
10150
|
var PRESET_LINE = /^preset:\s*([A-Za-z0-9_-]+)\s*$/m;
|
|
9966
|
-
var presetFromConfig = (
|
|
10151
|
+
var presetFromConfig = (text8) => parsePreset(PRESET_LINE.exec(text8)?.[1]) ?? "neutral";
|
|
9967
10152
|
var projectPreset = (runtime, root) => {
|
|
9968
10153
|
const config = runtime.readText(toAbsolutePath(CONFIG_FILE, root));
|
|
9969
10154
|
return config.ok ? presetFromConfig(config.value) : "neutral";
|
|
@@ -10140,16 +10325,16 @@ var ANY_MARKER = /<!--\s*finitemonkey:prose-allow(?:-file)?\s*-->/g;
|
|
|
10140
10325
|
var INLINE_CODE = /`[^`]*`/g;
|
|
10141
10326
|
var FENCE2 = /^\s*(```|~~~)/;
|
|
10142
10327
|
var outsideCode = (line2) => line2.replace(INLINE_CODE, "");
|
|
10143
|
-
var unfencedLines = (
|
|
10328
|
+
var unfencedLines = (text8) => {
|
|
10144
10329
|
let fenced = false;
|
|
10145
|
-
return
|
|
10330
|
+
return text8.split("\n").filter((line2) => {
|
|
10146
10331
|
if (FENCE2.test(line2)) fenced = !fenced;
|
|
10147
10332
|
return !fenced && !FENCE2.test(line2);
|
|
10148
10333
|
});
|
|
10149
10334
|
};
|
|
10150
|
-
var allowsWholeFile = (
|
|
10335
|
+
var allowsWholeFile = (text8) => unfencedLines(text8).some((line2) => FILE_MARKER.test(outsideCode(line2)));
|
|
10151
10336
|
var allowsLine = (line2) => LINE_MARKER.test(outsideCode(line2));
|
|
10152
|
-
var markerCount = (
|
|
10337
|
+
var markerCount = (text8) => text8.match(ANY_MARKER)?.length ?? 0;
|
|
10153
10338
|
var isMarkerOnlyLine = (line2) => allowsLine(line2) && outsideCode(line2).replace(LINE_MARKER, "").trim() === "";
|
|
10154
10339
|
|
|
10155
10340
|
// src/prose/extract.ts
|
|
@@ -10169,7 +10354,7 @@ var MASKS = [
|
|
|
10169
10354
|
[/<[^>]+>/g, ""],
|
|
10170
10355
|
[/\*\*|__/g, ""]
|
|
10171
10356
|
];
|
|
10172
|
-
var maskInline = (line2) => MASKS.reduce((
|
|
10357
|
+
var maskInline = (line2) => MASKS.reduce((text8, [pattern, replacement]) => text8.replace(pattern, replacement), line2);
|
|
10173
10358
|
var BlockReader = class {
|
|
10174
10359
|
blocks = [];
|
|
10175
10360
|
current;
|
|
@@ -10260,14 +10445,14 @@ var splitBlock = (block4) => {
|
|
|
10260
10445
|
return joined.split(SENTENCE_END).flatMap((sentence) => {
|
|
10261
10446
|
const at = joined.indexOf(sentence, offset);
|
|
10262
10447
|
offset = at + sentence.length;
|
|
10263
|
-
const
|
|
10264
|
-
return
|
|
10448
|
+
const text8 = sentence.trim();
|
|
10449
|
+
return text8 === "" ? [] : [{ text: text8, line: lineAt(at) }];
|
|
10265
10450
|
});
|
|
10266
10451
|
};
|
|
10267
10452
|
var unitsOf = (blocks) => {
|
|
10268
|
-
let
|
|
10453
|
+
let section2 = 0;
|
|
10269
10454
|
return blocks.flatMap((block4, paragraph) => {
|
|
10270
|
-
if (block4.kind === "heading")
|
|
10455
|
+
if (block4.kind === "heading") section2 += 1;
|
|
10271
10456
|
const pieces = splitBlock(block4);
|
|
10272
10457
|
const next2 = blocks[paragraph + 1];
|
|
10273
10458
|
const closesSection = block4.kind === "sentence" && (next2 === void 0 || next2.kind === "heading");
|
|
@@ -10275,16 +10460,16 @@ var unitsOf = (blocks) => {
|
|
|
10275
10460
|
text: piece.text,
|
|
10276
10461
|
line: piece.line,
|
|
10277
10462
|
paragraph,
|
|
10278
|
-
section,
|
|
10463
|
+
section: section2,
|
|
10279
10464
|
kind: block4.kind,
|
|
10280
10465
|
lastInParagraph: index === pieces.length - 1,
|
|
10281
10466
|
closesSection
|
|
10282
10467
|
}));
|
|
10283
10468
|
});
|
|
10284
10469
|
};
|
|
10285
|
-
var extractProse = (
|
|
10286
|
-
if (allowsWholeFile(
|
|
10287
|
-
const lines3 =
|
|
10470
|
+
var extractProse = (text8) => {
|
|
10471
|
+
if (allowsWholeFile(text8)) return { units: [], allowed: true };
|
|
10472
|
+
const lines3 = text8.split("\n").map(withoutCarriageReturn);
|
|
10288
10473
|
return { units: unitsOf(readBlocks(lines3)), allowed: false };
|
|
10289
10474
|
};
|
|
10290
10475
|
|
|
@@ -10409,7 +10594,7 @@ var CONNECTIVES = new Set(
|
|
|
10409
10594
|
" "
|
|
10410
10595
|
)
|
|
10411
10596
|
);
|
|
10412
|
-
var wordsOf = (
|
|
10597
|
+
var wordsOf = (text8) => text8.split(/\s+/).filter((word2) => WORD.test(word2));
|
|
10413
10598
|
var hit = (rule, units, index, weight) => {
|
|
10414
10599
|
const unit = units[index];
|
|
10415
10600
|
return { rule, unit: index, line: unit?.line ?? 0, weight, excerpt: unit?.text ?? "" };
|
|
@@ -10503,9 +10688,9 @@ var phraseHits = (units, catalog) => units.flatMap(
|
|
|
10503
10688
|
})
|
|
10504
10689
|
);
|
|
10505
10690
|
var windowEnd = (units, from, size) => {
|
|
10506
|
-
const
|
|
10691
|
+
const section2 = units[from]?.section;
|
|
10507
10692
|
let to = from;
|
|
10508
|
-
while (to + 1 < Math.min(units.length, from + size) && units[to + 1]?.section ===
|
|
10693
|
+
while (to + 1 < Math.min(units.length, from + size) && units[to + 1]?.section === section2) to += 1;
|
|
10509
10694
|
return to;
|
|
10510
10695
|
};
|
|
10511
10696
|
var windowScore = (hits, window) => hits.filter((item2) => item2.unit >= window.from && item2.unit <= window.to).reduce((sum, item2) => sum + item2.weight, 0);
|
|
@@ -10537,8 +10722,8 @@ var mergeSpans = (hits, windows) => {
|
|
|
10537
10722
|
return ranges.map(([from, to]) => spanOf2(hits, from, to)).filter((span) => span.hits.length > 0);
|
|
10538
10723
|
};
|
|
10539
10724
|
var byPosition = (left, right) => left.unit - right.unit || left.line - right.line;
|
|
10540
|
-
var scoreProse = (
|
|
10541
|
-
const { units, allowed: allowed2 } = extractProse(
|
|
10725
|
+
var scoreProse = (text8, options = {}) => {
|
|
10726
|
+
const { units, allowed: allowed2 } = extractProse(text8);
|
|
10542
10727
|
const size = options.window ?? DENSITY_WINDOW;
|
|
10543
10728
|
const threshold2 = options.threshold ?? DENSITY_THRESHOLD;
|
|
10544
10729
|
const catalog = options.context === "record" ? [...phraseRules(), ...recordRules()] : phraseRules();
|
|
@@ -10570,9 +10755,9 @@ var changedMarkdown = (runtime, root) => {
|
|
|
10570
10755
|
var scoreFiles = (runtime, root, files, forced) => {
|
|
10571
10756
|
const reports = [];
|
|
10572
10757
|
for (const file of files) {
|
|
10573
|
-
const
|
|
10574
|
-
if (!
|
|
10575
|
-
reports.push({ file: toPosixPath(file), report: scoreProse(
|
|
10758
|
+
const text8 = runtime.readText(toAbsolutePath(file, root));
|
|
10759
|
+
if (!text8.ok) return reject(`${file}: ${text8.error}`);
|
|
10760
|
+
reports.push({ file: toPosixPath(file), report: scoreProse(text8.value, { context: forced ?? contextOf(file) }) });
|
|
10576
10761
|
}
|
|
10577
10762
|
return accept(reports);
|
|
10578
10763
|
};
|
|
@@ -10671,8 +10856,8 @@ var parseAppCheck = (stdout) => {
|
|
|
10671
10856
|
var PROBE_MARGIN_MS = 15e3;
|
|
10672
10857
|
var MIN_REMAINING_MS2 = 5e3;
|
|
10673
10858
|
var TAIL2 = 160;
|
|
10674
|
-
var lastLine = (
|
|
10675
|
-
const rows =
|
|
10859
|
+
var lastLine = (text8) => {
|
|
10860
|
+
const rows = text8.split(/\r?\n/).filter((row) => row.trim() !== "" && !/^\s*at\s|^Node\.js v\d/.test(row));
|
|
10676
10861
|
const line2 = (rows.findLast((row) => /error/i.test(row)) ?? rows.at(-1))?.trim() ?? "";
|
|
10677
10862
|
return line2.length > TAIL2 ? `${line2.slice(0, TAIL2)}…` : line2;
|
|
10678
10863
|
};
|
|
@@ -11015,12 +11200,12 @@ var diffScanner = () => {
|
|
|
11015
11200
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
11016
11201
|
var SKIPPED = /(?:^|\/)(?:\.git|node_modules|\.finitemonkey)\//;
|
|
11017
11202
|
var DIFF_FLAGS = ["-U0", "--no-color", "--no-ext-diff", "--no-renames"];
|
|
11018
|
-
var binary = (
|
|
11203
|
+
var binary = (text8) => text8.slice(0, 8e3).includes("\0");
|
|
11019
11204
|
var listed2 = (runtime, root, target2) => {
|
|
11020
|
-
const
|
|
11021
|
-
if (
|
|
11205
|
+
const git2 = runtime.git(["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", target2], root);
|
|
11206
|
+
if (git2.ok)
|
|
11022
11207
|
return accept(
|
|
11023
|
-
|
|
11208
|
+
git2.stdout.split("\0").filter((name) => name !== "").map(toPosixPath)
|
|
11024
11209
|
);
|
|
11025
11210
|
const base = toAbsolutePath(target2, root);
|
|
11026
11211
|
const walked = runtime.walkFiles(base);
|
|
@@ -11028,9 +11213,9 @@ var listed2 = (runtime, root, target2) => {
|
|
|
11028
11213
|
return accept(walked.value.map((name) => toPosixPath(relative6(root, join23(base, toPosixPath(name))))));
|
|
11029
11214
|
};
|
|
11030
11215
|
var scanFile = (runtime, root, file) => {
|
|
11031
|
-
const
|
|
11032
|
-
if (!
|
|
11033
|
-
return scanText(
|
|
11216
|
+
const text8 = runtime.readText(toAbsolutePath(file, root));
|
|
11217
|
+
if (!text8.ok || text8.value.length > MAX_FILE_BYTES || binary(text8.value)) return [];
|
|
11218
|
+
return scanText(text8.value, { path: file }).map((finding4) => locate(finding4, file, finding4.line));
|
|
11034
11219
|
};
|
|
11035
11220
|
var scanPath = (runtime, root, target2) => {
|
|
11036
11221
|
const files = listed2(runtime, root, target2);
|
|
@@ -11044,15 +11229,15 @@ var diffFindings = (diff) => {
|
|
|
11044
11229
|
};
|
|
11045
11230
|
var scanStaged = (runtime, root, includeUnstaged = false) => {
|
|
11046
11231
|
const args2 = includeUnstaged ? ["diff", "HEAD", ...DIFF_FLAGS] : ["diff", "--cached", ...DIFF_FLAGS];
|
|
11047
|
-
const
|
|
11048
|
-
if (!
|
|
11049
|
-
return accept(diffFindings(
|
|
11232
|
+
const git2 = runtime.git(args2, root);
|
|
11233
|
+
if (!git2.ok) return reject(git2.found ? "git diff failed" : "git not found");
|
|
11234
|
+
return accept(diffFindings(git2.stdout));
|
|
11050
11235
|
};
|
|
11051
11236
|
var scanHistory = async (runtime, root) => {
|
|
11052
11237
|
const scanner = diffScanner();
|
|
11053
11238
|
const args2 = ["log", "-p", "--all", "--format=commit %H", ...DIFF_FLAGS];
|
|
11054
|
-
const
|
|
11055
|
-
if (!
|
|
11239
|
+
const git2 = await runtime.gitLines(args2, root, scanner.push);
|
|
11240
|
+
if (!git2.ok) return reject(git2.found ? "git log failed" : "git not found");
|
|
11056
11241
|
return accept(scanner.end().filter((finding4) => !SKIPPED.test(finding4.file)));
|
|
11057
11242
|
};
|
|
11058
11243
|
|
|
@@ -11127,8 +11312,8 @@ var treeViolations = (ctx) => {
|
|
|
11127
11312
|
changes: relevant,
|
|
11128
11313
|
before: (path) => fileAt(ctx.git, ctx.base, path),
|
|
11129
11314
|
after: (path) => {
|
|
11130
|
-
const
|
|
11131
|
-
return
|
|
11315
|
+
const text8 = ctx.runtime.readText(toAbsolutePath(join24(ctx.paths.root, path)));
|
|
11316
|
+
return text8.ok ? text8.value : void 0;
|
|
11132
11317
|
},
|
|
11133
11318
|
acceptedConfigs: ctx.trust.configs,
|
|
11134
11319
|
scopePaths: ctx.contract.scope.paths,
|
|
@@ -11292,10 +11477,10 @@ var bodyLine = (draft, line2) => {
|
|
|
11292
11477
|
else if (line2.startsWith("+")) draft.current.added.push(line2.slice(1));
|
|
11293
11478
|
};
|
|
11294
11479
|
var DIFF_GIT = /^diff --git a\/(.+) b\/(.+)$/;
|
|
11295
|
-
var parseUnifiedDiff = (
|
|
11480
|
+
var parseUnifiedDiff = (text8) => {
|
|
11296
11481
|
const out = [];
|
|
11297
11482
|
let draft;
|
|
11298
|
-
for (const raw of
|
|
11483
|
+
for (const raw of text8.split("\n")) {
|
|
11299
11484
|
const line2 = withoutCarriageReturn(raw);
|
|
11300
11485
|
const start = DIFF_GIT.exec(line2);
|
|
11301
11486
|
if (start !== null) {
|
|
@@ -11396,10 +11581,10 @@ var mergeInProgress = (runtime, repo) => revParse(runtime, repo, "MERGE_HEAD") !
|
|
|
11396
11581
|
|
|
11397
11582
|
// src/git/facts.ts
|
|
11398
11583
|
var memo2 = (compute) => {
|
|
11399
|
-
let
|
|
11584
|
+
let cached3;
|
|
11400
11585
|
return () => {
|
|
11401
|
-
|
|
11402
|
-
return
|
|
11586
|
+
cached3 ??= { value: compute() };
|
|
11587
|
+
return cached3.value;
|
|
11403
11588
|
};
|
|
11404
11589
|
};
|
|
11405
11590
|
var memoBy = (compute) => {
|
|
@@ -11446,7 +11631,7 @@ var normalizeLine = (line2) => line2.replace(/\r$/, "").trim().replace(/\s*,$/,
|
|
|
11446
11631
|
var WEAK = /^[\p{P}\p{S}\s]+$/u;
|
|
11447
11632
|
var isWeak = (line2) => WEAK.test(line2);
|
|
11448
11633
|
var meaningful = (lines3) => lines3.map(normalizeLine).filter((line2) => line2 !== "");
|
|
11449
|
-
var contentLines = (
|
|
11634
|
+
var contentLines = (text8) => text8 === void 0 ? [] : meaningful(text8.split("\n"));
|
|
11450
11635
|
var countLines = (lines3) => {
|
|
11451
11636
|
const counts = /* @__PURE__ */ new Map();
|
|
11452
11637
|
for (const line2 of lines3) counts.set(line2, (counts.get(line2) ?? 0) + 1);
|
|
@@ -11495,16 +11680,16 @@ var itemOf = (value) => {
|
|
|
11495
11680
|
approvedBy: optionalString(value, "approved_by")
|
|
11496
11681
|
};
|
|
11497
11682
|
};
|
|
11498
|
-
var invalidFinding = (file,
|
|
11683
|
+
var invalidFinding = (file, text8) => ({
|
|
11499
11684
|
kind: "invalid-dropped",
|
|
11500
11685
|
file,
|
|
11501
11686
|
side: void 0,
|
|
11502
11687
|
line: void 0,
|
|
11503
|
-
text:
|
|
11688
|
+
text: text8,
|
|
11504
11689
|
weak: false
|
|
11505
11690
|
});
|
|
11506
|
-
var parseDropped = (
|
|
11507
|
-
const parsed = parseJson(
|
|
11691
|
+
var parseDropped = (text8) => {
|
|
11692
|
+
const parsed = parseJson(text8);
|
|
11508
11693
|
if (!parsed.ok) return parsed;
|
|
11509
11694
|
const list4 = isRecord(parsed.value) ? parsed.value["dropped"] : parsed.value;
|
|
11510
11695
|
if (!Array.isArray(list4)) return reject("expected a list of dropped items");
|
|
@@ -11520,9 +11705,9 @@ var parseDropped = (text7) => {
|
|
|
11520
11705
|
};
|
|
11521
11706
|
var loadDropped = (runtime, root, file) => {
|
|
11522
11707
|
const path = toAbsolutePath(file ?? join26(root, DROPPED_FILE), root);
|
|
11523
|
-
const
|
|
11524
|
-
if (!
|
|
11525
|
-
return parseDropped(
|
|
11708
|
+
const text8 = runtime.readText(path);
|
|
11709
|
+
if (!text8.ok) return file === void 0 ? accept({ items: [], invalid: [] }) : reject(`${file}: ${text8.error}`);
|
|
11710
|
+
return parseDropped(text8.value);
|
|
11526
11711
|
};
|
|
11527
11712
|
var isDropped = (finding4, items) => items.some(
|
|
11528
11713
|
(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)
|
|
@@ -11645,13 +11830,13 @@ var removalOf = (ours, theirs, paired) => {
|
|
|
11645
11830
|
var missingAdditions = (hunks, file, elsewhere) => {
|
|
11646
11831
|
const present2 = new Set(file.result);
|
|
11647
11832
|
return hunks.flatMap(({ side, hunk, added }) => {
|
|
11648
|
-
const
|
|
11649
|
-
if (
|
|
11833
|
+
const text8 = added.find((line2) => !isWeak(line2) && !present2.has(line2));
|
|
11834
|
+
if (text8 === void 0 || containsSequence(file.result, added) || elsewhere(added)) return [];
|
|
11650
11835
|
const offset = Math.max(
|
|
11651
11836
|
0,
|
|
11652
|
-
hunk.added.findIndex((line2) => normalizeLine(line2) ===
|
|
11837
|
+
hunk.added.findIndex((line2) => normalizeLine(line2) === text8)
|
|
11653
11838
|
);
|
|
11654
|
-
return [{ kind: "missing-addition", file: file.path, side, line: hunk.newStart + offset, text:
|
|
11839
|
+
return [{ kind: "missing-addition", file: file.path, side, line: hunk.newStart + offset, text: text8, weak: false }];
|
|
11655
11840
|
});
|
|
11656
11841
|
};
|
|
11657
11842
|
var expectations = (file, hunks, removal) => {
|
|
@@ -11724,8 +11909,8 @@ var symbolsOf = (line2) => {
|
|
|
11724
11909
|
...title === void 0 ? [] : [{ name: title, test: true }]
|
|
11725
11910
|
];
|
|
11726
11911
|
};
|
|
11727
|
-
var escapeRegex = (
|
|
11728
|
-
var containsSymbol = (
|
|
11912
|
+
var escapeRegex = (text8) => text8.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11913
|
+
var containsSymbol = (text8, symbol) => symbol.test ? text8.includes(symbol.name) : new RegExp(`(?<![\\w$])${escapeRegex(symbol.name)}(?![\\w$])`).test(text8);
|
|
11729
11914
|
var missingSymbols = (file, side, hunks, present2) => {
|
|
11730
11915
|
const seen = /* @__PURE__ */ new Set();
|
|
11731
11916
|
return hunks.flatMap(
|
|
@@ -11744,12 +11929,12 @@ var created2 = (diff) => diff?.status === "added" || diff?.status === "copied";
|
|
|
11744
11929
|
var resultPathOf = (context, pair) => [pair.ours?.newPath, pair.theirs?.newPath, pair.key].find(
|
|
11745
11930
|
(path) => path !== void 0 && context.resultText(path) !== void 0
|
|
11746
11931
|
);
|
|
11747
|
-
var finding2 = (kind, file, side,
|
|
11932
|
+
var finding2 = (kind, file, side, text8) => ({
|
|
11748
11933
|
kind,
|
|
11749
11934
|
file,
|
|
11750
11935
|
side,
|
|
11751
11936
|
line: void 0,
|
|
11752
|
-
text:
|
|
11937
|
+
text: text8,
|
|
11753
11938
|
weak: false
|
|
11754
11939
|
});
|
|
11755
11940
|
var binaryFindings = (context, pair, path) => {
|
|
@@ -11768,8 +11953,8 @@ var binaryFindings = (context, pair, path) => {
|
|
|
11768
11953
|
return result === expected ? [] : [finding2("binary", file, side, file)];
|
|
11769
11954
|
};
|
|
11770
11955
|
var elsewhereOf = (context) => (lines3) => context.resultFiles().some((path) => containsSequence(contentLines(context.resultText(path)), lines3));
|
|
11771
|
-
var symbolFindings = (context, pair, path,
|
|
11772
|
-
const present2 = (symbol) =>
|
|
11956
|
+
var symbolFindings = (context, pair, path, text8) => {
|
|
11957
|
+
const present2 = (symbol) => text8 !== void 0 && containsSymbol(text8, symbol) || context.resultFiles().some((other) => containsSymbol(context.resultText(other) ?? "", symbol));
|
|
11773
11958
|
return [
|
|
11774
11959
|
...missingSymbols(path, "ours", pair.ours?.hunks ?? [], present2),
|
|
11775
11960
|
...missingSymbols(path, "theirs", pair.theirs?.hunks ?? [], present2)
|
|
@@ -11785,19 +11970,19 @@ var verifyFile = (context, pair) => {
|
|
|
11785
11970
|
const copied = [pair.ours, pair.theirs].find((diff) => diff?.status === "copied");
|
|
11786
11971
|
const baseSource = copied?.oldPath ?? (created2(pair.ours) || created2(pair.theirs) ? void 0 : pair.key);
|
|
11787
11972
|
const baseText = baseSource === void 0 ? void 0 : blobText(context.runtime, context.repo, context.inputs.base, baseSource);
|
|
11788
|
-
const
|
|
11973
|
+
const text8 = path === void 0 ? void 0 : context.resultText(path);
|
|
11789
11974
|
const file = path ?? pair.key;
|
|
11790
11975
|
const lines3 = checkFile(
|
|
11791
11976
|
{
|
|
11792
11977
|
path: file,
|
|
11793
11978
|
base: contentLines(baseText),
|
|
11794
|
-
result: contentLines(
|
|
11979
|
+
result: contentLines(text8),
|
|
11795
11980
|
ours: pair.ours?.hunks ?? [],
|
|
11796
11981
|
theirs: pair.theirs?.hunks ?? []
|
|
11797
11982
|
},
|
|
11798
11983
|
elsewhereOf(context)
|
|
11799
11984
|
);
|
|
11800
|
-
return [...lines3, ...symbolFindings(context, pair, file,
|
|
11985
|
+
return [...lines3, ...symbolFindings(context, pair, file, text8)];
|
|
11801
11986
|
};
|
|
11802
11987
|
|
|
11803
11988
|
// src/git/merge/verify.ts
|
|
@@ -11917,8 +12102,8 @@ var hashesOf = (value) => isRecord(value) ? Object.fromEntries(
|
|
|
11917
12102
|
Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
|
|
11918
12103
|
) : {};
|
|
11919
12104
|
var readObject = (runtime, path) => {
|
|
11920
|
-
const
|
|
11921
|
-
return
|
|
12105
|
+
const text8 = runtime.readText(path);
|
|
12106
|
+
return text8.ok ? parseJsonObject(text8.value) : text8;
|
|
11922
12107
|
};
|
|
11923
12108
|
var readBaseline2 = (runtime, repo, owner) => {
|
|
11924
12109
|
const parsed = readObject(runtime, fileOf(repo, "baseline", owner));
|
|
@@ -11984,7 +12169,7 @@ var foreignFiles = (runtime, repo, owner, targets2) => {
|
|
|
11984
12169
|
};
|
|
11985
12170
|
|
|
11986
12171
|
// src/git/gate-status.ts
|
|
11987
|
-
var lines = (
|
|
12172
|
+
var lines = (text8) => text8.split("\n").filter((line2) => line2.trim() !== "");
|
|
11988
12173
|
var unpushedSince = (runtime, repo, since) => {
|
|
11989
12174
|
if (lines(runtime.git(["remote"], repo.root).stdout).length === 0) return [];
|
|
11990
12175
|
const result = runtime.git(["rev-list", `${since}..HEAD`, "--not", "--remotes"], repo.root);
|
|
@@ -12388,8 +12573,8 @@ var subagentStart = (input) => {
|
|
|
12388
12573
|
const active = readState(runtime, paths).active;
|
|
12389
12574
|
if (active === void 0) return { kind: "allow" };
|
|
12390
12575
|
const disk = (relative11) => {
|
|
12391
|
-
const
|
|
12392
|
-
return
|
|
12576
|
+
const text8 = runtime.readText(toAbsolutePath(join29(paths.root, relative11)));
|
|
12577
|
+
return text8.ok ? text8.value : void 0;
|
|
12393
12578
|
};
|
|
12394
12579
|
const loaded = loadTask(runtime, paths, { disk, base: () => void 0 }, active);
|
|
12395
12580
|
if (!loaded.ok) return { kind: "allow" };
|
|
@@ -12399,7 +12584,7 @@ var subagentStart = (input) => {
|
|
|
12399
12584
|
|
|
12400
12585
|
// src/gate/hooks/index.ts
|
|
12401
12586
|
var PORTAO_HANDLERS = {
|
|
12402
|
-
PreToolUse: [selfGuard, bypassGuard, testsGuard],
|
|
12587
|
+
PreToolUse: [reviewAgentGuard, selfGuard, bypassGuard, testsGuard],
|
|
12403
12588
|
UserPromptSubmit: [pausePrompt, promptRelease],
|
|
12404
12589
|
Stop: [unlessPaused(stopGate)],
|
|
12405
12590
|
SubagentStop: [unlessPaused(subagentStopGate)],
|
|
@@ -12415,8 +12600,8 @@ import { join as join31 } from "node:path";
|
|
|
12415
12600
|
|
|
12416
12601
|
// src/git/command-text.ts
|
|
12417
12602
|
var MAX_SHOWN = 160;
|
|
12418
|
-
var shown = (
|
|
12419
|
-
const flat =
|
|
12603
|
+
var shown = (text8) => {
|
|
12604
|
+
const flat = text8.replace(/\s+/g, " ").trim();
|
|
12420
12605
|
return flat.length > MAX_SHOWN ? `${flat.slice(0, MAX_SHOWN - 3)}...` : flat;
|
|
12421
12606
|
};
|
|
12422
12607
|
var commandText = (command) => shown([command.program, ...command.args].join(" "));
|
|
@@ -12766,9 +12951,9 @@ var SSH_URL = /ssh:\/\/(?:[^@\s/]+@)?([A-Za-z0-9.-]+)/g;
|
|
|
12766
12951
|
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;
|
|
12767
12952
|
var hostKey = (raw) => raw.trim().toLowerCase().replace(/^[^@]*@/, "").replace(/^\[([^\]]+)\].*$/, "$1").replace(/:\d+$/, "").replace(/\.$/, "");
|
|
12768
12953
|
var shellLine = (line2) => line2.replace(/^\s*(?:-\s*)?(?:run:\s*\|?)?/, "");
|
|
12769
|
-
var hostsInText = (
|
|
12770
|
-
const urls = [...
|
|
12771
|
-
const lines3 =
|
|
12954
|
+
var hostsInText = (text8) => {
|
|
12955
|
+
const urls = [...text8.matchAll(SSH_URL)].map((match) => match[1] ?? "");
|
|
12956
|
+
const lines3 = text8.split(/\r?\n/);
|
|
12772
12957
|
const keyed = lines3.flatMap((line2) => {
|
|
12773
12958
|
const match = HOST_KEY.exec(line2);
|
|
12774
12959
|
return match?.[1] === void 0 ? [] : [match[1]];
|
|
@@ -12782,8 +12967,8 @@ var workflowFiles = (runtime, root) => {
|
|
|
12782
12967
|
return listed4.value.filter((name) => /deploy/i.test(name) && /\.ya?ml$/i.test(name)).map((name) => `.github/workflows/${name}`);
|
|
12783
12968
|
};
|
|
12784
12969
|
var detectedHosts = (runtime, root, files) => files.flatMap((file) => {
|
|
12785
|
-
const
|
|
12786
|
-
return
|
|
12970
|
+
const text8 = runtime.readText(toAbsolutePath(join30(root, file)));
|
|
12971
|
+
return text8.ok ? hostsInText(text8.value) : [];
|
|
12787
12972
|
});
|
|
12788
12973
|
var loadProdTargets = (runtime, root, config) => {
|
|
12789
12974
|
const workflows = workflowFiles(runtime, root);
|
|
@@ -12957,8 +13142,8 @@ var prodGuard = (runtime, root, t) => {
|
|
|
12957
13142
|
if (!PROD_PROGRAMS.has(command.program)) return void 0;
|
|
12958
13143
|
const hit3 = prodHit(command, targets2(), isProd);
|
|
12959
13144
|
if (hit3 === void 0) return void 0;
|
|
12960
|
-
const
|
|
12961
|
-
return hit3.effect === "read" ? { kind: "read", hit: hit3, command:
|
|
13145
|
+
const text8 = commandText(command);
|
|
13146
|
+
return hit3.effect === "read" ? { kind: "read", hit: hit3, command: text8 } : blockOf(t, hit3, text8, targets2().deployPath);
|
|
12962
13147
|
}
|
|
12963
13148
|
};
|
|
12964
13149
|
};
|
|
@@ -12978,8 +13163,8 @@ var recordEvidence = (runtime, root, owner, reads) => {
|
|
|
12978
13163
|
const path = toAbsolutePath(join31(root, EVIDENCE_FILE));
|
|
12979
13164
|
runtime.mkdir(toAbsolutePath(join31(root, ".finitemonkey")));
|
|
12980
13165
|
const before = runtime.readText(path);
|
|
12981
|
-
const
|
|
12982
|
-
runtime.writeAtomic(path, `${
|
|
13166
|
+
const text8 = before.ok ? before.value : "";
|
|
13167
|
+
runtime.writeAtomic(path, `${text8}${text8 === "" || text8.endsWith("\n") ? "" : "\n"}${lines3.join("\n")}
|
|
12983
13168
|
`);
|
|
12984
13169
|
};
|
|
12985
13170
|
|
|
@@ -12994,15 +13179,15 @@ var editTexts = (input, file) => {
|
|
|
12994
13179
|
if (!Array.isArray(edits)) return [];
|
|
12995
13180
|
return edits.flatMap((edit) => {
|
|
12996
13181
|
if (!isRecord(edit)) return [];
|
|
12997
|
-
const
|
|
12998
|
-
return
|
|
13182
|
+
const text8 = optionalString(edit, "new_string");
|
|
13183
|
+
return text8 === void 0 ? [] : [{ file, text: text8, whole: false }];
|
|
12999
13184
|
});
|
|
13000
13185
|
};
|
|
13001
13186
|
var writtenTexts = (tool, input) => {
|
|
13002
13187
|
const file = optionalString(input, "file_path") ?? optionalString(input, "notebook_path") ?? "";
|
|
13003
13188
|
const single2 = (key, whole) => {
|
|
13004
|
-
const
|
|
13005
|
-
return
|
|
13189
|
+
const text8 = optionalString(input, key);
|
|
13190
|
+
return text8 === void 0 ? [] : [{ file, text: text8, whole }];
|
|
13006
13191
|
};
|
|
13007
13192
|
switch (tool) {
|
|
13008
13193
|
case "Write":
|
|
@@ -13684,24 +13869,24 @@ var withChain = (facts, chain) => {
|
|
|
13684
13869
|
const branch = chain?.branch;
|
|
13685
13870
|
return branch === void 0 ? facts : { ...facts, currentBranch: () => branch };
|
|
13686
13871
|
};
|
|
13687
|
-
var decide = (context, repo, verdict, facts,
|
|
13872
|
+
var decide = (context, repo, verdict, facts, text8) => {
|
|
13688
13873
|
switch (verdict.kind) {
|
|
13689
13874
|
case "pass":
|
|
13690
13875
|
return { kind: "pass" };
|
|
13691
13876
|
case "block":
|
|
13692
|
-
return { kind: "block", rule: verdict.rule, subject: verdict.subject, command:
|
|
13877
|
+
return { kind: "block", rule: verdict.rule, subject: verdict.subject, command: text8 };
|
|
13693
13878
|
case "snapshot":
|
|
13694
|
-
return { kind: "snapshot", repo, op: verdict.op, command:
|
|
13879
|
+
return { kind: "snapshot", repo, op: verdict.op, command: text8 };
|
|
13695
13880
|
case "discard": {
|
|
13696
13881
|
const entries = facts.dirty();
|
|
13697
|
-
if (entries === void 0) return { kind: "unknown", command:
|
|
13882
|
+
if (entries === void 0) return { kind: "unknown", command: text8 };
|
|
13698
13883
|
const foreign = foreignFiles(
|
|
13699
13884
|
context.runtime,
|
|
13700
13885
|
repo,
|
|
13701
13886
|
context.owner,
|
|
13702
13887
|
targetsOf(entries, verdict.discard, facts.prefix)
|
|
13703
13888
|
);
|
|
13704
|
-
return foreign.length > 0 ? { kind: "foreign", files: foreign, command:
|
|
13889
|
+
return foreign.length > 0 ? { kind: "foreign", files: foreign, command: text8 } : { kind: "snapshot", repo, op: verdict.op, command: text8 };
|
|
13705
13890
|
}
|
|
13706
13891
|
}
|
|
13707
13892
|
};
|
|
@@ -13818,9 +14003,9 @@ var parseJournalLine = (line2) => {
|
|
|
13818
14003
|
};
|
|
13819
14004
|
};
|
|
13820
14005
|
var readJournal = (runtime, root) => {
|
|
13821
|
-
const
|
|
13822
|
-
if (!
|
|
13823
|
-
return
|
|
14006
|
+
const text8 = runtime.readText(journalPath(root));
|
|
14007
|
+
if (!text8.ok) return [];
|
|
14008
|
+
return text8.value.split("\n").flatMap((line2) => {
|
|
13824
14009
|
const entry2 = line2.trim() === "" ? void 0 : parseJournalLine(line2);
|
|
13825
14010
|
return entry2 === void 0 ? [] : [entry2];
|
|
13826
14011
|
});
|
|
@@ -14012,9 +14197,9 @@ var review = (input, commands, owner) => {
|
|
|
14012
14197
|
return { blocked: void 0, snapshots, reads };
|
|
14013
14198
|
};
|
|
14014
14199
|
var gitPreToolUse = (input) => {
|
|
14015
|
-
const
|
|
14016
|
-
if (
|
|
14017
|
-
const commands = normalizeCommand(
|
|
14200
|
+
const text8 = shellText(input.payload);
|
|
14201
|
+
if (text8 === void 0) return ALLOW;
|
|
14202
|
+
const commands = normalizeCommand(text8, dialectOf(toolName2(input.payload))).commands;
|
|
14018
14203
|
if (commands.length === 0) return ALLOW;
|
|
14019
14204
|
const owner = ownerOf2(input.payload);
|
|
14020
14205
|
const result = review(input, commands, owner);
|
|
@@ -14076,8 +14261,8 @@ var GIT_HANDLERS = {
|
|
|
14076
14261
|
var SHADOW_FILE = "shadow.json";
|
|
14077
14262
|
var numberOf = (value) => typeof value === "number" && Number.isInteger(value) ? value : 0;
|
|
14078
14263
|
var readShadow = (runtime, paths) => {
|
|
14079
|
-
const
|
|
14080
|
-
const parsed =
|
|
14264
|
+
const text8 = runtime.readText(paths.cache(SHADOW_FILE));
|
|
14265
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
14081
14266
|
if (parsed?.ok !== true) return {};
|
|
14082
14267
|
return Object.fromEntries(
|
|
14083
14268
|
Object.entries(parsed.value).flatMap(
|
|
@@ -14161,11 +14346,11 @@ var ASSIGNMENT3 = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
|
14161
14346
|
var WRAPPERS2 = /* @__PURE__ */ new Set(["sudo", "command", "exec", "time", "nice", "nohup", "builtin", "env"]);
|
|
14162
14347
|
var liftHeredocs = (command) => {
|
|
14163
14348
|
const heredocs = [];
|
|
14164
|
-
const
|
|
14349
|
+
const text8 = command.replace(HEREDOC2, (_match, _quote, _tag, body2) => {
|
|
14165
14350
|
heredocs.push(body2);
|
|
14166
14351
|
return `<<__FM_HEREDOC_${String(heredocs.length - 1)}__`;
|
|
14167
14352
|
});
|
|
14168
|
-
return { text:
|
|
14353
|
+
return { text: text8, heredocs };
|
|
14169
14354
|
};
|
|
14170
14355
|
var endWord = (scan) => {
|
|
14171
14356
|
if (scan.word !== void 0) scan.words.push(scan.word);
|
|
@@ -14176,23 +14361,23 @@ var endSegment = (scan) => {
|
|
|
14176
14361
|
if (scan.words.length > 0) scan.segments.push(scan.words);
|
|
14177
14362
|
scan.words = [];
|
|
14178
14363
|
};
|
|
14179
|
-
var quotedEnd2 = (
|
|
14364
|
+
var quotedEnd2 = (text8, from, quote) => {
|
|
14180
14365
|
let index = from;
|
|
14181
|
-
while (index <
|
|
14366
|
+
while (index < text8.length && text8[index] !== quote) index += quote === '"' && text8[index] === "\\" ? 2 : 1;
|
|
14182
14367
|
return index;
|
|
14183
14368
|
};
|
|
14184
14369
|
var unescape = (value) => value.replace(/\\(["\\$`])/g, "$1");
|
|
14185
|
-
var splitWords = (
|
|
14370
|
+
var splitWords = (text8) => {
|
|
14186
14371
|
const scan = { words: [], segments: [], word: void 0 };
|
|
14187
|
-
for (let index = 0; index <
|
|
14188
|
-
const char =
|
|
14372
|
+
for (let index = 0; index < text8.length; index += 1) {
|
|
14373
|
+
const char = text8[index] ?? "";
|
|
14189
14374
|
if (char === '"' || char === "'") {
|
|
14190
|
-
const end = quotedEnd2(
|
|
14191
|
-
const inner =
|
|
14375
|
+
const end = quotedEnd2(text8, index + 1, char);
|
|
14376
|
+
const inner = text8.slice(index + 1, end);
|
|
14192
14377
|
scan.word = (scan.word ?? "") + (char === '"' ? unescape(inner) : inner);
|
|
14193
14378
|
index = end;
|
|
14194
|
-
} else if (char === "\\" && index + 1 <
|
|
14195
|
-
scan.word = (scan.word ?? "") + (
|
|
14379
|
+
} else if (char === "\\" && index + 1 < text8.length) {
|
|
14380
|
+
scan.word = (scan.word ?? "") + (text8[index + 1] === "\n" ? "" : text8[index + 1] ?? "");
|
|
14196
14381
|
index += 1;
|
|
14197
14382
|
} else if (SEPARATOR.test(char)) {
|
|
14198
14383
|
endSegment(scan);
|
|
@@ -14213,8 +14398,8 @@ var segmentOf = (words) => {
|
|
|
14213
14398
|
return { program: program.split(/[\\/]/).pop() ?? program, args: words.slice(index + 1) };
|
|
14214
14399
|
};
|
|
14215
14400
|
var shellWords = (command) => {
|
|
14216
|
-
const { text:
|
|
14217
|
-
const segments = splitWords(
|
|
14401
|
+
const { text: text8, heredocs } = liftHeredocs(command);
|
|
14402
|
+
const segments = splitWords(text8).flatMap((words) => {
|
|
14218
14403
|
const segment = segmentOf(words);
|
|
14219
14404
|
return segment === void 0 ? [] : [segment];
|
|
14220
14405
|
});
|
|
@@ -14319,7 +14504,7 @@ var sourceOf = (value, isFile, segment, heredocs) => {
|
|
|
14319
14504
|
if (inline3 !== void 0) return { kind: "inline", text: inline3 };
|
|
14320
14505
|
if (!isFile) return { kind: "inline", text: value };
|
|
14321
14506
|
if (value !== "-") return { kind: "file", path: value };
|
|
14322
|
-
const piped = segment.args.map((arg) => heredocIn(arg, heredocs)).find((
|
|
14507
|
+
const piped = segment.args.map((arg) => heredocIn(arg, heredocs)).find((text8) => text8 !== void 0);
|
|
14323
14508
|
return { kind: "inline", text: piped ?? "" };
|
|
14324
14509
|
};
|
|
14325
14510
|
var collect3 = (target2, segment, args2, spec2, heredocs) => {
|
|
@@ -14416,15 +14601,15 @@ var textOf2 = (input, found) => {
|
|
|
14416
14601
|
return accept(parts.join("\n\n"));
|
|
14417
14602
|
};
|
|
14418
14603
|
var decisionFor = (input, found, blocking2) => {
|
|
14419
|
-
const
|
|
14420
|
-
if (!
|
|
14604
|
+
const text8 = textOf2(input, found);
|
|
14605
|
+
if (!text8.ok) {
|
|
14421
14606
|
return blocking2 ? {
|
|
14422
14607
|
kind: "block",
|
|
14423
|
-
reason: modelReason(input.t, "prose.block.unreadable", { ...
|
|
14608
|
+
reason: modelReason(input.t, "prose.block.unreadable", { ...text8.error }),
|
|
14424
14609
|
human: input.t("prose.block.human")
|
|
14425
14610
|
} : void 0;
|
|
14426
14611
|
}
|
|
14427
|
-
const report2 = scoreProse(
|
|
14612
|
+
const report2 = scoreProse(text8.value, { context: "record" });
|
|
14428
14613
|
if (report2.allowed || report2.spans.length === 0) return void 0;
|
|
14429
14614
|
const params2 = {
|
|
14430
14615
|
target: input.t(TARGET_KEYS[found.target]),
|
|
@@ -14471,7 +14656,7 @@ var HOOK_ACTOR = "finitemonkey";
|
|
|
14471
14656
|
var liveFindings = (input, texts2) => {
|
|
14472
14657
|
const baseline = loadBaseline(input.runtime, input.cwd);
|
|
14473
14658
|
const accepted2 = acceptedFingerprints(baseline.ok ? baseline.value : EMPTY_BASELINE, input.runtime.now());
|
|
14474
|
-
return texts2.flatMap((
|
|
14659
|
+
return texts2.flatMap((text8) => scanText(text8, { rules: CREDENTIAL_RULES })).filter((finding4) => !accepted2.has(finding4.fingerprint));
|
|
14475
14660
|
};
|
|
14476
14661
|
var record2 = (input, findings, draft, where) => {
|
|
14477
14662
|
const ids2 = findings.map((finding4) => {
|
|
@@ -14732,12 +14917,12 @@ var secretsSessionStart = (input) => {
|
|
|
14732
14917
|
const remotes = remotesWithCredentials(input.runtime, input.cwd);
|
|
14733
14918
|
if (remotes.length === 0) return { kind: "allow" };
|
|
14734
14919
|
const params2 = { remotes: remotes.join(", "), remote: remotes[0] ?? "origin" };
|
|
14735
|
-
const
|
|
14920
|
+
const text8 = input.t("secrets.session.remote_credential", params2);
|
|
14736
14921
|
return {
|
|
14737
14922
|
kind: "allow",
|
|
14738
14923
|
output: {
|
|
14739
|
-
systemMessage: `finitemonkey: ${
|
|
14740
|
-
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext:
|
|
14924
|
+
systemMessage: `finitemonkey: ${text8}`,
|
|
14925
|
+
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: text8 }
|
|
14741
14926
|
}
|
|
14742
14927
|
};
|
|
14743
14928
|
};
|
|
@@ -14826,9 +15011,9 @@ var EMPTY_PAYLOAD = {
|
|
|
14826
15011
|
stopHookActive: false,
|
|
14827
15012
|
raw: {}
|
|
14828
15013
|
};
|
|
14829
|
-
var parseHookPayload = (event,
|
|
14830
|
-
if (interactive &&
|
|
14831
|
-
const parsed = parseJsonObject(
|
|
15014
|
+
var parseHookPayload = (event, text8, interactive) => {
|
|
15015
|
+
if (interactive && text8.trim() === "") return accept(EMPTY_PAYLOAD);
|
|
15016
|
+
const parsed = parseJsonObject(text8);
|
|
14832
15017
|
if (!parsed.ok) return reject(`stdin: ${parsed.error}`);
|
|
14833
15018
|
const payload = {
|
|
14834
15019
|
hookEventName: optionalString(parsed.value, "hook_event_name"),
|
|
@@ -14888,8 +15073,8 @@ var HOOK_PROBE_ENV = "FINITEMONKEY_HOOK_PROBE";
|
|
|
14888
15073
|
var messageOf2 = (error) => error instanceof Error ? error.message : String(error);
|
|
14889
15074
|
var decide2 = (input, handlers) => composeDecisions(input, handlers[input.event] ?? [allow]);
|
|
14890
15075
|
var evaluate = async (event, options) => {
|
|
14891
|
-
const
|
|
14892
|
-
const parsed = parseHookPayload(event,
|
|
15076
|
+
const text8 = await readAll(options.stdin);
|
|
15077
|
+
const parsed = parseHookPayload(event, text8, options.stdin.isTTY === true);
|
|
14893
15078
|
if (!parsed.ok) return { kind: "failed", payload: void 0, detail: parsed.error };
|
|
14894
15079
|
const payload = parsed.value;
|
|
14895
15080
|
if (options.env?.[HOOK_PROBE_ENV] === "1") return { kind: "decided", payload, decision: { kind: "allow" } };
|
|
@@ -14966,7 +15151,7 @@ var runHook = async (event, options) => {
|
|
|
14966
15151
|
var PROBE_TIMEOUT_MS2 = 1e4;
|
|
14967
15152
|
var SLOW_HOOK_MS = 1500;
|
|
14968
15153
|
var DEFAULT_TIMEOUT_S = 60;
|
|
14969
|
-
var expand = (
|
|
15154
|
+
var expand = (text8, vars) => text8.replace(/\$\{(\w+)\}|\$(\w+)/g, (match, braced, bare) => {
|
|
14970
15155
|
const name = braced ?? bare ?? "";
|
|
14971
15156
|
return vars[name] ?? match;
|
|
14972
15157
|
});
|
|
@@ -15015,7 +15200,7 @@ var run2 = (exec, invocation, cwd, env, input) => exec({
|
|
|
15015
15200
|
env: { ...env, [HOOK_PROBE_ENV]: "1" },
|
|
15016
15201
|
...input === void 0 ? {} : { input }
|
|
15017
15202
|
});
|
|
15018
|
-
var firstLine = (
|
|
15203
|
+
var firstLine = (text8) => maskText(text8.trim().split("\n")[0] ?? "").slice(0, 160);
|
|
15019
15204
|
var classify2 = (result) => {
|
|
15020
15205
|
if (!result.found) return { kind: "failed", problem: "missing", detail: "" };
|
|
15021
15206
|
if (result.timedOut) return { kind: "failed", problem: "timeout", detail: "" };
|
|
@@ -15246,8 +15431,8 @@ var installRecord = (value, root) => {
|
|
|
15246
15431
|
return records.find((record3) => record3["projectPath"] === root) ?? records[0];
|
|
15247
15432
|
};
|
|
15248
15433
|
var readJson = (env, path) => {
|
|
15249
|
-
const
|
|
15250
|
-
const parsed =
|
|
15434
|
+
const text8 = env.runtime.readText(path);
|
|
15435
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
15251
15436
|
return parsed?.ok === true ? parsed.value : void 0;
|
|
15252
15437
|
};
|
|
15253
15438
|
var installedPlugin = (env) => {
|
|
@@ -15258,13 +15443,13 @@ var installedPlugin = (env) => {
|
|
|
15258
15443
|
const root = toAbsolutePath(installPath);
|
|
15259
15444
|
const manifest = readJson(env, toAbsolutePath(join33(root, ".claude-plugin", "plugin.json")));
|
|
15260
15445
|
const hooksPath = toAbsolutePath(join33(root, "hooks", "hooks.json"));
|
|
15261
|
-
const
|
|
15262
|
-
const parsed =
|
|
15446
|
+
const text8 = env.runtime.readText(hooksPath);
|
|
15447
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
15263
15448
|
return {
|
|
15264
15449
|
root,
|
|
15265
15450
|
version: (manifest === void 0 ? void 0 : optionalString(manifest, "version")) ?? optionalString(record3, "version"),
|
|
15266
15451
|
hooksFile: parsed?.ok === true ? { source: "plugin", path: hooksPath, hooks: parsed.value["hooks"] } : void 0,
|
|
15267
|
-
hooksText:
|
|
15452
|
+
hooksText: text8.ok ? text8.value : void 0
|
|
15268
15453
|
};
|
|
15269
15454
|
};
|
|
15270
15455
|
var PRECEDENCE2 = ["managed", "local", "project", "user"];
|
|
@@ -15294,8 +15479,8 @@ var schemaFinding = (file, problems, claude) => problems.length === 0 ? [] : [
|
|
|
15294
15479
|
)
|
|
15295
15480
|
];
|
|
15296
15481
|
var unreadableProjectFiles = (env) => settingsPaths(env).filter(({ scope: scope2 }) => scope2 === "project" || scope2 === "local").flatMap(({ path }) => {
|
|
15297
|
-
const
|
|
15298
|
-
return
|
|
15482
|
+
const text8 = env.runtime.readText(path);
|
|
15483
|
+
return text8.ok && text8.value.trim() !== "" && !parseJsonObject(text8.value).ok ? [path] : [];
|
|
15299
15484
|
});
|
|
15300
15485
|
var schemaFindings = (env, plugin, claude) => {
|
|
15301
15486
|
const label2 = claude === void 0 ? "?" : claude.join(".");
|
|
@@ -15381,15 +15566,15 @@ var TOP_INSTRUCTIONS = ["AGENTS.md", "CLAUDE.md", ".claude/CLAUDE.md", "CLAUDE.l
|
|
|
15381
15566
|
var RULES_DIR = ".claude/rules";
|
|
15382
15567
|
var SHOWN = 3;
|
|
15383
15568
|
var readProjectFile = (env, relative11) => {
|
|
15384
|
-
const
|
|
15385
|
-
return
|
|
15569
|
+
const text8 = env.runtime.readText(toAbsolutePath(join35(env.root, ...relative11.split("/"))));
|
|
15570
|
+
return text8.ok ? text8.value : void 0;
|
|
15386
15571
|
};
|
|
15387
15572
|
var instructionFiles = (env) => {
|
|
15388
15573
|
const listed4 = env.runtime.listDir(toAbsolutePath(join35(env.root, ...RULES_DIR.split("/"))));
|
|
15389
15574
|
const rules = listed4.ok ? listed4.value.filter((name) => name.endsWith(".md")).map((name) => `${RULES_DIR}/${name}`) : [];
|
|
15390
15575
|
return [...TOP_INSTRUCTIONS, ...rules].flatMap((path) => {
|
|
15391
|
-
const
|
|
15392
|
-
return
|
|
15576
|
+
const text8 = readProjectFile(env, path);
|
|
15577
|
+
return text8 === void 0 ? [] : [{ path, text: text8 }];
|
|
15393
15578
|
});
|
|
15394
15579
|
};
|
|
15395
15580
|
var shortList = (items) => items.length <= SHOWN ? items.join(", ") : `${items.slice(0, SHOWN).join(", ")} (+${String(items.length - SHOWN)})`;
|
|
@@ -15437,10 +15622,10 @@ var hiddenInstructions = (env, deny2) => {
|
|
|
15437
15622
|
];
|
|
15438
15623
|
};
|
|
15439
15624
|
var localFile = (env) => readProjectFile(env, "CLAUDE.local.md") === void 0 ? [] : [finding("instructions.local", "warn", "doctor.instructions.local", {}, "doctor.instructions.fix_local")];
|
|
15440
|
-
var lineCount = (
|
|
15625
|
+
var lineCount = (text8) => text8.split("\n").length - (text8.endsWith("\n") ? 1 : 0);
|
|
15441
15626
|
var oversized = (env) => ["AGENTS.md", "CLAUDE.md"].flatMap((file) => {
|
|
15442
|
-
const
|
|
15443
|
-
const lines3 =
|
|
15627
|
+
const text8 = readProjectFile(env, file);
|
|
15628
|
+
const lines3 = text8 === void 0 ? 0 : lineCount(text8);
|
|
15444
15629
|
if (lines3 <= INSTRUCTION_LINE_CEILING) return [];
|
|
15445
15630
|
return [
|
|
15446
15631
|
finding(
|
|
@@ -15453,13 +15638,13 @@ var oversized = (env) => ["AGENTS.md", "CLAUDE.md"].flatMap((file) => {
|
|
|
15453
15638
|
];
|
|
15454
15639
|
});
|
|
15455
15640
|
var MIN_RULE_WORDS = 6;
|
|
15456
|
-
var ruleLines = (
|
|
15641
|
+
var ruleLines = (text8) => text8.split("\n").map((raw) => withoutCarriageReturn(raw).trim()).filter((line2) => !/^(#|\||```|@|>)/.test(line2)).map(
|
|
15457
15642
|
(line2) => line2.replace(/^([-*+]|\d+[.)])\s+/, "").replace(/\s+/g, " ").toLowerCase()
|
|
15458
15643
|
).filter((line2) => line2.split(" ").length >= MIN_RULE_WORDS);
|
|
15459
15644
|
var duplicatedRules = (files) => {
|
|
15460
15645
|
const seen = /* @__PURE__ */ new Map();
|
|
15461
|
-
for (const { path, text:
|
|
15462
|
-
for (const line2 of ruleLines(
|
|
15646
|
+
for (const { path, text: text8 } of files) {
|
|
15647
|
+
for (const line2 of ruleLines(text8)) seen.set(line2, (seen.get(line2) ?? /* @__PURE__ */ new Set()).add(path));
|
|
15463
15648
|
}
|
|
15464
15649
|
return [...seen].filter(([, paths]) => paths.size > 1).map(([line2]) => line2);
|
|
15465
15650
|
};
|
|
@@ -15509,13 +15694,13 @@ var ceilingOf = (config) => {
|
|
|
15509
15694
|
const parsed = typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
|
|
15510
15695
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : AGENTS_MAX_LINES;
|
|
15511
15696
|
};
|
|
15512
|
-
var lineCount2 = (
|
|
15697
|
+
var lineCount2 = (text8) => text8.replace(/\r?\n$/, "").split(/\r?\n/).length;
|
|
15513
15698
|
var checkAgentsFile = ({ root, runtime }) => {
|
|
15514
|
-
const
|
|
15515
|
-
if (!
|
|
15699
|
+
const text8 = runtime.readText(toAbsolutePath(join37(root, AGENTS_FILE)));
|
|
15700
|
+
if (!text8.ok) return [];
|
|
15516
15701
|
const config = runtime.readText(toAbsolutePath(join37(root, CONFIG_FILE)));
|
|
15517
15702
|
const max = ceilingOf(config.ok ? config.value : void 0);
|
|
15518
|
-
const lines3 = lineCount2(
|
|
15703
|
+
const lines3 = lineCount2(text8.value);
|
|
15519
15704
|
const params2 = { file: AGENTS_FILE, lines: lines3, max };
|
|
15520
15705
|
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)];
|
|
15521
15706
|
};
|
|
@@ -15696,9 +15881,9 @@ var manualLines = (ctx, report2) => report2.checks.some((item2) => item2.status
|
|
|
15696
15881
|
var writeReport = (ctx, data, result) => {
|
|
15697
15882
|
const env = doctorEnvFrom(ctx);
|
|
15698
15883
|
const path = reportPath(env);
|
|
15699
|
-
const
|
|
15884
|
+
const text8 = reportMarkdown(env, ctx, toEnvelope("doctor", result));
|
|
15700
15885
|
const dir = ctx.runtime.mkdir(toAbsolutePath(dirname7(path)));
|
|
15701
|
-
const written = dir.ok ? ctx.runtime.writeAtomic(path,
|
|
15886
|
+
const written = dir.ok ? ctx.runtime.writeAtomic(path, text8) : dir;
|
|
15702
15887
|
if (!written.ok) return failureFrom(ctx.t, applyProblem({ path, detail: written.error }));
|
|
15703
15888
|
const human2 = [...result.human ?? [], ctx.t("doctor.report.written", { path })];
|
|
15704
15889
|
return { ...result, data: { ...data, reportPath: path }, human: human2 };
|
|
@@ -16024,15 +16209,15 @@ var commitMsgCommand = {
|
|
|
16024
16209
|
usageProblem("invalid_arguments", "error.invalid_arguments", COMMIT_MSG_USAGE, { detail: "<message-file>" })
|
|
16025
16210
|
)
|
|
16026
16211
|
);
|
|
16027
|
-
const
|
|
16028
|
-
if (!
|
|
16212
|
+
const text8 = ctx.runtime.readText(toAbsolutePath(file, ctx.cwd));
|
|
16213
|
+
if (!text8.ok)
|
|
16029
16214
|
return Promise.resolve(
|
|
16030
16215
|
failureFrom(
|
|
16031
16216
|
ctx.t,
|
|
16032
16217
|
usageProblem("invalid_arguments", "error.invalid_arguments", COMMIT_MSG_USAGE, { detail: file })
|
|
16033
16218
|
)
|
|
16034
16219
|
);
|
|
16035
|
-
const failures = commitMessageProblems(
|
|
16220
|
+
const failures = commitMessageProblems(text8.value);
|
|
16036
16221
|
const fix = "git commit --amend, using type(scope): subject and no AI attribution lines";
|
|
16037
16222
|
return Promise.resolve(
|
|
16038
16223
|
checksResult(ctx.t, COMMIT_MSG_SCHEMA, "githook_failed", [{ id: "commit-messages", failures, fix }])
|
|
@@ -16170,7 +16355,7 @@ var mergeVerifyCommand = {
|
|
|
16170
16355
|
var REFLOG_LIMIT = 300;
|
|
16171
16356
|
var LIST_LIMIT = 50;
|
|
16172
16357
|
var SHA = /^[0-9a-f]{40,64}$/;
|
|
16173
|
-
var linesOf = (
|
|
16358
|
+
var linesOf = (text8) => text8.split("\n").filter((line2) => line2.trim() !== "");
|
|
16174
16359
|
var reflogLines = (runtime, repo) => {
|
|
16175
16360
|
const result = runtime.git(["reflog", "show", "--all", `-n${String(REFLOG_LIMIT)}`, "--format=%H %gD"], repo.root);
|
|
16176
16361
|
if (!result.ok) return [];
|
|
@@ -16459,7 +16644,7 @@ var GLOBAL_FLAGS = [
|
|
|
16459
16644
|
{ flag: "--help, -h", key: "flag.help" }
|
|
16460
16645
|
];
|
|
16461
16646
|
var exitCodeHelp = (ctx) => EXIT_CODE_NAMES.map((name) => ({ code: ExitCode[name], name, summary: ctx.t(exitCodeDocs[ExitCode[name]]) }));
|
|
16462
|
-
var pad = (
|
|
16647
|
+
var pad = (text8, width) => text8.padEnd(Math.max(width, text8.length + 1));
|
|
16463
16648
|
var describe2 = (ctx, spec2) => ({
|
|
16464
16649
|
name: commandName(spec2),
|
|
16465
16650
|
usage: spec2.usage,
|
|
@@ -16601,10 +16786,10 @@ var parseBranches = (body2, inPlural) => {
|
|
|
16601
16786
|
};
|
|
16602
16787
|
var parseNodes = (source, inPlural) => {
|
|
16603
16788
|
const nodes = [];
|
|
16604
|
-
let
|
|
16789
|
+
let text8 = "";
|
|
16605
16790
|
const flush2 = () => {
|
|
16606
|
-
if (
|
|
16607
|
-
|
|
16791
|
+
if (text8 !== "") nodes.push({ kind: "text", text: text8 });
|
|
16792
|
+
text8 = "";
|
|
16608
16793
|
};
|
|
16609
16794
|
for (let index = 0; index < source.length; index += 1) {
|
|
16610
16795
|
const char = source.charAt(index);
|
|
@@ -16614,7 +16799,7 @@ var parseNodes = (source, inPlural) => {
|
|
|
16614
16799
|
continue;
|
|
16615
16800
|
}
|
|
16616
16801
|
if (char !== "{") {
|
|
16617
|
-
|
|
16802
|
+
text8 += char;
|
|
16618
16803
|
continue;
|
|
16619
16804
|
}
|
|
16620
16805
|
const cursor = { source, index: index + 1 };
|
|
@@ -16622,18 +16807,18 @@ var parseNodes = (source, inPlural) => {
|
|
|
16622
16807
|
const inner = end < 0 ? void 0 : source.slice(index + 1, end);
|
|
16623
16808
|
const match = inner === void 0 ? null : ARG.exec(inner);
|
|
16624
16809
|
if (inner === void 0 || match === null) {
|
|
16625
|
-
|
|
16810
|
+
text8 += char;
|
|
16626
16811
|
continue;
|
|
16627
16812
|
}
|
|
16628
16813
|
const name = match[1] ?? "";
|
|
16629
16814
|
const type = match[2] === "plural" || match[2] === "select" ? match[2] : void 0;
|
|
16630
16815
|
if (type === void 0 && match[0].trim().length !== inner.trim().length) {
|
|
16631
|
-
|
|
16816
|
+
text8 += char;
|
|
16632
16817
|
continue;
|
|
16633
16818
|
}
|
|
16634
16819
|
const branches = type === void 0 ? void 0 : parseBranches(inner.slice(match[0].length), type === "plural");
|
|
16635
16820
|
if (type !== void 0 && branches === void 0) {
|
|
16636
|
-
|
|
16821
|
+
text8 += char;
|
|
16637
16822
|
continue;
|
|
16638
16823
|
}
|
|
16639
16824
|
flush2();
|
|
@@ -16756,8 +16941,8 @@ var RELEASE_HINTS = [
|
|
|
16756
16941
|
/\bFINITEMONKEY_[A-Z_]+/,
|
|
16757
16942
|
/\b[A-Z][A-Z0-9_]{2,}=/
|
|
16758
16943
|
];
|
|
16759
|
-
var releaseHints = (
|
|
16760
|
-
const match = pattern.exec(
|
|
16944
|
+
var releaseHints = (text8) => RELEASE_HINTS.flatMap((pattern) => {
|
|
16945
|
+
const match = pattern.exec(text8);
|
|
16761
16946
|
return match === null ? [] : [match[0]];
|
|
16762
16947
|
});
|
|
16763
16948
|
var hintProblems = (lang, catalog) => Object.keys(catalog).filter((key) => MODEL_FACING.test(key)).sort().flatMap((key) => {
|
|
@@ -16796,9 +16981,9 @@ var unreadable = (dir, file, detail) => ({
|
|
|
16796
16981
|
});
|
|
16797
16982
|
var readCatalog = (ctx, dir, lang) => {
|
|
16798
16983
|
const file = toAbsolutePath(join40(dir, `${lang}.json`));
|
|
16799
|
-
const
|
|
16800
|
-
if (!
|
|
16801
|
-
const parsed = parseJsonObject(
|
|
16984
|
+
const text8 = ctx.runtime.readText(file);
|
|
16985
|
+
if (!text8.ok) return reject(unreadable(dir, file, text8.error));
|
|
16986
|
+
const parsed = parseJsonObject(text8.value);
|
|
16802
16987
|
return parsed.ok ? parsed : reject(unreadable(dir, file, parsed.error));
|
|
16803
16988
|
};
|
|
16804
16989
|
var loadCatalogs = (ctx, dir) => {
|
|
@@ -16818,8 +17003,8 @@ var describeProblem = (ctx, problem4) => {
|
|
|
16818
17003
|
return ` ${problem4.lang} ${problem4.key} ${kind}`;
|
|
16819
17004
|
};
|
|
16820
17005
|
var loadReview = (ctx, dir) => {
|
|
16821
|
-
const
|
|
16822
|
-
const parsed =
|
|
17006
|
+
const text8 = ctx.runtime.readText(toAbsolutePath(join40(dir, "review.json")));
|
|
17007
|
+
const parsed = text8.ok ? parseJsonObject(text8.value) : void 0;
|
|
16823
17008
|
if (parsed?.ok !== true) return void 0;
|
|
16824
17009
|
const statuses = (lang) => {
|
|
16825
17010
|
const value = parsed.value[lang];
|
|
@@ -16964,8 +17149,8 @@ var addResults = (draft, blocks) => {
|
|
|
16964
17149
|
let any = false;
|
|
16965
17150
|
for (const block4 of blocks) {
|
|
16966
17151
|
if (!isRecord(block4) || block4["type"] !== "tool_result" || typeof block4["tool_use_id"] !== "string") continue;
|
|
16967
|
-
const
|
|
16968
|
-
draft.results.set(block4["tool_use_id"], { failed: block4["is_error"] === true || FAILED_EXIT.test(
|
|
17152
|
+
const text8 = textOf3(block4["content"]);
|
|
17153
|
+
draft.results.set(block4["tool_use_id"], { failed: block4["is_error"] === true || FAILED_EXIT.test(text8), text: text8 });
|
|
16969
17154
|
any = true;
|
|
16970
17155
|
}
|
|
16971
17156
|
return any;
|
|
@@ -17016,9 +17201,9 @@ var lookBack = (runtime, root, env) => {
|
|
|
17016
17201
|
};
|
|
17017
17202
|
let turns = 0;
|
|
17018
17203
|
for (const name of files) {
|
|
17019
|
-
const
|
|
17020
|
-
if (!
|
|
17021
|
-
for (const turn of parseTranscript(
|
|
17204
|
+
const text8 = runtime.readText(toAbsolutePath(join41(dir, name)));
|
|
17205
|
+
if (!text8.ok) continue;
|
|
17206
|
+
for (const turn of parseTranscript(text8.value)) {
|
|
17022
17207
|
turns += 1;
|
|
17023
17208
|
for (const hit3 of hitsIn(turn)) {
|
|
17024
17209
|
counts[hit3.pattern] += 1;
|
|
@@ -17066,7 +17251,7 @@ var walk2 = (a, b) => {
|
|
|
17066
17251
|
}
|
|
17067
17252
|
return ops;
|
|
17068
17253
|
};
|
|
17069
|
-
var splitLines = (
|
|
17254
|
+
var splitLines = (text8) => text8 === "" ? [] : text8.replace(/\r?\n$/, "").split(/\r?\n/);
|
|
17070
17255
|
var sharedPrefix = (a, b) => {
|
|
17071
17256
|
let count4 = 0;
|
|
17072
17257
|
while (count4 < a.length && count4 < b.length && a[count4] === b[count4]) count4 += 1;
|
|
@@ -17143,7 +17328,7 @@ var planData = (t, plan, mode, applied, backup) => ({
|
|
|
17143
17328
|
diff: planDiff(plan),
|
|
17144
17329
|
...backup === void 0 ? {} : { backup }
|
|
17145
17330
|
});
|
|
17146
|
-
var pad2 = (
|
|
17331
|
+
var pad2 = (text8, width) => text8.padEnd(width);
|
|
17147
17332
|
var planLines = (t, data, withDiff) => [
|
|
17148
17333
|
t("install.plan.title", { root: data.root, preset: data.preset }),
|
|
17149
17334
|
...data.actions.map(({ path, action }) => ` ${pad2(action, 13)}${path}`),
|
|
@@ -17279,8 +17464,8 @@ var measurePage = (doc) => {
|
|
|
17279
17464
|
const walker = doc.createTreeWalker(doc.body, SHOW_TEXT);
|
|
17280
17465
|
const keys = /* @__PURE__ */ new Set();
|
|
17281
17466
|
for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
|
|
17282
|
-
const
|
|
17283
|
-
if (KEY2.test(
|
|
17467
|
+
const text8 = (node.textContent ?? "").trim();
|
|
17468
|
+
if (KEY2.test(text8) && !NOT_KEY.test(text8) || PLACEHOLDER2.test(text8)) keys.add(text8.slice(0, 80));
|
|
17284
17469
|
}
|
|
17285
17470
|
const small = [...doc.querySelectorAll("a, button, input, select, textarea, [role=button], [role=link]")].filter((element) => {
|
|
17286
17471
|
const box = element.getBoundingClientRect();
|
|
@@ -17313,8 +17498,8 @@ var slugOf = (route) => {
|
|
|
17313
17498
|
var watch = (page) => {
|
|
17314
17499
|
const errors = [];
|
|
17315
17500
|
const failed4 = [];
|
|
17316
|
-
const push = (list4,
|
|
17317
|
-
if (list4.length < MAX_EVENTS) list4.push(
|
|
17501
|
+
const push = (list4, text8) => {
|
|
17502
|
+
if (list4.length < MAX_EVENTS) list4.push(text8.slice(0, 200));
|
|
17318
17503
|
};
|
|
17319
17504
|
page.on("console", (message) => {
|
|
17320
17505
|
if (message.type() === "error" && !RESOURCE_ERROR.test(message.text())) push(errors, message.text());
|
|
@@ -17398,7 +17583,7 @@ var launchBrowser = async (playwright) => {
|
|
|
17398
17583
|
// src/look/node-sheet.ts
|
|
17399
17584
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
17400
17585
|
import { join as join44 } from "node:path";
|
|
17401
|
-
var escapeHtml = (
|
|
17586
|
+
var escapeHtml = (text8) => text8.replace(/[&<>"]/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[char] ?? char);
|
|
17402
17587
|
var card = (result) => {
|
|
17403
17588
|
const image = (() => {
|
|
17404
17589
|
try {
|
|
@@ -17759,6 +17944,96 @@ var proseCheckCommand = {
|
|
|
17759
17944
|
}
|
|
17760
17945
|
};
|
|
17761
17946
|
|
|
17947
|
+
// src/review/packet.ts
|
|
17948
|
+
import { join as join45 } from "node:path";
|
|
17949
|
+
var REVIEW_STATE_FILE = ".finitemonkey/review/state.json";
|
|
17950
|
+
var FULL_ROUNDS = 3;
|
|
17951
|
+
var REVIEW_FILE_MAX_LINES = 200;
|
|
17952
|
+
var LEDGER_MAX_LINES = 100;
|
|
17953
|
+
var statePath = (root) => toAbsolutePath(join45(root, REVIEW_STATE_FILE));
|
|
17954
|
+
var readReviewState = (runtime, root) => {
|
|
17955
|
+
const raw = runtime.readText(statePath(root));
|
|
17956
|
+
const parsed = raw.ok ? parseJsonObject(raw.value) : void 0;
|
|
17957
|
+
if (parsed === void 0 || !parsed.ok || !isRecord(parsed.value)) return void 0;
|
|
17958
|
+
const { base, rounds, lastHead } = parsed.value;
|
|
17959
|
+
return typeof base === "string" && typeof rounds === "number" && typeof lastHead === "string" ? { base, rounds, lastHead } : void 0;
|
|
17960
|
+
};
|
|
17961
|
+
var git = (runtime, root, args2) => {
|
|
17962
|
+
const result = runtime.git(args2, root);
|
|
17963
|
+
return result.ok ? result.stdout : void 0;
|
|
17964
|
+
};
|
|
17965
|
+
var lastLines = (text8, max) => text8.split("\n").slice(-max).join("\n");
|
|
17966
|
+
var section = (title, body2, empty) => `## ${title}
|
|
17967
|
+
|
|
17968
|
+
${body2 === void 0 || body2.trim() === "" ? empty : body2.trimEnd()}
|
|
17969
|
+
`;
|
|
17970
|
+
var taskSections = (runtime, root) => {
|
|
17971
|
+
const paths = fmPaths(root);
|
|
17972
|
+
const active = readState(runtime, paths).active;
|
|
17973
|
+
if (active === void 0)
|
|
17974
|
+
return [section("Contract", void 0, "(no active task)"), section("Evidence ledger", void 0, "(none)")];
|
|
17975
|
+
const contract = runtime.readText(toAbsolutePath(join45(root, taskRelative(active))));
|
|
17976
|
+
const ledger = runtime.readText(paths.evidence(active));
|
|
17977
|
+
return [
|
|
17978
|
+
section("Contract", contract.ok ? contract.value : void 0, "(contract file missing)"),
|
|
17979
|
+
section("Evidence ledger", ledger.ok ? lastLines(ledger.value, LEDGER_MAX_LINES) : void 0, "(none)")
|
|
17980
|
+
];
|
|
17981
|
+
};
|
|
17982
|
+
var reviewFile = (runtime, root, base) => {
|
|
17983
|
+
const text8 = git(runtime, root, ["show", `${base}:REVIEW.md`]);
|
|
17984
|
+
return text8 === void 0 ? void 0 : text8.split("\n").slice(0, REVIEW_FILE_MAX_LINES).join("\n");
|
|
17985
|
+
};
|
|
17986
|
+
var nextState = (previous, base, head) => {
|
|
17987
|
+
if (previous?.base !== base) return { base, rounds: 1, lastHead: head };
|
|
17988
|
+
return { base, rounds: previous.rounds + 1, lastHead: head };
|
|
17989
|
+
};
|
|
17990
|
+
var buildPacket = (runtime, root, base, head) => {
|
|
17991
|
+
const previous = readReviewState(runtime, root);
|
|
17992
|
+
const state = nextState(previous, base, head);
|
|
17993
|
+
const from = state.rounds > FULL_ROUNDS && previous !== void 0 ? previous.lastHead : base;
|
|
17994
|
+
const range = `${from}...${head}`;
|
|
17995
|
+
const diff = git(runtime, root, ["diff", "--no-color", "--no-ext-diff", range, "--", ".", ":(exclude).finitemonkey"]);
|
|
17996
|
+
const owners = reviewFile(runtime, root, base);
|
|
17997
|
+
const text8 = [
|
|
17998
|
+
`# Review packet (round ${String(state.rounds)}, ${range})`,
|
|
17999
|
+
"",
|
|
18000
|
+
...state.rounds > FULL_ROUNDS ? ["Only the diff since the last review round is included.", ""] : [],
|
|
18001
|
+
section("Diff", diff, "(empty diff)"),
|
|
18002
|
+
...taskSections(runtime, root),
|
|
18003
|
+
...owners === void 0 ? [] : [section("REVIEW.md (base branch)", owners, "")]
|
|
18004
|
+
].join("\n");
|
|
18005
|
+
runtime.mkdir(toAbsolutePath(join45(root, ".finitemonkey", "review")));
|
|
18006
|
+
runtime.writeAtomic(statePath(root), `${JSON.stringify(state, null, 2)}
|
|
18007
|
+
`);
|
|
18008
|
+
return { round: state.rounds, base, head, range, text: text8 };
|
|
18009
|
+
};
|
|
18010
|
+
|
|
18011
|
+
// src/commands/review.ts
|
|
18012
|
+
var REVIEW_PACKET_SCHEMA = "finitemonkey/review-packet@1";
|
|
18013
|
+
var reviewPacketCommand = {
|
|
18014
|
+
path: ["review", "packet"],
|
|
18015
|
+
summaryKey: "review.command.packet",
|
|
18016
|
+
usage: "finitemonkey review packet [--base <ref>] [--json]",
|
|
18017
|
+
status: "ready",
|
|
18018
|
+
options: { base: { type: "string" } },
|
|
18019
|
+
run: (ctx, args2) => {
|
|
18020
|
+
const repo = repoOf2(ctx);
|
|
18021
|
+
if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
|
|
18022
|
+
const base = stringOption(args2.values, "base") ?? repoFacts(ctx.runtime, repo).defaultBranch();
|
|
18023
|
+
const resolved = ctx.runtime.git(["rev-parse", "--verify", "--quiet", `${base}^{commit}`], repo.root);
|
|
18024
|
+
const head = ctx.runtime.git(["rev-parse", "HEAD"], repo.root).stdout.trim();
|
|
18025
|
+
if (!resolved.ok || head === "")
|
|
18026
|
+
return Promise.resolve(
|
|
18027
|
+
failureFrom(
|
|
18028
|
+
ctx.t,
|
|
18029
|
+
gitProblem("invalid_arguments", "review.error.no_base", base, "finitemonkey review packet --base <ref>")
|
|
18030
|
+
)
|
|
18031
|
+
);
|
|
18032
|
+
const packet = buildPacket(ctx.runtime, repo.root, base, head);
|
|
18033
|
+
return Promise.resolve(success({ schema: REVIEW_PACKET_SCHEMA, ...packet }, [packet.text]));
|
|
18034
|
+
}
|
|
18035
|
+
};
|
|
18036
|
+
|
|
17762
18037
|
// src/commands/secrets-problems.ts
|
|
17763
18038
|
var baselineInvalid = (detail) => ({
|
|
17764
18039
|
code: "secrets_baseline_invalid",
|
|
@@ -17882,7 +18157,7 @@ var secretsAcceptCommand = {
|
|
|
17882
18157
|
};
|
|
17883
18158
|
|
|
17884
18159
|
// src/secrets/runbooks.ts
|
|
17885
|
-
import { dirname as dirname9, join as
|
|
18160
|
+
import { dirname as dirname9, join as join46 } from "node:path";
|
|
17886
18161
|
var PROVIDER_OF_RULE = {
|
|
17887
18162
|
"aws-access-token": "aws",
|
|
17888
18163
|
"aws-secret-access-key": "aws",
|
|
@@ -17904,7 +18179,7 @@ var RUNBOOKS_URL = "https://github.com/gabrielvieiraf/finitemonkey/blob/main/run
|
|
|
17904
18179
|
var runbookProvider = (rule) => PROVIDER_OF_RULE[rule] ?? "generic";
|
|
17905
18180
|
var runbookLink = (runtime, rule) => {
|
|
17906
18181
|
const file = `${runbookProvider(rule)}.md`;
|
|
17907
|
-
const local = toAbsolutePath(
|
|
18182
|
+
const local = toAbsolutePath(join46(dirname9(dirname9(runtime.selfPath)), "runbooks", file));
|
|
17908
18183
|
return runtime.stat(local)?.kind === "file" ? local : `${RUNBOOKS_URL}/${file}`;
|
|
17909
18184
|
};
|
|
17910
18185
|
|
|
@@ -18265,8 +18540,206 @@ var secretsScanCommand = {
|
|
|
18265
18540
|
}
|
|
18266
18541
|
};
|
|
18267
18542
|
|
|
18543
|
+
// src/statusline/gate-state.ts
|
|
18544
|
+
var GATE_HOOKS = /* @__PURE__ */ new Set(["Stop", "SubagentStop"]);
|
|
18545
|
+
var TAIL_BYTES = 64 * 1024;
|
|
18546
|
+
var lastGateFailure = (runtime, paths) => {
|
|
18547
|
+
const log = runtime.readText(paths.log);
|
|
18548
|
+
if (!log.ok) return void 0;
|
|
18549
|
+
const lines3 = log.value.slice(-TAIL_BYTES).split("\n").reverse();
|
|
18550
|
+
for (const line2 of lines3) {
|
|
18551
|
+
if (!line2.includes(HOOK_FAILURE_EVENT)) continue;
|
|
18552
|
+
const parsed = parseJsonObject(line2);
|
|
18553
|
+
if (!parsed.ok || !isRecord(parsed.value)) continue;
|
|
18554
|
+
const { event, hook, at } = parsed.value;
|
|
18555
|
+
if (event === HOOK_FAILURE_EVENT && typeof hook === "string" && GATE_HOOKS.has(hook) && typeof at === "string")
|
|
18556
|
+
return at;
|
|
18557
|
+
}
|
|
18558
|
+
return void 0;
|
|
18559
|
+
};
|
|
18560
|
+
var gateLight = (runtime, paths) => {
|
|
18561
|
+
const status = readGateStatus(runtime, paths);
|
|
18562
|
+
const failure4 = lastGateFailure(runtime, paths);
|
|
18563
|
+
const greenSince = status?.status === "passed" ? Date.parse(status.at) : Number.NEGATIVE_INFINITY;
|
|
18564
|
+
if (failure4 !== void 0 && Date.parse(failure4) >= greenSince) return "did_not_run";
|
|
18565
|
+
if (status === void 0) return "none";
|
|
18566
|
+
switch (status.status) {
|
|
18567
|
+
case "passed":
|
|
18568
|
+
return "green";
|
|
18569
|
+
case "failed":
|
|
18570
|
+
case "not_done":
|
|
18571
|
+
return "red";
|
|
18572
|
+
case "incomplete":
|
|
18573
|
+
return "incomplete";
|
|
18574
|
+
case "not_run":
|
|
18575
|
+
return "did_not_run";
|
|
18576
|
+
}
|
|
18577
|
+
};
|
|
18578
|
+
|
|
18579
|
+
// src/statusline/input.ts
|
|
18580
|
+
var text7 = (value) => typeof value === "string" && value !== "" ? value : void 0;
|
|
18581
|
+
var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
18582
|
+
var usedTokens = (usage) => {
|
|
18583
|
+
if (!isRecord(usage)) return void 0;
|
|
18584
|
+
return (num(usage["input_tokens"]) ?? 0) + (num(usage["cache_creation_input_tokens"]) ?? 0) + (num(usage["cache_read_input_tokens"]) ?? 0);
|
|
18585
|
+
};
|
|
18586
|
+
var contextPercent = (record3) => {
|
|
18587
|
+
const window = record3["context_window"];
|
|
18588
|
+
if (!isRecord(window)) return void 0;
|
|
18589
|
+
const direct = window["used_percentage"];
|
|
18590
|
+
if (typeof direct === "number" && Number.isFinite(direct)) return Math.round(direct);
|
|
18591
|
+
const size = window["context_window_size"];
|
|
18592
|
+
const used = usedTokens(window["current_usage"]);
|
|
18593
|
+
return typeof size === "number" && size > 0 && used !== void 0 ? Math.round(used / size * 100) : void 0;
|
|
18594
|
+
};
|
|
18595
|
+
var parseStatusInput = (raw) => {
|
|
18596
|
+
const parsed = parseJsonObject(raw.trim() === "" ? "{}" : raw);
|
|
18597
|
+
const record3 = parsed.ok ? parsed.value : {};
|
|
18598
|
+
const workspace = isRecord(record3["workspace"]) ? record3["workspace"] : {};
|
|
18599
|
+
return {
|
|
18600
|
+
sessionId: text7(record3["session_id"]),
|
|
18601
|
+
dir: text7(workspace["project_dir"]) ?? text7(workspace["current_dir"]) ?? text7(record3["cwd"]),
|
|
18602
|
+
contextPercent: contextPercent(record3),
|
|
18603
|
+
raw
|
|
18604
|
+
};
|
|
18605
|
+
};
|
|
18606
|
+
|
|
18607
|
+
// src/statusline/render.ts
|
|
18608
|
+
var SEPARATOR2 = " | ";
|
|
18609
|
+
var segmentText = (t, segment) => [
|
|
18610
|
+
"fm",
|
|
18611
|
+
...segment.task === void 0 ? [] : [segment.task],
|
|
18612
|
+
...segment.gate === "none" ? [] : [t(`statusline.gate.${segment.gate}`)],
|
|
18613
|
+
...segment.contextPercent === void 0 ? [] : [t("statusline.context", { percent: segment.contextPercent })]
|
|
18614
|
+
].join(" · ");
|
|
18615
|
+
var ANSI3 = new RegExp(`${String.fromCharCode(27)}\\[[0-9;?]*[A-Za-z]`, "g");
|
|
18616
|
+
var graphemes = (text8) => Array.from(new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(text8), (part2) => part2.segment);
|
|
18617
|
+
var fit = (line2, columns) => {
|
|
18618
|
+
if (columns === void 0) return line2;
|
|
18619
|
+
const visible = graphemes(line2.replace(ANSI3, ""));
|
|
18620
|
+
return visible.length <= columns ? line2 : `${visible.slice(0, Math.max(0, columns - 1)).join("")}…`;
|
|
18621
|
+
};
|
|
18622
|
+
var composeLine = (wrapped, segment, columns) => {
|
|
18623
|
+
const lines3 = wrapped === "" ? [] : wrapped.split(/\r?\n/);
|
|
18624
|
+
const last = lines3.pop();
|
|
18625
|
+
const joined = last === void 0 || last.trim() === "" ? segment : `${last}${SEPARATOR2}${segment}`;
|
|
18626
|
+
return [...lines3, joined].map((line2) => fit(line2, columns)).join("\n");
|
|
18627
|
+
};
|
|
18628
|
+
|
|
18629
|
+
// src/statusline/node-shell.ts
|
|
18630
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
18631
|
+
var killTree = (child2) => {
|
|
18632
|
+
const pid = child2.pid;
|
|
18633
|
+
if (pid === void 0) return;
|
|
18634
|
+
try {
|
|
18635
|
+
if (process.platform === "win32") {
|
|
18636
|
+
spawn4("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true }).unref();
|
|
18637
|
+
} else process.kill(-pid, "SIGKILL");
|
|
18638
|
+
} catch {
|
|
18639
|
+
child2.kill("SIGKILL");
|
|
18640
|
+
}
|
|
18641
|
+
};
|
|
18642
|
+
var nodeShell = (command, input, cwd, timeoutMs) => new Promise((resolve2) => {
|
|
18643
|
+
const child2 = spawn4(command, {
|
|
18644
|
+
cwd,
|
|
18645
|
+
shell: true,
|
|
18646
|
+
detached: process.platform !== "win32",
|
|
18647
|
+
windowsHide: true,
|
|
18648
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
18649
|
+
});
|
|
18650
|
+
let out = "";
|
|
18651
|
+
const timer = setTimeout(() => {
|
|
18652
|
+
killTree(child2);
|
|
18653
|
+
resolve2({ text: void 0, timedOut: true });
|
|
18654
|
+
}, timeoutMs);
|
|
18655
|
+
child2.stdout.on("data", (chunk) => {
|
|
18656
|
+
out += chunk.toString("utf8");
|
|
18657
|
+
});
|
|
18658
|
+
child2.once("error", () => {
|
|
18659
|
+
clearTimeout(timer);
|
|
18660
|
+
resolve2({ text: void 0, timedOut: false });
|
|
18661
|
+
});
|
|
18662
|
+
child2.once("close", () => {
|
|
18663
|
+
clearTimeout(timer);
|
|
18664
|
+
resolve2({ text: out, timedOut: false });
|
|
18665
|
+
});
|
|
18666
|
+
child2.stdin.on("error", () => void 0);
|
|
18667
|
+
child2.stdin.end(input);
|
|
18668
|
+
});
|
|
18669
|
+
|
|
18670
|
+
// src/statusline/wrap.ts
|
|
18671
|
+
var WRAP_CACHE = "statusline.json";
|
|
18672
|
+
var KEEP3 = 20;
|
|
18673
|
+
var cached2 = (runtime, paths) => {
|
|
18674
|
+
const raw = readJsonFile(runtime, paths.cache(WRAP_CACHE))?.["sessions"];
|
|
18675
|
+
return isRecord(raw) ? Object.fromEntries(Object.entries(raw).filter((entry2) => typeof entry2[1] === "string")) : {};
|
|
18676
|
+
};
|
|
18677
|
+
var remember = (runtime, paths, session, text8) => {
|
|
18678
|
+
const sessions = Object.entries({ ...cached2(runtime, paths), [session]: text8 }).slice(-KEEP3);
|
|
18679
|
+
writeJsonFile(runtime, paths.cache(WRAP_CACHE), { sessions: Object.fromEntries(sessions) });
|
|
18680
|
+
};
|
|
18681
|
+
var runWrapped = async (runtime, paths, request, shell = nodeShell) => {
|
|
18682
|
+
const result = await shell(request.command, request.input, request.cwd, request.timeoutMs);
|
|
18683
|
+
if (result.text !== void 0) {
|
|
18684
|
+
const text8 = result.text.replace(/\s+$/, "");
|
|
18685
|
+
if (paths !== void 0) remember(runtime, paths, request.session, text8);
|
|
18686
|
+
return text8;
|
|
18687
|
+
}
|
|
18688
|
+
return paths === void 0 ? "" : cached2(runtime, paths)[request.session] ?? "";
|
|
18689
|
+
};
|
|
18690
|
+
|
|
18691
|
+
// src/statusline/run.ts
|
|
18692
|
+
var STDIN_MS = 300;
|
|
18693
|
+
var WRAP_MS = 700;
|
|
18694
|
+
var wrapOf = (ctx, root) => {
|
|
18695
|
+
const text8 = ctx.runtime.readText(toAbsolutePath(`${root}/${LOCK_FILE}`));
|
|
18696
|
+
const lock = text8.ok ? parseLock(text8.value) : void 0;
|
|
18697
|
+
if (!lock?.ok) return void 0;
|
|
18698
|
+
return lock.value.entries.find((entry2) => entry2.added?.statusLine !== void 0)?.added?.statusLine?.wrap ?? void 0;
|
|
18699
|
+
};
|
|
18700
|
+
var columnsOf = (ctx) => {
|
|
18701
|
+
const columns = Number.parseInt(ctx.env["COLUMNS"] ?? "", 10);
|
|
18702
|
+
return Number.isInteger(columns) && columns > 0 ? columns : void 0;
|
|
18703
|
+
};
|
|
18704
|
+
var lineFor = async (ctx, input) => {
|
|
18705
|
+
const start = input.dir === void 0 ? ctx.cwd : toAbsolutePath(input.dir, ctx.cwd);
|
|
18706
|
+
const root = findGitRoot(start, ctx.runtime);
|
|
18707
|
+
const paths = root === void 0 ? void 0 : fmPaths(root);
|
|
18708
|
+
const wrap = root === void 0 ? void 0 : wrapOf(ctx, root);
|
|
18709
|
+
const wrapped = wrap === void 0 || root === void 0 ? "" : await runWrapped(ctx.runtime, paths, {
|
|
18710
|
+
command: wrap,
|
|
18711
|
+
input: input.raw,
|
|
18712
|
+
cwd: root,
|
|
18713
|
+
timeoutMs: WRAP_MS,
|
|
18714
|
+
session: input.sessionId ?? "none"
|
|
18715
|
+
});
|
|
18716
|
+
const segment = segmentText(ctx.t, {
|
|
18717
|
+
task: paths === void 0 ? void 0 : readState(ctx.runtime, paths).active,
|
|
18718
|
+
gate: paths === void 0 ? "none" : gateLight(ctx.runtime, paths),
|
|
18719
|
+
contextPercent: input.contextPercent
|
|
18720
|
+
});
|
|
18721
|
+
return composeLine(wrapped, segment, columnsOf(ctx));
|
|
18722
|
+
};
|
|
18723
|
+
var renderStatusLine = async (ctx) => {
|
|
18724
|
+
try {
|
|
18725
|
+
const raw = await withDeadline(readAll(ctx.io.stdin), STDIN_MS, ctx.runtime.startTimer).catch(() => "");
|
|
18726
|
+
return await lineFor(ctx, parseStatusInput(raw));
|
|
18727
|
+
} catch {
|
|
18728
|
+
return "fm";
|
|
18729
|
+
}
|
|
18730
|
+
};
|
|
18731
|
+
|
|
18732
|
+
// src/commands/statusline.ts
|
|
18733
|
+
var statuslineCommand = {
|
|
18734
|
+
path: ["statusline"],
|
|
18735
|
+
summaryKey: "command.statusline.summary",
|
|
18736
|
+
usage: "finitemonkey statusline",
|
|
18737
|
+
status: "ready",
|
|
18738
|
+
run: async (ctx) => success({}, [await renderStatusLine(ctx)])
|
|
18739
|
+
};
|
|
18740
|
+
|
|
18268
18741
|
// src/task/create.ts
|
|
18269
|
-
import { join as
|
|
18742
|
+
import { join as join47 } from "node:path";
|
|
18270
18743
|
|
|
18271
18744
|
// src/core/yaml/write.ts
|
|
18272
18745
|
var PLAIN_SAFE = /^[A-Za-z_./][A-Za-z0-9_./ ()-]*$/;
|
|
@@ -18355,10 +18828,10 @@ var explicitChecks = (draft) => [
|
|
|
18355
18828
|
var draftChecks = (runtime, paths, draft) => {
|
|
18356
18829
|
const disk = {
|
|
18357
18830
|
read: (relative11) => {
|
|
18358
|
-
const
|
|
18359
|
-
return
|
|
18831
|
+
const text8 = runtime.readText(toAbsolutePath(join47(paths.root, relative11)));
|
|
18832
|
+
return text8.ok ? text8.value : void 0;
|
|
18360
18833
|
},
|
|
18361
|
-
exists: (relative11) => runtime.stat(toAbsolutePath(
|
|
18834
|
+
exists: (relative11) => runtime.stat(toAbsolutePath(join47(paths.root, relative11))) !== void 0
|
|
18362
18835
|
};
|
|
18363
18836
|
const detected = detectChecks(disk);
|
|
18364
18837
|
const base = draft.runs.length > 0 || draft.tests !== void 0 ? explicitChecks(draft) : detected;
|
|
@@ -18373,7 +18846,7 @@ var draftChecks = (runtime, paths, draft) => {
|
|
|
18373
18846
|
{ id: "merge", kind: "merge" },
|
|
18374
18847
|
{ id: "callers", kind: "callers" },
|
|
18375
18848
|
...draft.paths.length > 0 || draft.budget !== void 0 ? [{ id: "diff", kind: "diff" }] : [],
|
|
18376
|
-
...runtime.stat(toAbsolutePath(
|
|
18849
|
+
...runtime.stat(toAbsolutePath(join47(paths.root, LOOK_CONFIG))) === void 0 ? [] : [{ id: "look", kind: "look" }],
|
|
18377
18850
|
...draft.git.length > 0 ? [{ id: "git", kind: "git", require: draft.git }] : [],
|
|
18378
18851
|
{ id: "pr", kind: "pr", requireOpen: draft.git.includes("pr_open") },
|
|
18379
18852
|
...draft.manual.map((description, index) => ({
|
|
@@ -18399,19 +18872,19 @@ var createTask = (runtime, paths, draft) => {
|
|
|
18399
18872
|
decisionsNeeded: [],
|
|
18400
18873
|
implicit: false
|
|
18401
18874
|
};
|
|
18402
|
-
const
|
|
18875
|
+
const text8 = stringifyYaml(contractYaml(shape));
|
|
18403
18876
|
const valid = validateContract(contractYaml(shape));
|
|
18404
18877
|
if (!valid.ok) return reject({ kind: "invalid", issues: valid.error });
|
|
18405
|
-
const wrote = runtime.writeText(paths.task(draft.id),
|
|
18878
|
+
const wrote = runtime.writeText(paths.task(draft.id), text8);
|
|
18406
18879
|
if (!wrote.ok) return reject({ kind: "write", detail: wrote.error });
|
|
18407
18880
|
const lock = readTrust(runtime, paths);
|
|
18408
18881
|
writeTrust(runtime, paths, {
|
|
18409
18882
|
...lock,
|
|
18410
18883
|
contracts: { ...lock.contracts, [draft.id]: contractHash(valid.value) },
|
|
18411
|
-
approved: { ...lock.approved, [draft.id]:
|
|
18884
|
+
approved: { ...lock.approved, [draft.id]: text8 }
|
|
18412
18885
|
});
|
|
18413
18886
|
writeState(runtime, paths, { ...readState(runtime, paths), active: draft.id });
|
|
18414
|
-
return accept({ contract: valid.value, text:
|
|
18887
|
+
return accept({ contract: valid.value, text: text8 });
|
|
18415
18888
|
};
|
|
18416
18889
|
|
|
18417
18890
|
// src/commands/task.ts
|
|
@@ -18562,7 +19035,7 @@ var taskDoneCommand = {
|
|
|
18562
19035
|
};
|
|
18563
19036
|
|
|
18564
19037
|
// src/install/githook-block.ts
|
|
18565
|
-
var withoutHookBlock = (
|
|
19038
|
+
var withoutHookBlock = (text8) => withoutManagedBlock(text8, { begin: BLOCK_BEGIN, end: BLOCK_END });
|
|
18566
19039
|
|
|
18567
19040
|
// src/install/uninstall.ts
|
|
18568
19041
|
var blockless = (kind, current2) => {
|
|
@@ -18571,8 +19044,8 @@ var blockless = (kind, current2) => {
|
|
|
18571
19044
|
};
|
|
18572
19045
|
var structural = (entry2, current2) => {
|
|
18573
19046
|
if (entry2.kind === "gitignore" || entry2.kind === "markdown" || entry2.kind === "githook") {
|
|
18574
|
-
const
|
|
18575
|
-
return
|
|
19047
|
+
const text8 = blockless(entry2.kind, current2);
|
|
19048
|
+
return text8.trim() === "" && !entry2.existed ? void 0 : text8;
|
|
18576
19049
|
}
|
|
18577
19050
|
const parsed = parseSettings(current2);
|
|
18578
19051
|
if (!parsed.ok || entry2.added === void 0) return current2;
|
|
@@ -18775,34 +19248,36 @@ var REGISTRY = [
|
|
|
18775
19248
|
pushCheckCommand,
|
|
18776
19249
|
ciGateCommand,
|
|
18777
19250
|
lookCommand,
|
|
19251
|
+
reviewPacketCommand,
|
|
19252
|
+
statuslineCommand,
|
|
18778
19253
|
versionCommand,
|
|
18779
19254
|
helpCommand
|
|
18780
19255
|
];
|
|
18781
19256
|
|
|
18782
19257
|
// src/i18n/lang-settings.ts
|
|
18783
|
-
import { join as
|
|
19258
|
+
import { join as join48 } from "node:path";
|
|
18784
19259
|
var read3 = (runtime, path) => {
|
|
18785
|
-
const
|
|
18786
|
-
return
|
|
19260
|
+
const text8 = runtime.readText(toAbsolutePath(path));
|
|
19261
|
+
return text8.ok ? text8.value : void 0;
|
|
18787
19262
|
};
|
|
18788
19263
|
var configText = (tree, ...path) => {
|
|
18789
19264
|
const value = configAt(tree, ...path);
|
|
18790
19265
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
18791
19266
|
};
|
|
18792
|
-
var languageIn = (
|
|
18793
|
-
if (
|
|
18794
|
-
const parsed = parseJsonObject(
|
|
19267
|
+
var languageIn = (text8) => {
|
|
19268
|
+
if (text8 === void 0) return void 0;
|
|
19269
|
+
const parsed = parseJsonObject(text8);
|
|
18795
19270
|
const value = parsed.ok && isRecord(parsed.value) ? parsed.value["language"] : void 0;
|
|
18796
19271
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
18797
19272
|
};
|
|
18798
|
-
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ??
|
|
19273
|
+
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join48(runtime.homeDir(), ".claude");
|
|
18799
19274
|
var langSettings = (runtime, cwd, env, platform) => {
|
|
18800
19275
|
const root = findGitRoot(cwd, runtime) ?? cwd;
|
|
18801
|
-
const config = parseConfigYaml(read3(runtime,
|
|
19276
|
+
const config = parseConfigYaml(read3(runtime, join48(root, CONFIG_FILE)) ?? "");
|
|
18802
19277
|
const claudeFiles = [
|
|
18803
|
-
|
|
18804
|
-
|
|
18805
|
-
|
|
19278
|
+
join48(root, ".claude", "settings.local.json"),
|
|
19279
|
+
join48(root, ".claude", "settings.json"),
|
|
19280
|
+
join48(claudeDir(runtime, env), "settings.json")
|
|
18806
19281
|
];
|
|
18807
19282
|
return {
|
|
18808
19283
|
config: configText(config, "lang"),
|
|
@@ -18889,8 +19364,8 @@ var resultFromThrown = (thrown, t) => {
|
|
|
18889
19364
|
};
|
|
18890
19365
|
var block3 = (items) => items.length > 0 ? `${items.join("\n")}
|
|
18891
19366
|
` : "";
|
|
18892
|
-
var write2 = (sink,
|
|
18893
|
-
if (
|
|
19367
|
+
var write2 = (sink, text8) => {
|
|
19368
|
+
if (text8 !== "") sink.write(maskText(text8));
|
|
18894
19369
|
};
|
|
18895
19370
|
var render2 = (ctx, command, result) => {
|
|
18896
19371
|
if (result.raw !== void 0) {
|
|
@@ -18959,9 +19434,9 @@ import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
|
18959
19434
|
var UNKNOWN_VERSION = "0.0.0-unknown";
|
|
18960
19435
|
var VERSION_SOURCES = ["../package.json", "../.claude-plugin/plugin.json"];
|
|
18961
19436
|
var versionAt = (readText, moduleUrl, relative11) => {
|
|
18962
|
-
const
|
|
18963
|
-
if (!
|
|
18964
|
-
const parsed = parseJson(
|
|
19437
|
+
const text8 = readText(toAbsolutePath(fileURLToPath4(new URL(relative11, moduleUrl))));
|
|
19438
|
+
if (!text8.ok) return void 0;
|
|
19439
|
+
const parsed = parseJson(text8.value);
|
|
18965
19440
|
if (!parsed.ok || !isRecord(parsed.value)) return void 0;
|
|
18966
19441
|
const version = parsed.value["version"];
|
|
18967
19442
|
return typeof version === "string" ? version : void 0;
|