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