finitemonkey 0.1.0-next.25 → 0.1.0-next.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.es.md +1 -1
- package/README.md +1 -1
- package/README.pt-BR.md +1 -1
- package/dist/finitemonkey.mjs +1296 -893
- 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) => {
|
|
@@ -880,7 +1371,7 @@ var parseStatusLineAdded = (value) => {
|
|
|
880
1371
|
};
|
|
881
1372
|
|
|
882
1373
|
// src/install/lock-file.ts
|
|
883
|
-
var ENTRY_KINDS = ["settings", "gitignore", "generated", "markdown", "githook"];
|
|
1374
|
+
var ENTRY_KINDS = ["settings", "gitignore", "generated", "markdown", "githook", "sandbox"];
|
|
884
1375
|
var strings = (value) => Array.isArray(value) && value.every((item2) => typeof item2 === "string") ? value : void 0;
|
|
885
1376
|
var parseAdded = (value) => {
|
|
886
1377
|
if (!isRecord(value)) return void 0;
|
|
@@ -914,6 +1405,7 @@ var parseEntry = (value) => {
|
|
|
914
1405
|
}
|
|
915
1406
|
const added = parseAdded(value["added"]);
|
|
916
1407
|
const handlers = Array.isArray(value["handlers"]) ? value["handlers"].filter(isRecord) : void 0;
|
|
1408
|
+
const sandbox = isRecord(value["sandbox"]) ? value["sandbox"] : void 0;
|
|
917
1409
|
return {
|
|
918
1410
|
path,
|
|
919
1411
|
kind: known2,
|
|
@@ -921,7 +1413,8 @@ var parseEntry = (value) => {
|
|
|
921
1413
|
hash,
|
|
922
1414
|
...typeof backup === "string" ? { backup } : {},
|
|
923
1415
|
...added === void 0 ? {} : { added },
|
|
924
|
-
...handlers === void 0 ? {} : { handlers }
|
|
1416
|
+
...handlers === void 0 ? {} : { handlers },
|
|
1417
|
+
...sandbox === void 0 ? {} : { sandbox }
|
|
925
1418
|
};
|
|
926
1419
|
};
|
|
927
1420
|
var parseEntries = (value) => {
|
|
@@ -935,130 +1428,44 @@ var fromRecord = (record3) => {
|
|
|
935
1428
|
const entries = parseEntries(record3["entries"]);
|
|
936
1429
|
const createdDirs = strings(record3["createdDirs"]);
|
|
937
1430
|
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") : [];
|
|
1026
|
-
};
|
|
1027
|
-
var configMap = (tree, ...path) => {
|
|
1028
|
-
const node = configAt(tree, ...path);
|
|
1029
|
-
if (!isTree(node)) return {};
|
|
1030
|
-
return Object.fromEntries(
|
|
1031
|
-
Object.entries(node).filter((entry2) => typeof entry2[1] === "string")
|
|
1032
|
-
);
|
|
1431
|
+
if (typeof version !== "string" || typeof installedAt !== "string" || typeof updatedAt !== "string") {
|
|
1432
|
+
return reject("version, installedAt and updatedAt must be text");
|
|
1433
|
+
}
|
|
1434
|
+
if (known2 === void 0 || entries === void 0 || createdDirs === void 0) {
|
|
1435
|
+
return reject("preset, entries or createdDirs is malformed");
|
|
1436
|
+
}
|
|
1437
|
+
return accept({ schema, version, preset: known2, installedAt, updatedAt, createdDirs, entries });
|
|
1033
1438
|
};
|
|
1034
|
-
var
|
|
1035
|
-
const
|
|
1036
|
-
return
|
|
1439
|
+
var parseLock = (text9) => {
|
|
1440
|
+
const parsed = parseJsonObject(text9);
|
|
1441
|
+
return parsed.ok ? fromRecord(parsed.value) : reject(parsed.error);
|
|
1037
1442
|
};
|
|
1443
|
+
var renderLock = (lock) => `${JSON.stringify(lock, null, 2)}
|
|
1444
|
+
`;
|
|
1038
1445
|
|
|
1039
1446
|
// src/install/managed-block.ts
|
|
1040
|
-
var lineBreak = (
|
|
1447
|
+
var lineBreak = (text9) => text9.includes("\r\n") ? "\r\n" : "\n";
|
|
1041
1448
|
var blockRange = (lines3, markers) => {
|
|
1042
1449
|
const start = lines3.findIndex((line2) => line2.trimEnd() === markers.begin);
|
|
1043
1450
|
if (start < 0) return void 0;
|
|
1044
1451
|
const end = lines3.findIndex((line2, index) => index > start && line2.trimEnd() === markers.end);
|
|
1045
1452
|
return end < 0 ? void 0 : { start, end };
|
|
1046
1453
|
};
|
|
1047
|
-
var withManagedBlock = (
|
|
1048
|
-
const eol =
|
|
1454
|
+
var withManagedBlock = (text9, markers, body2) => {
|
|
1455
|
+
const eol = text9 === void 0 ? "\n" : lineBreak(text9);
|
|
1049
1456
|
const block4 = [markers.begin, ...body2, markers.end];
|
|
1050
|
-
if (
|
|
1051
|
-
const lines3 =
|
|
1457
|
+
if (text9 === void 0 || text9 === "") return `${block4.join(eol)}${eol}`;
|
|
1458
|
+
const lines3 = text9.split(eol);
|
|
1052
1459
|
const range = blockRange(lines3, markers);
|
|
1053
1460
|
if (range !== void 0) return [...lines3.slice(0, range.start), ...block4, ...lines3.slice(range.end + 1)].join(eol);
|
|
1054
|
-
const separator =
|
|
1055
|
-
return `${
|
|
1461
|
+
const separator = text9.endsWith(eol) ? "" : eol;
|
|
1462
|
+
return `${text9}${separator}${block4.join(eol)}${eol}`;
|
|
1056
1463
|
};
|
|
1057
|
-
var withoutManagedBlock = (
|
|
1058
|
-
const eol = lineBreak(
|
|
1059
|
-
const lines3 =
|
|
1464
|
+
var withoutManagedBlock = (text9, markers) => {
|
|
1465
|
+
const eol = lineBreak(text9);
|
|
1466
|
+
const lines3 = text9.split(eol);
|
|
1060
1467
|
const range = blockRange(lines3, markers);
|
|
1061
|
-
if (range === void 0) return
|
|
1468
|
+
if (range === void 0) return text9;
|
|
1062
1469
|
return [...lines3.slice(0, range.start), ...lines3.slice(range.end + 1)].join(eol);
|
|
1063
1470
|
};
|
|
1064
1471
|
|
|
@@ -1155,7 +1562,7 @@ var configTemplate = (preset, lang) => [
|
|
|
1155
1562
|
`lang: ${lang}`,
|
|
1156
1563
|
""
|
|
1157
1564
|
].join("\n");
|
|
1158
|
-
var hasMarker = (
|
|
1565
|
+
var hasMarker = (text9) => text9.includes(GENERATED_MARKER);
|
|
1159
1566
|
|
|
1160
1567
|
// src/install/skill-template.ts
|
|
1161
1568
|
var cliCommand = (runner, args2) => {
|
|
@@ -1220,7 +1627,7 @@ var lefthookBody = (runner) => [
|
|
|
1220
1627
|
` run: ${cliCommand(runner, "push check {1}")}`,
|
|
1221
1628
|
" use_stdin: true"
|
|
1222
1629
|
];
|
|
1223
|
-
var definesHook = (
|
|
1630
|
+
var definesHook = (text9) => GIT_HOOK_NAMES.some((hook) => new RegExp(`^${hook}:`, "m").test(withoutManagedBlock(text9, HOOK_MARKERS)));
|
|
1224
1631
|
var lefthookItem = (project, runner, path) => {
|
|
1225
1632
|
const before = project.read(path);
|
|
1226
1633
|
const after = withManagedBlock(before, HOOK_MARKERS, lefthookBody(runner));
|
|
@@ -1245,8 +1652,8 @@ var preCommitBody = (runner, indent) => {
|
|
|
1245
1652
|
];
|
|
1246
1653
|
};
|
|
1247
1654
|
var TOP_KEY = /^[A-Za-z_][\w-]*:/;
|
|
1248
|
-
var reposIsLast = (
|
|
1249
|
-
const keys = withoutManagedBlock(
|
|
1655
|
+
var reposIsLast = (text9) => {
|
|
1656
|
+
const keys = withoutManagedBlock(text9, HOOK_MARKERS).split(/\r?\n/).filter((line2) => TOP_KEY.test(line2));
|
|
1250
1657
|
return keys.at(-1)?.startsWith("repos:") === true;
|
|
1251
1658
|
};
|
|
1252
1659
|
var preCommitItem = (project, runner) => {
|
|
@@ -1269,7 +1676,7 @@ var gitHookItems = (project, runner) => {
|
|
|
1269
1676
|
};
|
|
1270
1677
|
|
|
1271
1678
|
// src/policy/policy.ts
|
|
1272
|
-
import { join as
|
|
1679
|
+
import { join as join5 } from "node:path";
|
|
1273
1680
|
var POLICY_FILE = ".finitemonkey/policy.yml";
|
|
1274
1681
|
var GUARD_MODES = ["off", "shadow", "active"];
|
|
1275
1682
|
var LEVELS = ["light", "standard", "strict"];
|
|
@@ -1284,8 +1691,8 @@ var DEFAULT_POLICY = {
|
|
|
1284
1691
|
guards: {}
|
|
1285
1692
|
};
|
|
1286
1693
|
var pick = (options, value, fallback) => options.find((option) => option === value) ?? fallback;
|
|
1287
|
-
var parsePolicy = (
|
|
1288
|
-
const tree = parseConfigYaml(
|
|
1694
|
+
var parsePolicy = (text9) => {
|
|
1695
|
+
const tree = parseConfigYaml(text9);
|
|
1289
1696
|
const raw = configAt(tree, "promoteAfter");
|
|
1290
1697
|
const promoteAfter = typeof raw === "string" ? Number.parseInt(raw, 10) : Number.NaN;
|
|
1291
1698
|
const guards = Object.fromEntries(
|
|
@@ -1302,10 +1709,10 @@ var parsePolicy = (text8) => {
|
|
|
1302
1709
|
guards
|
|
1303
1710
|
};
|
|
1304
1711
|
};
|
|
1305
|
-
var policyPath = (root) => toAbsolutePath(
|
|
1712
|
+
var policyPath = (root) => toAbsolutePath(join5(root, POLICY_FILE));
|
|
1306
1713
|
var loadPolicy = (runtime, root) => {
|
|
1307
|
-
const
|
|
1308
|
-
return
|
|
1714
|
+
const text9 = runtime.readText(policyPath(root));
|
|
1715
|
+
return text9.ok ? parsePolicy(text9.value) : DEFAULT_POLICY;
|
|
1309
1716
|
};
|
|
1310
1717
|
|
|
1311
1718
|
// src/review/agents.ts
|
|
@@ -1322,6 +1729,7 @@ var REVIEWER_BODY = [
|
|
|
1322
1729
|
"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
1730
|
"4. Every must-fix finding carries a reproduction command and the expected result. A finding without one goes under should fix.",
|
|
1324
1731
|
"5. End with a verdict: approve, approve with changes, or request changes.",
|
|
1732
|
+
'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
1733
|
"",
|
|
1326
1734
|
"You cannot edit files or start other agents. Report; do not repair."
|
|
1327
1735
|
].join("\n");
|
|
@@ -1330,9 +1738,9 @@ var VERIFIER_BODY = [
|
|
|
1330
1738
|
"",
|
|
1331
1739
|
"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
1740
|
"",
|
|
1333
|
-
"1.
|
|
1334
|
-
"2.
|
|
1335
|
-
"3. Report
|
|
1741
|
+
"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`.",
|
|
1742
|
+
"2. Fails only on head: the finding stands. Fails on both: it was already there. Fails on neither: not reproduced.",
|
|
1743
|
+
"3. Report the table it prints, unchanged. Do not rerun a command by hand to get a different answer.",
|
|
1336
1744
|
"",
|
|
1337
1745
|
"You cannot edit files or start other agents. Report; do not repair."
|
|
1338
1746
|
].join("\n");
|
|
@@ -1370,16 +1778,16 @@ var agentMarkdown = (spec2, marker) => [
|
|
|
1370
1778
|
// src/install/agents-block.ts
|
|
1371
1779
|
var MARKDOWN_MARKERS = { begin: MD_BLOCK_BEGIN, end: MD_BLOCK_END };
|
|
1372
1780
|
var IMPORT_LINE = /^@AGENTS\.md[ \t]*$/m;
|
|
1373
|
-
var withAgentsBlock = (
|
|
1374
|
-
|
|
1781
|
+
var withAgentsBlock = (text9, runner, lang) => withManagedBlock(
|
|
1782
|
+
text9,
|
|
1375
1783
|
MARKDOWN_MARKERS,
|
|
1376
1784
|
TEMPLATES[lang].agentsBlock({
|
|
1377
1785
|
gate: cliCommand(runner, "gate --fast --json"),
|
|
1378
1786
|
doctor: cliCommand(runner, "doctor --json")
|
|
1379
1787
|
})
|
|
1380
1788
|
);
|
|
1381
|
-
var withAgentsImport = (
|
|
1382
|
-
var withoutMarkdownBlock = (
|
|
1789
|
+
var withAgentsImport = (text9) => text9 !== void 0 && IMPORT_LINE.test(withoutManagedBlock(text9, MARKDOWN_MARKERS)) ? text9 : withManagedBlock(text9, MARKDOWN_MARKERS, [AGENTS_IMPORT]);
|
|
1790
|
+
var withoutMarkdownBlock = (text9) => withoutManagedBlock(text9, MARKDOWN_MARKERS);
|
|
1383
1791
|
|
|
1384
1792
|
// src/install/ask-floor.ts
|
|
1385
1793
|
var DEFAULT_BRANCHES = ["main", "master"];
|
|
@@ -1455,7 +1863,7 @@ var ruleFor = (raw) => {
|
|
|
1455
1863
|
};
|
|
1456
1864
|
var claudeignoreRules = (texts2) => [
|
|
1457
1865
|
...new Set(
|
|
1458
|
-
texts2.flatMap((
|
|
1866
|
+
texts2.flatMap((text9) => (text9 ?? "").split("\n")).map(ruleFor).filter((rule) => rule !== void 0)
|
|
1459
1867
|
)
|
|
1460
1868
|
];
|
|
1461
1869
|
|
|
@@ -1484,16 +1892,16 @@ var DENY_FLOOR = {
|
|
|
1484
1892
|
var DENY_RULES = [...DENY_FLOOR.secrets, ...DENY_FLOOR.git, ...DENY_FLOOR.proof];
|
|
1485
1893
|
|
|
1486
1894
|
// src/install/detect.ts
|
|
1487
|
-
import { join as
|
|
1895
|
+
import { join as join6 } from "node:path";
|
|
1488
1896
|
|
|
1489
1897
|
// src/install/json-text.ts
|
|
1490
1898
|
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:
|
|
1899
|
+
var styleOf = (text9) => {
|
|
1900
|
+
const indented = /\n([ \t]+)\S/.exec(text9);
|
|
1901
|
+
return { indent: indented?.[1] ?? DEFAULT_STYLE.indent, finalNewline: text9.endsWith("\n") };
|
|
1494
1902
|
};
|
|
1495
1903
|
var renderJson = (value, style) => `${JSON.stringify(value, null, style.indent)}${style.finalNewline ? "\n" : ""}`;
|
|
1496
|
-
var parseSettings = (
|
|
1904
|
+
var parseSettings = (text9) => text9 === void 0 || text9.trim() === "" ? { ok: true, value: {} } : parseJsonObject(text9);
|
|
1497
1905
|
var asList = (value) => Array.isArray(value) ? value : [];
|
|
1498
1906
|
var asRecord = (value) => isRecord(value) ? value : {};
|
|
1499
1907
|
var setKey = (record3, key, value) => {
|
|
@@ -1530,8 +1938,8 @@ var WORKFLOW_DIRS = [
|
|
|
1530
1938
|
{ dir: "openspec", name: "openspec" }
|
|
1531
1939
|
];
|
|
1532
1940
|
var fromPackageJson = (project) => {
|
|
1533
|
-
const
|
|
1534
|
-
const parsed =
|
|
1941
|
+
const text9 = project.read("package.json");
|
|
1942
|
+
const parsed = text9 === void 0 ? void 0 : parseJsonObject(text9);
|
|
1535
1943
|
const field = parsed?.ok === true ? parsed.value["packageManager"] : void 0;
|
|
1536
1944
|
if (typeof field !== "string") return void 0;
|
|
1537
1945
|
return PACKAGE_MANAGERS.find(({ name }) => field.startsWith(`${name}@`))?.name;
|
|
@@ -1547,9 +1955,9 @@ var allHandlers = (settings) => {
|
|
|
1547
1955
|
const hooks = settings["hooks"];
|
|
1548
1956
|
return isRecord(hooks) ? Object.keys(hooks).flatMap((event) => handlersOf(settings, event)) : [];
|
|
1549
1957
|
};
|
|
1550
|
-
var settingsInfo = (
|
|
1551
|
-
if (
|
|
1552
|
-
const parsed =
|
|
1958
|
+
var settingsInfo = (text9) => {
|
|
1959
|
+
if (text9 === void 0) return { exists: false, valid: true, userHooks: 0, ownHooks: 0 };
|
|
1960
|
+
const parsed = text9.trim() === "" ? { ok: true, value: {} } : parseJsonObject(text9);
|
|
1553
1961
|
if (!parsed.ok) return { exists: true, valid: false, userHooks: 0, ownHooks: 0 };
|
|
1554
1962
|
const handlers = allHandlers(parsed.value);
|
|
1555
1963
|
const own = handlers.filter(isOwnHandler).length;
|
|
@@ -1563,8 +1971,8 @@ var pluginAuthorities = (settings, source) => {
|
|
|
1563
1971
|
return LOOP_PLUGINS.includes(name) ? [{ kind: "loop-plugin", source, name }] : [];
|
|
1564
1972
|
});
|
|
1565
1973
|
};
|
|
1566
|
-
var scopeAuthorities = (
|
|
1567
|
-
const parsed =
|
|
1974
|
+
var scopeAuthorities = (text9, source) => {
|
|
1975
|
+
const parsed = text9 === void 0 ? void 0 : parseJsonObject(text9);
|
|
1568
1976
|
if (parsed?.ok !== true) return [];
|
|
1569
1977
|
const stops = ["Stop", "SubagentStop"].flatMap(
|
|
1570
1978
|
(event) => handlersOf(parsed.value, event).filter((handler) => !isOwnHandler(handler)).map((handler) => {
|
|
@@ -1577,9 +1985,9 @@ var scopeAuthorities = (text8, source) => {
|
|
|
1577
1985
|
var userSettingsText = (project, env) => {
|
|
1578
1986
|
const home = env["CLAUDE_CONFIG_DIR"] ?? env["HOME"] ?? env["USERPROFILE"];
|
|
1579
1987
|
if (home === void 0) return void 0;
|
|
1580
|
-
const file = env["CLAUDE_CONFIG_DIR"] === void 0 ?
|
|
1581
|
-
const
|
|
1582
|
-
return
|
|
1988
|
+
const file = env["CLAUDE_CONFIG_DIR"] === void 0 ? join6(home, ".claude", "settings.json") : join6(home, "settings.json");
|
|
1989
|
+
const text9 = project.runtime.readText(toAbsolutePath(file));
|
|
1990
|
+
return text9.ok ? text9.value : void 0;
|
|
1583
1991
|
};
|
|
1584
1992
|
var detectAuthorities = (project, env) => [
|
|
1585
1993
|
...scopeAuthorities(project.read(SETTINGS_FILE), "project"),
|
|
@@ -1599,8 +2007,8 @@ var chooseRunner = (project, bin) => {
|
|
|
1599
2007
|
// src/install/gitignore-block.ts
|
|
1600
2008
|
var MARKERS = { begin: BLOCK_BEGIN, end: BLOCK_END };
|
|
1601
2009
|
var body = () => [`${STATE_DIR}/*`, ...COMMITTED_STATE.map((path) => `!${STATE_DIR}/${path}`)];
|
|
1602
|
-
var withBlock = (
|
|
1603
|
-
var withoutBlock = (
|
|
2010
|
+
var withBlock = (text9) => withManagedBlock(text9, MARKERS, body());
|
|
2011
|
+
var withoutBlock = (text9) => withoutManagedBlock(text9, MARKERS);
|
|
1604
2012
|
|
|
1605
2013
|
// src/core/semver.ts
|
|
1606
2014
|
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 +2051,7 @@ var pendingMigrations = (migrations, from, to) => {
|
|
|
1643
2051
|
};
|
|
1644
2052
|
var migrateItem = (item2, previous, steps) => {
|
|
1645
2053
|
if (steps.length === 0 || item2.before === void 0) return item2;
|
|
1646
|
-
const migrated = steps.reduce((
|
|
2054
|
+
const migrated = steps.reduce((text9, step3) => step3(text9), item2.after);
|
|
1647
2055
|
if (migrated === item2.after) return item2;
|
|
1648
2056
|
const recorded = previous.entries.find((entry2) => entry2.path === item2.path)?.hash;
|
|
1649
2057
|
if (recorded !== hashText(item2.before)) {
|
|
@@ -1681,6 +2089,37 @@ var policyTemplate = (lang) => [
|
|
|
1681
2089
|
""
|
|
1682
2090
|
].join("\n");
|
|
1683
2091
|
|
|
2092
|
+
// src/install/sandbox.ts
|
|
2093
|
+
var SANDBOX_FILE = ".claude/settings.local.json";
|
|
2094
|
+
var DEFAULT_HOSTS = [
|
|
2095
|
+
"registry.npmjs.org",
|
|
2096
|
+
"github.com",
|
|
2097
|
+
"api.github.com",
|
|
2098
|
+
"codeload.github.com",
|
|
2099
|
+
"objects.githubusercontent.com"
|
|
2100
|
+
];
|
|
2101
|
+
var SANDBOX_PLATFORMS = ["darwin", "linux"];
|
|
2102
|
+
var sandboxBlock = (root, hosts) => ({
|
|
2103
|
+
enabled: true,
|
|
2104
|
+
failIfUnavailable: true,
|
|
2105
|
+
filesystem: { denyRead: ["~/.ssh", "~/.aws", `${root}/.env`, `${root}/.env.*`] },
|
|
2106
|
+
network: { allowedDomains: [...hosts] }
|
|
2107
|
+
});
|
|
2108
|
+
var withSandbox = (text9, block4) => {
|
|
2109
|
+
const parsed = parseSettings(text9);
|
|
2110
|
+
if (!parsed.ok) return void 0;
|
|
2111
|
+
if ("sandbox" in parsed.value) return text9;
|
|
2112
|
+
return renderJson({ ...parsed.value, sandbox: block4 }, text9 === void 0 ? DEFAULT_STYLE : styleOf(text9));
|
|
2113
|
+
};
|
|
2114
|
+
var withoutSandbox = (text9, added) => {
|
|
2115
|
+
const parsed = parseSettings(text9);
|
|
2116
|
+
if (!parsed.ok) return text9;
|
|
2117
|
+
const current2 = parsed.value["sandbox"];
|
|
2118
|
+
if (!isRecord(current2) || JSON.stringify(current2) !== JSON.stringify(added)) return text9;
|
|
2119
|
+
const rest = Object.fromEntries(Object.entries(parsed.value).filter(([key]) => key !== "sandbox"));
|
|
2120
|
+
return Object.keys(rest).length === 0 ? void 0 : renderJson(rest, styleOf(text9));
|
|
2121
|
+
};
|
|
2122
|
+
|
|
1684
2123
|
// src/install/settings-merge.ts
|
|
1685
2124
|
var SETTINGS_SCHEMA_URL = "https://json.schemastore.org/claude-code-settings.json";
|
|
1686
2125
|
var ATTRIBUTION_OFF = { commit: "", pr: "", sessionUrl: false };
|
|
@@ -1809,9 +2248,9 @@ var LOCAL_SETTINGS_FILE = ".claude/settings.local.json";
|
|
|
1809
2248
|
var existingStatusLine = (project, projectSettings, env) => statusLineCommand(projectSettings["statusLine"]) ?? statusLineCommand(statusLineIn(project.read(LOCAL_SETTINGS_FILE))) ?? statusLineCommand(statusLineIn(userSettingsText(project, env)));
|
|
1810
2249
|
|
|
1811
2250
|
// src/install/vendor.ts
|
|
1812
|
-
var isBundle = (
|
|
1813
|
-
var isOwnPackage = (
|
|
1814
|
-
const parsed = parseJsonObject(
|
|
2251
|
+
var isBundle = (text9) => text9.startsWith("#!/usr/bin/env node") && text9.includes("finitemonkey");
|
|
2252
|
+
var isOwnPackage = (text9) => {
|
|
2253
|
+
const parsed = parseJsonObject(text9);
|
|
1815
2254
|
return parsed.ok && parsed.value["name"] === "finitemonkey";
|
|
1816
2255
|
};
|
|
1817
2256
|
var vendoredPackage = (version) => `${JSON.stringify({ name: "finitemonkey", version, private: true, type: "module" }, null, 2)}
|
|
@@ -1842,9 +2281,9 @@ var detect = (project, gitRoot2, input) => ({
|
|
|
1842
2281
|
location: locationOf(project.root, input.env)
|
|
1843
2282
|
});
|
|
1844
2283
|
var readPreviousLock = (project) => {
|
|
1845
|
-
const
|
|
1846
|
-
if (
|
|
1847
|
-
const parsed = parseLock(
|
|
2284
|
+
const text9 = project.read(LOCK_FILE);
|
|
2285
|
+
if (text9 === void 0) return accept(void 0);
|
|
2286
|
+
const parsed = parseLock(text9);
|
|
1848
2287
|
return parsed.ok ? accept(parsed.value) : reject({ kind: "lock", detail: parsed.error });
|
|
1849
2288
|
};
|
|
1850
2289
|
var actionFor = (before, after, existing) => {
|
|
@@ -1893,13 +2332,24 @@ var settingsItem = (project, detection, input, previous) => {
|
|
|
1893
2332
|
});
|
|
1894
2333
|
};
|
|
1895
2334
|
var markdownItems = (project, runner, lang) => [
|
|
1896
|
-
{ path: AGENTS_FILE, render: (
|
|
2335
|
+
{ path: AGENTS_FILE, render: (text9) => withAgentsBlock(text9, runner, lang) },
|
|
1897
2336
|
{ path: CLAUDE_FILE, render: withAgentsImport }
|
|
1898
2337
|
].map(({ path, render: render3 }) => {
|
|
1899
2338
|
const before = project.read(path);
|
|
1900
2339
|
const after = render3(before);
|
|
1901
2340
|
return { path, kind: "markdown", action: actionFor(before, after, "merge"), before, after };
|
|
1902
2341
|
});
|
|
2342
|
+
var sandboxItems = (project, input) => {
|
|
2343
|
+
if (!input.sandbox || !SANDBOX_PLATFORMS.includes(input.platform)) return [];
|
|
2344
|
+
const declared = configList(parseConfigYaml(project.read(CONFIG_FILE2) ?? ""), "sandbox", "hosts");
|
|
2345
|
+
const block4 = sandboxBlock(project.root, declared.length > 0 ? declared : DEFAULT_HOSTS);
|
|
2346
|
+
const before = project.read(SANDBOX_FILE);
|
|
2347
|
+
const after = withSandbox(before, block4);
|
|
2348
|
+
if (after === void 0) return [];
|
|
2349
|
+
return [
|
|
2350
|
+
{ path: SANDBOX_FILE, kind: "sandbox", action: actionFor(before, after, "merge"), before, after, sandbox: block4 }
|
|
2351
|
+
];
|
|
2352
|
+
};
|
|
1903
2353
|
var gitignoreItem = (project) => {
|
|
1904
2354
|
const before = project.read(GITIGNORE_FILE);
|
|
1905
2355
|
const after = withBlock(before);
|
|
@@ -1920,14 +2370,14 @@ var generatedItem = (project, file, previous) => {
|
|
|
1920
2370
|
};
|
|
1921
2371
|
var PLAYWRIGHT_VERIFIER = ".claude/agents/verifier.md";
|
|
1922
2372
|
var playwrightVerifier = (project) => {
|
|
1923
|
-
const wanted = configAt(parseConfigYaml(project.read(
|
|
2373
|
+
const wanted = configAt(parseConfigYaml(project.read(CONFIG_FILE2) ?? ""), "review", "playwright") === "true";
|
|
1924
2374
|
if (!wanted) return [];
|
|
1925
2375
|
const template = agentMarkdown(withPlaywright(VERIFIER), GENERATED_MARKER);
|
|
1926
|
-
return [{ path: PLAYWRIGHT_VERIFIER, template, isOurs: (
|
|
2376
|
+
return [{ path: PLAYWRIGHT_VERIFIER, template, isOurs: (text9) => text9.includes(GENERATED_MARKER) }];
|
|
1927
2377
|
};
|
|
1928
2378
|
var generatedFiles = (project, input, runner, vendor) => {
|
|
1929
2379
|
const own = [
|
|
1930
|
-
{ path:
|
|
2380
|
+
{ path: CONFIG_FILE2, template: configTemplate(input.preset, input.lang), isOurs: hasMarker },
|
|
1931
2381
|
{ path: VERIFY_SKILL, template: verifySkillTemplate(runner, input.lang), isOurs: isOwnSkill },
|
|
1932
2382
|
{ path: POLICY_FILE, template: policyTemplate(input.lang), isOurs: hasMarker },
|
|
1933
2383
|
...playwrightVerifier(project)
|
|
@@ -1944,14 +2394,18 @@ var buildPlan = (project, detection, input, previous, migrations = MIGRATIONS) =
|
|
|
1944
2394
|
const files = generatedFiles(project, input, detection.runner, vendor);
|
|
1945
2395
|
if (!files.ok) return files;
|
|
1946
2396
|
const generated = files.value.map((file) => generatedItem(project, file, entryFor(previous, file.path)));
|
|
2397
|
+
const sandboxSupported = SANDBOX_PLATFORMS.includes(input.platform);
|
|
2398
|
+
const sandbox = input.sandbox ? sandboxSupported ? "added" : "unsupported" : input.offerSandbox ? "offer" : "none";
|
|
1947
2399
|
return accept({
|
|
1948
2400
|
detection,
|
|
2401
|
+
sandbox,
|
|
1949
2402
|
preset: input.preset,
|
|
1950
2403
|
items: applyMigrations(
|
|
1951
2404
|
[
|
|
1952
2405
|
settings.value,
|
|
1953
2406
|
gitignoreItem(project),
|
|
1954
2407
|
...markdownItems(project, detection.runner, input.lang),
|
|
2408
|
+
...sandboxItems(project, input),
|
|
1955
2409
|
...generated,
|
|
1956
2410
|
...gitHookItems(project, detection.runner)
|
|
1957
2411
|
],
|
|
@@ -1968,9 +2422,9 @@ var needsWrite = (plan) => hasChanges(plan) || plan.previous === void 0;
|
|
|
1968
2422
|
// src/install/apply.ts
|
|
1969
2423
|
var stamp = (iso) => iso.replace(/[:.]/g, "-");
|
|
1970
2424
|
var ensureParent = (project, relative11) => project.runtime.mkdir(project.abs(dirname3(relative11)));
|
|
1971
|
-
var writeText = (project, relative11,
|
|
2425
|
+
var writeText = (project, relative11, text9) => {
|
|
1972
2426
|
const parent = ensureParent(project, relative11);
|
|
1973
|
-
const written = parent.ok ? project.runtime.writeAtomic(project.abs(relative11),
|
|
2427
|
+
const written = parent.ok ? project.runtime.writeAtomic(project.abs(relative11), text9) : parent;
|
|
1974
2428
|
return written.ok ? accept(void 0) : reject({ path: relative11, detail: written.error });
|
|
1975
2429
|
};
|
|
1976
2430
|
var restore = (project, items) => {
|
|
@@ -1988,7 +2442,7 @@ var backupItems = (project, dir, items) => {
|
|
|
1988
2442
|
return writeText(project, `${dir}/manifest.json`, `${JSON.stringify(manifest, null, 2)}
|
|
1989
2443
|
`);
|
|
1990
2444
|
};
|
|
1991
|
-
var
|
|
2445
|
+
var entryOf2 = (plan, item2, backupDir) => {
|
|
1992
2446
|
const previous = plan.previous?.entries.find((entry2) => entry2.path === item2.path);
|
|
1993
2447
|
const existed = previous?.existed ?? item2.before !== void 0;
|
|
1994
2448
|
const backup = previous?.backup ?? (existed && backupDir !== void 0 ? `${backupDir}/${item2.path}` : void 0);
|
|
@@ -2000,7 +2454,8 @@ var entryOf = (plan, item2, backupDir) => {
|
|
|
2000
2454
|
hash,
|
|
2001
2455
|
...backup === void 0 ? {} : { backup },
|
|
2002
2456
|
...item2.added === void 0 ? {} : { added: item2.added },
|
|
2003
|
-
...item2.handlers === void 0 ? {} : { handlers: item2.handlers }
|
|
2457
|
+
...item2.handlers === void 0 ? {} : { handlers: item2.handlers },
|
|
2458
|
+
...item2.sandbox === void 0 ? {} : { sandbox: item2.sandbox }
|
|
2004
2459
|
};
|
|
2005
2460
|
};
|
|
2006
2461
|
var nextLock = (plan, version, now, backupDir, createdDirs, only) => ({
|
|
@@ -2011,7 +2466,7 @@ var nextLock = (plan, version, now, backupDir, createdDirs, only) => ({
|
|
|
2011
2466
|
updatedAt: now,
|
|
2012
2467
|
createdDirs: [.../* @__PURE__ */ new Set([...plan.previous?.createdDirs ?? [], ...createdDirs])],
|
|
2013
2468
|
entries: plan.items.filter((item2) => item2.action !== "skip-foreign").flatMap((item2) => {
|
|
2014
|
-
if (only === void 0 || only.includes(item2.path)) return [
|
|
2469
|
+
if (only === void 0 || only.includes(item2.path)) return [entryOf2(plan, item2, backupDir)];
|
|
2015
2470
|
const kept = plan.previous?.entries.find((entry2) => entry2.path === item2.path);
|
|
2016
2471
|
return kept === void 0 ? [] : [kept];
|
|
2017
2472
|
})
|
|
@@ -2110,16 +2565,16 @@ var detectLang = (flag2, env, settings = {}) => {
|
|
|
2110
2565
|
};
|
|
2111
2566
|
|
|
2112
2567
|
// src/install/project.ts
|
|
2113
|
-
import { dirname as dirname4, join as
|
|
2568
|
+
import { dirname as dirname4, join as join7 } from "node:path";
|
|
2114
2569
|
var projectAt = (root, runtime) => {
|
|
2115
|
-
const abs = (relative11) => toAbsolutePath(
|
|
2570
|
+
const abs = (relative11) => toAbsolutePath(join7(root, ...relative11.split("/")));
|
|
2116
2571
|
return {
|
|
2117
2572
|
root,
|
|
2118
2573
|
runtime,
|
|
2119
2574
|
abs,
|
|
2120
2575
|
read: (relative11) => {
|
|
2121
|
-
const
|
|
2122
|
-
return
|
|
2576
|
+
const text9 = runtime.readText(abs(relative11));
|
|
2577
|
+
return text9.ok ? text9.value : void 0;
|
|
2123
2578
|
},
|
|
2124
2579
|
exists: (relative11) => runtime.stat(abs(relative11)) !== void 0,
|
|
2125
2580
|
isDir: (relative11) => runtime.stat(abs(relative11))?.kind === "dir"
|
|
@@ -2128,7 +2583,7 @@ var projectAt = (root, runtime) => {
|
|
|
2128
2583
|
var findGitRoot = (start, runtime) => {
|
|
2129
2584
|
let current2 = start;
|
|
2130
2585
|
for (; ; ) {
|
|
2131
|
-
if (runtime.stat(toAbsolutePath(
|
|
2586
|
+
if (runtime.stat(toAbsolutePath(join7(current2, ".git"))) !== void 0) return toAbsolutePath(current2);
|
|
2132
2587
|
const parent = dirname4(current2);
|
|
2133
2588
|
if (parent === current2) return void 0;
|
|
2134
2589
|
current2 = parent;
|
|
@@ -2192,17 +2647,20 @@ var invalidPreset = (raw) => problem("invalid_arguments", "error.invalid_preset"
|
|
|
2192
2647
|
});
|
|
2193
2648
|
var selfBundle = (ctx) => {
|
|
2194
2649
|
if (!isBundlePath(ctx.runtime.selfPath)) return void 0;
|
|
2195
|
-
const
|
|
2196
|
-
return
|
|
2650
|
+
const text9 = ctx.runtime.readText(ctx.runtime.selfPath);
|
|
2651
|
+
return text9.ok ? text9.value : void 0;
|
|
2197
2652
|
};
|
|
2198
2653
|
var INHERITED = /* @__PURE__ */ new Set(["default", "config", "claude-settings"]);
|
|
2199
|
-
var planInputFor = (ctx, preset, bin, marketplace) => ({
|
|
2654
|
+
var planInputFor = (ctx, preset, bin, marketplace, sandbox = false) => ({
|
|
2200
2655
|
preset,
|
|
2201
2656
|
lang: ctx.lang,
|
|
2202
2657
|
claudeLanguage: INHERITED.has(ctx.langSource) ? void 0 : CLAUDE_LANGUAGE[ctx.lang],
|
|
2203
2658
|
bin,
|
|
2204
2659
|
version: ctx.version,
|
|
2205
2660
|
marketplace,
|
|
2661
|
+
sandbox,
|
|
2662
|
+
offerSandbox: !sandbox && SANDBOX_PLATFORMS.includes(ctx.platform) && (ctx.io.stdin.isTTY === true || ctx.yes),
|
|
2663
|
+
platform: ctx.platform,
|
|
2206
2664
|
bundle: selfBundle(ctx),
|
|
2207
2665
|
env: ctx.env,
|
|
2208
2666
|
t: ctx.t
|
|
@@ -2229,7 +2687,7 @@ var applyFixes = (ctx, write3) => {
|
|
|
2229
2687
|
};
|
|
2230
2688
|
|
|
2231
2689
|
// src/doctor/report-file.ts
|
|
2232
|
-
import { join as
|
|
2690
|
+
import { join as join9 } from "node:path";
|
|
2233
2691
|
|
|
2234
2692
|
// src/evidence/log.ts
|
|
2235
2693
|
var logEvent = (runtime, paths, event, data) => runtime.appendText(paths.log, `${JSON.stringify({ at: runtime.now(), event, ...data })}
|
|
@@ -2297,13 +2755,13 @@ var isPlaceholder = (value) => {
|
|
|
2297
2755
|
var isTestOrLockPath = (path) => path !== void 0 && (LOCKFILES.test(toPosixPath(path)) || TEST_PATH.test(toPosixPath(path)));
|
|
2298
2756
|
|
|
2299
2757
|
// src/secrets/entropy.ts
|
|
2300
|
-
var shannonEntropy = (
|
|
2301
|
-
if (
|
|
2758
|
+
var shannonEntropy = (text9) => {
|
|
2759
|
+
if (text9.length === 0) return 0;
|
|
2302
2760
|
const counts = /* @__PURE__ */ new Map();
|
|
2303
|
-
for (const char of
|
|
2761
|
+
for (const char of text9) counts.set(char, (counts.get(char) ?? 0) + 1);
|
|
2304
2762
|
let entropy = 0;
|
|
2305
2763
|
for (const count4 of counts.values()) {
|
|
2306
|
-
const p = count4 /
|
|
2764
|
+
const p = count4 / text9.length;
|
|
2307
2765
|
entropy -= p * Math.log2(p);
|
|
2308
2766
|
}
|
|
2309
2767
|
return entropy;
|
|
@@ -2311,7 +2769,7 @@ var shannonEntropy = (text8) => {
|
|
|
2311
2769
|
|
|
2312
2770
|
// src/secrets/fingerprint.ts
|
|
2313
2771
|
import { createHash as createHash2 } from "node:crypto";
|
|
2314
|
-
var sha256Hex = (
|
|
2772
|
+
var sha256Hex = (text9) => createHash2("sha256").update(text9).digest("hex");
|
|
2315
2773
|
var fingerprintOf = (secret) => sha256Hex(`finitemonkey:secret:${secret}`).slice(0, 16);
|
|
2316
2774
|
var ALNUM = /[A-Za-z0-9]/g;
|
|
2317
2775
|
var MIN_TAIL_LENGTH = 12;
|
|
@@ -2321,9 +2779,9 @@ var tailOf = (secret) => {
|
|
|
2321
2779
|
};
|
|
2322
2780
|
|
|
2323
2781
|
// src/secrets/lines.ts
|
|
2324
|
-
var lineLocator = (
|
|
2782
|
+
var lineLocator = (text9) => {
|
|
2325
2783
|
const starts = [0];
|
|
2326
|
-
for (let index =
|
|
2784
|
+
for (let index = text9.indexOf("\n"); index !== -1; index = text9.indexOf("\n", index + 1)) starts.push(index + 1);
|
|
2327
2785
|
return (offset) => {
|
|
2328
2786
|
let low = 0;
|
|
2329
2787
|
let high = starts.length - 1;
|
|
@@ -2617,12 +3075,12 @@ var hasRandomRun = (secret, length) => (secret.match(new RegExp(`[A-Za-z0-9]{${S
|
|
|
2617
3075
|
);
|
|
2618
3076
|
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
3077
|
var NAME_CHAR = /[A-Za-z0-9_.-]/;
|
|
2620
|
-
var nameAround = (
|
|
3078
|
+
var nameAround = (text9, at) => {
|
|
2621
3079
|
let start = at;
|
|
2622
|
-
while (start > 0 && NAME_CHAR.test(
|
|
3080
|
+
while (start > 0 && NAME_CHAR.test(text9.charAt(start - 1))) start -= 1;
|
|
2623
3081
|
let end = at;
|
|
2624
|
-
while (end <
|
|
2625
|
-
return
|
|
3082
|
+
while (end < text9.length && NAME_CHAR.test(text9.charAt(end))) end += 1;
|
|
3083
|
+
return text9.slice(start, end).toLowerCase();
|
|
2626
3084
|
};
|
|
2627
3085
|
var ignoredName = (rule, match) => rule.ignoredNames.length > 0 && rule.ignoredNames.includes(nameAround(match.input, match.index));
|
|
2628
3086
|
var spanOf = (rule, match) => {
|
|
@@ -2632,9 +3090,9 @@ var spanOf = (rule, match) => {
|
|
|
2632
3090
|
const secret = match.input.slice(start, end);
|
|
2633
3091
|
return accepted(rule, secret) ? { rule, start, end, secret } : void 0;
|
|
2634
3092
|
};
|
|
2635
|
-
var ruleSpans = (rule,
|
|
3093
|
+
var ruleSpans = (rule, text9) => {
|
|
2636
3094
|
const spans = [];
|
|
2637
|
-
for (const match of
|
|
3095
|
+
for (const match of text9.matchAll(rule.pattern)) {
|
|
2638
3096
|
const span = spanOf(rule, match);
|
|
2639
3097
|
if (span !== void 0) spans.push(span);
|
|
2640
3098
|
}
|
|
@@ -2660,31 +3118,31 @@ var toFinding = (span, locate2) => {
|
|
|
2660
3118
|
tail: tailOf(span.secret)
|
|
2661
3119
|
};
|
|
2662
3120
|
};
|
|
2663
|
-
var scanText = (
|
|
2664
|
-
if (
|
|
2665
|
-
const rules = applicable(options.rules ?? RULES,
|
|
3121
|
+
var scanText = (text9, options = {}) => {
|
|
3122
|
+
if (text9.length === 0) return [];
|
|
3123
|
+
const rules = applicable(options.rules ?? RULES, text9.toLowerCase(), options.path);
|
|
2666
3124
|
if (rules.length === 0) return [];
|
|
2667
3125
|
const taken = [];
|
|
2668
3126
|
for (const rule of rules) {
|
|
2669
|
-
for (const span of ruleSpans(rule,
|
|
3127
|
+
for (const span of ruleSpans(rule, text9)) if (!overlaps(taken, span)) taken.push(span);
|
|
2670
3128
|
}
|
|
2671
3129
|
if (taken.length === 0) return [];
|
|
2672
|
-
const locate2 = lineLocator(
|
|
3130
|
+
const locate2 = lineLocator(text9);
|
|
2673
3131
|
return taken.sort((a, b) => a.start - b.start).map((span) => toFinding(span, locate2));
|
|
2674
3132
|
};
|
|
2675
3133
|
|
|
2676
3134
|
// src/mask/mask.ts
|
|
2677
3135
|
var maskToken = (finding4) => `«secret:${finding4.ruleId}:${finding4.tail}»`;
|
|
2678
|
-
var maskText = (
|
|
2679
|
-
const findings = scanText(
|
|
2680
|
-
if (findings.length === 0) return
|
|
3136
|
+
var maskText = (text9) => {
|
|
3137
|
+
const findings = scanText(text9, { rules: CREDENTIAL_RULES });
|
|
3138
|
+
if (findings.length === 0) return text9;
|
|
2681
3139
|
let out = "";
|
|
2682
3140
|
let cursor = 0;
|
|
2683
3141
|
for (const finding4 of findings) {
|
|
2684
|
-
out +=
|
|
3142
|
+
out += text9.slice(cursor, finding4.start) + maskToken(finding4);
|
|
2685
3143
|
cursor = finding4.end;
|
|
2686
3144
|
}
|
|
2687
|
-
return out +
|
|
3145
|
+
return out + text9.slice(cursor);
|
|
2688
3146
|
};
|
|
2689
3147
|
var maskDeep = (value) => {
|
|
2690
3148
|
if (typeof value === "string") return maskText(value);
|
|
@@ -2696,11 +3154,11 @@ var maskDeep = (value) => {
|
|
|
2696
3154
|
};
|
|
2697
3155
|
|
|
2698
3156
|
// src/task/paths.ts
|
|
2699
|
-
import { join as
|
|
3157
|
+
import { join as join8 } from "node:path";
|
|
2700
3158
|
var FM_DIR = ".finitemonkey";
|
|
2701
3159
|
var fmPaths = (root) => {
|
|
2702
|
-
const dir = toAbsolutePath(
|
|
2703
|
-
const at = (...parts) => toAbsolutePath(
|
|
3160
|
+
const dir = toAbsolutePath(join8(root, FM_DIR));
|
|
3161
|
+
const at = (...parts) => toAbsolutePath(join8(dir, ...parts));
|
|
2704
3162
|
return {
|
|
2705
3163
|
root,
|
|
2706
3164
|
dir,
|
|
@@ -2902,14 +3360,14 @@ var toolVersion = (env, command) => {
|
|
|
2902
3360
|
return result.ok ? result.stdout.trim().split("\n")[0] ?? "" : "not found";
|
|
2903
3361
|
};
|
|
2904
3362
|
var lastHookFailures = (env) => {
|
|
2905
|
-
const
|
|
2906
|
-
if (!
|
|
2907
|
-
return
|
|
3363
|
+
const text9 = env.runtime.readText(fmPaths(env.root).log);
|
|
3364
|
+
if (!text9.ok) return [];
|
|
3365
|
+
return text9.value.split("\n").filter((line2) => {
|
|
2908
3366
|
const parsed = line2.trim() === "" ? void 0 : parseJsonObject(line2);
|
|
2909
3367
|
return parsed?.ok === true && isRecord(parsed.value) && parsed.value["event"] === HOOK_FAILURE_EVENT;
|
|
2910
3368
|
}).slice(-MAX_FAILURES);
|
|
2911
3369
|
};
|
|
2912
|
-
var withoutHome = (
|
|
3370
|
+
var withoutHome = (text9, home) => home.length > 1 ? text9.split(home).join("~") : text9;
|
|
2913
3371
|
var reportMarkdown = (env, ctx, envelope) => {
|
|
2914
3372
|
const failures = lastHookFailures(env);
|
|
2915
3373
|
const lines3 = [
|
|
@@ -2936,18 +3394,18 @@ var reportMarkdown = (env, ctx, envelope) => {
|
|
|
2936
3394
|
];
|
|
2937
3395
|
return maskText(withoutHome(lines3.join("\n"), env.runtime.homeDir()));
|
|
2938
3396
|
};
|
|
2939
|
-
var reportPath = (env) => toAbsolutePath(
|
|
3397
|
+
var reportPath = (env) => toAbsolutePath(join9(env.root, ...REPORTS_DIR.split("/"), `doctor-${stamp(env.runtime.now())}.md`));
|
|
2940
3398
|
|
|
2941
3399
|
// src/install/plugin-yield.ts
|
|
2942
|
-
import { join as
|
|
3400
|
+
import { join as join10 } from "node:path";
|
|
2943
3401
|
var PLUGIN_VIA = "plugin";
|
|
2944
3402
|
var YIELD_STDOUT = `${JSON.stringify({ continue: true })}
|
|
2945
3403
|
`;
|
|
2946
3404
|
var projectOwnsHooks = (ctx) => {
|
|
2947
3405
|
const root = ctx.env["CLAUDE_PROJECT_DIR"] ?? ctx.cwd;
|
|
2948
|
-
const
|
|
2949
|
-
if (!
|
|
2950
|
-
const parsed = parseJsonObject(
|
|
3406
|
+
const text9 = ctx.runtime.readText(toAbsolutePath(join10(root, ...SETTINGS_FILE.split("/"))));
|
|
3407
|
+
if (!text9.ok) return false;
|
|
3408
|
+
const parsed = parseJsonObject(text9.value);
|
|
2951
3409
|
return parsed.ok && ownHandlersIn(parsed.value) > 0;
|
|
2952
3410
|
};
|
|
2953
3411
|
|
|
@@ -2959,7 +3417,7 @@ var STABLE_PLUGIN_ID = `${PLUGIN_NAME}@${MARKETPLACE_NAME}`;
|
|
|
2959
3417
|
var NEXT_PLUGIN_ID = `${PLUGIN_NAME}@${NEXT_MARKETPLACE_NAME}`;
|
|
2960
3418
|
|
|
2961
3419
|
// src/doctor/settings.ts
|
|
2962
|
-
import { join as
|
|
3420
|
+
import { join as join11 } from "node:path";
|
|
2963
3421
|
var MANAGED = {
|
|
2964
3422
|
darwin: "/Library/Application Support/ClaudeCode/managed-settings.json",
|
|
2965
3423
|
linux: "/etc/claude-code/managed-settings.json",
|
|
@@ -2968,7 +3426,7 @@ var MANAGED = {
|
|
|
2968
3426
|
var userSettingsPath = ({ env, runtime }) => {
|
|
2969
3427
|
const configDir = env["CLAUDE_CONFIG_DIR"];
|
|
2970
3428
|
return toAbsolutePath(
|
|
2971
|
-
configDir === void 0 ?
|
|
3429
|
+
configDir === void 0 ? join11(runtime.homeDir(), ".claude", "settings.json") : join11(configDir, "settings.json")
|
|
2972
3430
|
);
|
|
2973
3431
|
};
|
|
2974
3432
|
var settingsPaths = (env) => {
|
|
@@ -2976,13 +3434,13 @@ var settingsPaths = (env) => {
|
|
|
2976
3434
|
return [
|
|
2977
3435
|
...managed === void 0 ? [] : [{ scope: "managed", path: toAbsolutePath(managed) }],
|
|
2978
3436
|
{ scope: "user", path: userSettingsPath(env) },
|
|
2979
|
-
{ scope: "project", path: toAbsolutePath(
|
|
2980
|
-
{ scope: "local", path: toAbsolutePath(
|
|
3437
|
+
{ scope: "project", path: toAbsolutePath(join11(env.root, ".claude", "settings.json")) },
|
|
3438
|
+
{ scope: "local", path: toAbsolutePath(join11(env.root, ".claude", "settings.local.json")) }
|
|
2981
3439
|
];
|
|
2982
3440
|
};
|
|
2983
3441
|
var readSettings = (env) => settingsPaths(env).flatMap(({ scope: scope2, path }) => {
|
|
2984
|
-
const
|
|
2985
|
-
const parsed =
|
|
3442
|
+
const text9 = env.runtime.readText(path);
|
|
3443
|
+
const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
|
|
2986
3444
|
return parsed?.ok === true ? [{ scope: scope2, path, settings: parsed.value }] : [];
|
|
2987
3445
|
});
|
|
2988
3446
|
var scopesWith = (all, test) => all.filter(({ settings }) => test(settings)).map(({ scope: scope2 }) => scope2);
|
|
@@ -3121,7 +3579,7 @@ var checkClaudeSettings = (env) => {
|
|
|
3121
3579
|
};
|
|
3122
3580
|
|
|
3123
3581
|
// src/doctor/environment.ts
|
|
3124
|
-
import { join as
|
|
3582
|
+
import { join as join12 } from "node:path";
|
|
3125
3583
|
var AUTH_VARIABLES = [
|
|
3126
3584
|
"ANTHROPIC_API_KEY",
|
|
3127
3585
|
"ANTHROPIC_AUTH_TOKEN",
|
|
@@ -3159,7 +3617,7 @@ var pathDirs = ({ env, platform }) => {
|
|
|
3159
3617
|
var copiesOnPath = (env) => {
|
|
3160
3618
|
const names = EXECUTABLES[env.platform] ?? ["finitemonkey"];
|
|
3161
3619
|
const found = pathDirs(env).flatMap(
|
|
3162
|
-
(dir) => names.map((name) => toAbsolutePath(
|
|
3620
|
+
(dir) => names.map((name) => toAbsolutePath(join12(dir, name))).filter((path) => env.runtime.stat(path)?.kind === "file").map((path) => env.runtime.realpath(path))
|
|
3163
3621
|
);
|
|
3164
3622
|
const byDir = new Map(found.map((path) => [path.replace(/\.(cmd|exe|ps1)$/i, ""), path]));
|
|
3165
3623
|
return [...byDir.values()];
|
|
@@ -3193,7 +3651,7 @@ var checkAuthVariables = ({ env, platform }) => {
|
|
|
3193
3651
|
};
|
|
3194
3652
|
|
|
3195
3653
|
// src/doctor/hooks.ts
|
|
3196
|
-
import { join as
|
|
3654
|
+
import { join as join36 } from "node:path";
|
|
3197
3655
|
|
|
3198
3656
|
// src/hooks/events.ts
|
|
3199
3657
|
var HOOK_EVENTS = [
|
|
@@ -3240,14 +3698,14 @@ var deadlineFor = (event, raw) => {
|
|
|
3240
3698
|
};
|
|
3241
3699
|
|
|
3242
3700
|
// src/gate/hooks/common.ts
|
|
3243
|
-
import { dirname as dirname5, join as
|
|
3701
|
+
import { dirname as dirname5, join as join13 } from "node:path";
|
|
3244
3702
|
var DEFAULT_GATE_BUDGET_MS = 3e5;
|
|
3245
3703
|
var sessionCwd = (input) => input.payload.cwd === void 0 ? input.cwd : toAbsolutePath(input.payload.cwd, input.cwd);
|
|
3246
3704
|
var MAX_DEPTH = 64;
|
|
3247
3705
|
var findRepoRoot = (runtime, start) => {
|
|
3248
3706
|
let current2 = start;
|
|
3249
3707
|
for (let depth = 0; depth < MAX_DEPTH; depth += 1) {
|
|
3250
|
-
if (runtime.stat(toAbsolutePath(
|
|
3708
|
+
if (runtime.stat(toAbsolutePath(join13(current2, ".git"))) !== void 0) return current2;
|
|
3251
3709
|
const parent = toAbsolutePath(dirname5(current2));
|
|
3252
3710
|
if (parent === current2) return void 0;
|
|
3253
3711
|
current2 = parent;
|
|
@@ -3267,7 +3725,7 @@ var contextOutput = (event, additionalContext, systemMessage) => ({
|
|
|
3267
3725
|
hookSpecificOutput: { hookEventName: event, additionalContext },
|
|
3268
3726
|
...systemMessage === void 0 ? {} : { systemMessage }
|
|
3269
3727
|
});
|
|
3270
|
-
var human = (
|
|
3728
|
+
var human = (text9) => `finitemonkey: ${text9}`;
|
|
3271
3729
|
|
|
3272
3730
|
// src/hooks/fields.ts
|
|
3273
3731
|
var textField = (record3, ...names) => {
|
|
@@ -3298,7 +3756,7 @@ var editsOf = (input) => {
|
|
|
3298
3756
|
});
|
|
3299
3757
|
};
|
|
3300
3758
|
var applyEdits = (before, edits) => edits.reduce(
|
|
3301
|
-
(
|
|
3759
|
+
(text9, edit) => edit.all ? text9.split(edit.oldText).join(edit.newText) : text9.replace(edit.oldText, () => edit.newText),
|
|
3302
3760
|
before
|
|
3303
3761
|
);
|
|
3304
3762
|
|
|
@@ -3318,7 +3776,7 @@ var familyOf = (base) => {
|
|
|
3318
3776
|
if (found === void 0) throw new Error(`no reason family for ${base}`);
|
|
3319
3777
|
return found;
|
|
3320
3778
|
};
|
|
3321
|
-
var capReason = (
|
|
3779
|
+
var capReason = (text9) => text9.length <= REASON_LIMIT ? text9 : `${text9.slice(0, REASON_LIMIT - 1)}…`;
|
|
3322
3780
|
var modelReason = (t, base, params2 = {}) => {
|
|
3323
3781
|
const claim = `${base}.claim`;
|
|
3324
3782
|
const requires = `${base}.requires`;
|
|
@@ -3345,9 +3803,9 @@ var textRecord = (value) => isRecord(value) ? Object.fromEntries(
|
|
|
3345
3803
|
Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
|
|
3346
3804
|
) : {};
|
|
3347
3805
|
var readJsonFile = (runtime, path) => {
|
|
3348
|
-
const
|
|
3349
|
-
if (!
|
|
3350
|
-
const parsed = parseJsonObject(
|
|
3806
|
+
const text9 = runtime.readText(path);
|
|
3807
|
+
if (!text9.ok) return void 0;
|
|
3808
|
+
const parsed = parseJsonObject(text9.value);
|
|
3351
3809
|
return parsed.ok ? parsed.value : void 0;
|
|
3352
3810
|
};
|
|
3353
3811
|
var writeJsonFile = (runtime, path, value) => runtime.writeText(path, `${JSON.stringify({ schemaVersion: 1, ...value }, null, 2)}
|
|
@@ -3397,28 +3855,28 @@ var closeTask = (state, id) => assemble({
|
|
|
3397
3855
|
|
|
3398
3856
|
// src/guards/release/grants.ts
|
|
3399
3857
|
var GRANT_MINUTES = 30;
|
|
3400
|
-
var
|
|
3858
|
+
var text3 = (record3, key) => {
|
|
3401
3859
|
const value = record3[key];
|
|
3402
3860
|
return typeof value === "string" ? value : void 0;
|
|
3403
3861
|
};
|
|
3404
3862
|
var toGrant = (value) => {
|
|
3405
3863
|
if (!isRecord(value)) return void 0;
|
|
3406
|
-
const guard =
|
|
3407
|
-
const reason =
|
|
3408
|
-
const grantedAt =
|
|
3409
|
-
const expiresAt =
|
|
3410
|
-
const sessionId =
|
|
3864
|
+
const guard = text3(value, "guard");
|
|
3865
|
+
const reason = text3(value, "reason");
|
|
3866
|
+
const grantedAt = text3(value, "grantedAt");
|
|
3867
|
+
const expiresAt = text3(value, "expiresAt");
|
|
3868
|
+
const sessionId = text3(value, "sessionId");
|
|
3411
3869
|
if (guard === void 0 || reason === void 0 || grantedAt === void 0 || expiresAt === void 0)
|
|
3412
3870
|
return void 0;
|
|
3413
3871
|
return { guard, reason, grantedAt, expiresAt, ...sessionId === void 0 ? {} : { sessionId } };
|
|
3414
3872
|
};
|
|
3415
3873
|
var toApproval = (value) => {
|
|
3416
3874
|
if (!isRecord(value)) return void 0;
|
|
3417
|
-
const task =
|
|
3418
|
-
const check2 =
|
|
3419
|
-
const at =
|
|
3875
|
+
const task = text3(value, "task");
|
|
3876
|
+
const check2 = text3(value, "check");
|
|
3877
|
+
const at = text3(value, "at");
|
|
3420
3878
|
if (task === void 0 || check2 === void 0 || at === void 0) return void 0;
|
|
3421
|
-
return { task, check: check2, at, reason:
|
|
3879
|
+
return { task, check: check2, at, reason: text3(value, "reason") ?? "" };
|
|
3422
3880
|
};
|
|
3423
3881
|
var list = (raw, map) => Array.isArray(raw) ? raw.flatMap((item2) => {
|
|
3424
3882
|
const value = map(item2);
|
|
@@ -3460,7 +3918,7 @@ var SHELL_TOOLS = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
|
|
|
3460
3918
|
import { posix } from "node:path";
|
|
3461
3919
|
|
|
3462
3920
|
// src/shell/scan.ts
|
|
3463
|
-
var cursorOf = (
|
|
3921
|
+
var cursorOf = (text9) => ({ text: text9, pos: 0, incomplete: false, subs: [] });
|
|
3464
3922
|
var peek = (cursor, offset = 0) => cursor.text.charAt(cursor.pos + offset);
|
|
3465
3923
|
var atEnd = (cursor) => cursor.pos >= cursor.text.length;
|
|
3466
3924
|
var skipQuoted = (cursor, quote) => {
|
|
@@ -3675,8 +4133,8 @@ var nextToken = (cursor, tokens, pending) => {
|
|
|
3675
4133
|
tokens.push({ kind: "op", text: op });
|
|
3676
4134
|
if (op === "\n") flushHeredocs(cursor, tokens, pending);
|
|
3677
4135
|
};
|
|
3678
|
-
var lexBash = (
|
|
3679
|
-
const cursor = cursorOf(
|
|
4136
|
+
var lexBash = (text9) => {
|
|
4137
|
+
const cursor = cursorOf(text9.replace(/\r\n/g, "\n"));
|
|
3680
4138
|
const tokens = [];
|
|
3681
4139
|
const pending = [];
|
|
3682
4140
|
skipBlank(cursor);
|
|
@@ -3824,9 +4282,9 @@ var next = (cursor, tokens, cmd) => {
|
|
|
3824
4282
|
}
|
|
3825
4283
|
const op = OPERATORS2.find((candidate) => cursor.text.startsWith(candidate, cursor.pos));
|
|
3826
4284
|
if (op !== void 0 || cmd && peek(cursor) === "&") {
|
|
3827
|
-
const
|
|
3828
|
-
cursor.pos +=
|
|
3829
|
-
tokens.push({ kind: "op", text:
|
|
4285
|
+
const text9 = op ?? "&";
|
|
4286
|
+
cursor.pos += text9.length;
|
|
4287
|
+
tokens.push({ kind: "op", text: text9 });
|
|
3830
4288
|
return;
|
|
3831
4289
|
}
|
|
3832
4290
|
if ((peek(cursor) === "&" || peek(cursor) === ".") && peek(cursor, 1) === " " && commandStart(tokens)) {
|
|
@@ -3835,8 +4293,8 @@ var next = (cursor, tokens, cmd) => {
|
|
|
3835
4293
|
}
|
|
3836
4294
|
tokens.push(word(cursor, cmd));
|
|
3837
4295
|
};
|
|
3838
|
-
var lexPowerShell = (
|
|
3839
|
-
const cursor = cursorOf(
|
|
4296
|
+
var lexPowerShell = (text9, cmd = false) => {
|
|
4297
|
+
const cursor = cursorOf(text9.replace(/\r\n/g, "\n"));
|
|
3840
4298
|
const tokens = [];
|
|
3841
4299
|
const skip = () => {
|
|
3842
4300
|
while (peek(cursor) === " " || peek(cursor) === " ") cursor.pos += 1;
|
|
@@ -4039,7 +4497,7 @@ var MAX_DEPTH2 = 8;
|
|
|
4039
4497
|
var ASSIGNMENT2 = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/s;
|
|
4040
4498
|
var PS_ENV = /^\$env:([A-Za-z_][A-Za-z0-9_]*)=?(.*)$/is;
|
|
4041
4499
|
var CHANGES_DIR = /* @__PURE__ */ new Set(["cd", "pushd", "set-location", "push-location"]);
|
|
4042
|
-
var lex = (
|
|
4500
|
+
var lex = (text9, language) => language === "bash" ? lexBash(text9) : lexPowerShell(text9, language === "cmd");
|
|
4043
4501
|
var isWindowsAbsolute = (path) => /^[A-Za-z]:\//.test(path);
|
|
4044
4502
|
var joinDir = (current2, target2) => {
|
|
4045
4503
|
const next2 = toPosixPath(target2);
|
|
@@ -4110,21 +4568,21 @@ var visit = (raw, language, context, depth, collector) => {
|
|
|
4110
4568
|
emit(result.stage, raw, language, context, collector);
|
|
4111
4569
|
for (const extra of result.extra) emit(extra, raw, language, context, collector);
|
|
4112
4570
|
};
|
|
4113
|
-
var collect = (
|
|
4571
|
+
var collect = (text9, language, context, depth, collector) => {
|
|
4114
4572
|
if (depth > MAX_DEPTH2) {
|
|
4115
4573
|
collector.incomplete = true;
|
|
4116
4574
|
return;
|
|
4117
4575
|
}
|
|
4118
|
-
const lexed = lex(
|
|
4576
|
+
const lexed = lex(text9, language);
|
|
4119
4577
|
if (lexed.incomplete) collector.incomplete = true;
|
|
4120
4578
|
for (const substitution of lexed.substitutions) {
|
|
4121
4579
|
collect(substitution, language, { ...context }, depth + 1, collector);
|
|
4122
4580
|
}
|
|
4123
4581
|
for (const raw of rawCommands(lexed.tokens)) visit(raw, language, context, depth, collector);
|
|
4124
4582
|
};
|
|
4125
|
-
var normalizeCommand = (
|
|
4583
|
+
var normalizeCommand = (text9, dialect = "bash") => {
|
|
4126
4584
|
const collector = { out: [], incomplete: false };
|
|
4127
|
-
collect(
|
|
4585
|
+
collect(text9, dialect, { env: {}, dir: void 0, via: [] }, 0, collector);
|
|
4128
4586
|
return { commands: collector.out, incomplete: collector.incomplete };
|
|
4129
4587
|
};
|
|
4130
4588
|
var dialectOf = (toolName3) => toolName3 === "PowerShell" ? "powershell" : "bash";
|
|
@@ -4136,7 +4594,7 @@ var segmentsOf = (command) => normalizeCommand(command).commands.map((item2) =>
|
|
|
4136
4594
|
|
|
4137
4595
|
// src/guards/shell.ts
|
|
4138
4596
|
var HEREDOC = /^<<[-<]?$/;
|
|
4139
|
-
var parseShell = (
|
|
4597
|
+
var parseShell = (text9) => normalizeCommand(text9).commands.map((command) => ({
|
|
4140
4598
|
env: command.env,
|
|
4141
4599
|
argv: command.program === "" ? [] : [command.program, ...command.args],
|
|
4142
4600
|
redirects: command.redirects.filter((redirect) => !HEREDOC.test(redirect.op)).map((redirect) => redirect.target)
|
|
@@ -4298,9 +4756,9 @@ var writtenPaths = (command) => {
|
|
|
4298
4756
|
if (name === "git" && GIT_WRITERS.has(operands2[0] ?? "")) return [...command.redirects, ...operands2.slice(1)];
|
|
4299
4757
|
return command.redirects;
|
|
4300
4758
|
};
|
|
4301
|
-
var mentioned = (
|
|
4302
|
-
if (/\.finitemonkey[\\/]allow/.test(
|
|
4303
|
-
if (/\.finitemonkey|\.gitleaksignore/.test(
|
|
4759
|
+
var mentioned = (text9) => {
|
|
4760
|
+
if (/\.finitemonkey[\\/]allow/.test(text9)) return "allow_store";
|
|
4761
|
+
if (/\.finitemonkey|\.gitleaksignore/.test(text9)) return "policy";
|
|
4304
4762
|
return void 0;
|
|
4305
4763
|
};
|
|
4306
4764
|
var bashSelfTargets = (root, commandLine) => parseShell(commandLine).flatMap(
|
|
@@ -4340,7 +4798,7 @@ var selfGuard = (input) => {
|
|
|
4340
4798
|
};
|
|
4341
4799
|
|
|
4342
4800
|
// src/guards/tests/hook.ts
|
|
4343
|
-
import { join as
|
|
4801
|
+
import { join as join14, relative as relative3 } from "node:path";
|
|
4344
4802
|
|
|
4345
4803
|
// src/guards/tests/patterns.ts
|
|
4346
4804
|
var JS_TEST = /(^|\/)(__tests__|__test__)\/|\.(test|spec)\.[cm]?[jt]sx?$|(^|\/)tests?\/.*\.[cm]?[jt]sx?$/;
|
|
@@ -4504,8 +4962,8 @@ var compareConfig = (path, before, after, accepted2) => {
|
|
|
4504
4962
|
// src/guards/tests/hook.ts
|
|
4505
4963
|
var localPath = (root, path) => toPosixPath(relative3(root, toAbsolutePath(path, root)));
|
|
4506
4964
|
var read = (input, path) => {
|
|
4507
|
-
const
|
|
4508
|
-
return
|
|
4965
|
+
const text9 = input.runtime.readText(path);
|
|
4966
|
+
return text9.ok ? text9.value : void 0;
|
|
4509
4967
|
};
|
|
4510
4968
|
var proposed = (tool, args2, before) => {
|
|
4511
4969
|
if (tool === "Write") return textField(args2, "content") ?? "";
|
|
@@ -4530,7 +4988,7 @@ var shellViolations = (input, root) => {
|
|
|
4530
4988
|
return parseShell(command).flatMap(
|
|
4531
4989
|
(simple) => writtenPaths(simple).flatMap((path) => {
|
|
4532
4990
|
const local = localPath(root, path);
|
|
4533
|
-
const exists = input.runtime.stat(toAbsolutePath(
|
|
4991
|
+
const exists = input.runtime.stat(toAbsolutePath(join14(root, local))) !== void 0;
|
|
4534
4992
|
if (!exists) return [];
|
|
4535
4993
|
if (isProtectedConfig(local)) return [{ kind: "config_changed", path: local }];
|
|
4536
4994
|
return isTestFile(local) ? [{ kind: "test_removed", path: local }] : [];
|
|
@@ -4610,8 +5068,8 @@ var parseMarkers = (prompt) => {
|
|
|
4610
5068
|
// src/policy/pause.ts
|
|
4611
5069
|
var PAUSE_FILE = "pause.json";
|
|
4612
5070
|
var readPause = (runtime, paths) => {
|
|
4613
|
-
const
|
|
4614
|
-
const parsed =
|
|
5071
|
+
const text9 = runtime.readText(paths.cache(PAUSE_FILE));
|
|
5072
|
+
const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
|
|
4615
5073
|
if (parsed?.ok !== true || !isRecord(parsed.value)) return void 0;
|
|
4616
5074
|
const { sessionId, since, by } = parsed.value;
|
|
4617
5075
|
if (typeof sessionId !== "string" || typeof since !== "string") return void 0;
|
|
@@ -4658,7 +5116,7 @@ var unlessPaused = (handler) => (input) => {
|
|
|
4658
5116
|
};
|
|
4659
5117
|
|
|
4660
5118
|
// src/gate/hooks/config.ts
|
|
4661
|
-
import { join as
|
|
5119
|
+
import { join as join15 } from "node:path";
|
|
4662
5120
|
var CONFIG_FILE3 = "config-hooks.json";
|
|
4663
5121
|
var GUARDED_SOURCES = /* @__PURE__ */ new Set(["user_settings", "project_settings", "local_settings"]);
|
|
4664
5122
|
var countIn = (value) => {
|
|
@@ -4666,20 +5124,20 @@ var countIn = (value) => {
|
|
|
4666
5124
|
if (Array.isArray(value)) return value.reduce((sum, item2) => sum + countIn(item2), 0);
|
|
4667
5125
|
return isRecord(value) ? Object.values(value).reduce((sum, item2) => sum + countIn(item2), 0) : 0;
|
|
4668
5126
|
};
|
|
4669
|
-
var censusOf = (
|
|
4670
|
-
if (
|
|
4671
|
-
const parsed = parseJsonObject(
|
|
5127
|
+
var censusOf = (text9) => {
|
|
5128
|
+
if (text9 === void 0) return { hooks: 0, disabled: false };
|
|
5129
|
+
const parsed = parseJsonObject(text9);
|
|
4672
5130
|
if (!parsed.ok) return void 0;
|
|
4673
5131
|
return { hooks: countIn(parsed.value["hooks"]), disabled: parsed.value["disableAllHooks"] === true };
|
|
4674
5132
|
};
|
|
4675
5133
|
var settingsFiles = (paths, env) => [
|
|
4676
|
-
toAbsolutePath(
|
|
4677
|
-
toAbsolutePath(
|
|
4678
|
-
...env["HOME"] === void 0 ? [] : [toAbsolutePath(
|
|
5134
|
+
toAbsolutePath(join15(paths.root, ".claude", "settings.json")),
|
|
5135
|
+
toAbsolutePath(join15(paths.root, ".claude", "settings.local.json")),
|
|
5136
|
+
...env["HOME"] === void 0 ? [] : [toAbsolutePath(join15(env["HOME"], ".claude", "settings.json"))]
|
|
4679
5137
|
];
|
|
4680
5138
|
var read2 = (runtime, path) => {
|
|
4681
|
-
const
|
|
4682
|
-
return
|
|
5139
|
+
const text9 = runtime.readText(path);
|
|
5140
|
+
return text9.ok ? text9.value : void 0;
|
|
4683
5141
|
};
|
|
4684
5142
|
var known = (runtime, paths) => {
|
|
4685
5143
|
const raw = readJsonFile(runtime, paths.cache(CONFIG_FILE3))?.["hooks"];
|
|
@@ -4734,9 +5192,9 @@ var LOCKFILES2 = [
|
|
|
4734
5192
|
];
|
|
4735
5193
|
var packageManager = (files) => LOCKFILES2.find(([file]) => files.exists(file))?.[1] ?? "npm";
|
|
4736
5194
|
var scriptChecks = (files) => {
|
|
4737
|
-
const
|
|
4738
|
-
if (
|
|
4739
|
-
const parsed = parseJsonObject(
|
|
5195
|
+
const text9 = files.read("package.json");
|
|
5196
|
+
if (text9 === void 0) return [];
|
|
5197
|
+
const parsed = parseJsonObject(text9);
|
|
4740
5198
|
const scripts = parsed.ok ? parsed.value["scripts"] : void 0;
|
|
4741
5199
|
if (!isRecord(scripts)) return [];
|
|
4742
5200
|
const pm = packageManager(files);
|
|
@@ -4788,7 +5246,7 @@ var implicitContract = (files) => {
|
|
|
4788
5246
|
};
|
|
4789
5247
|
|
|
4790
5248
|
// src/gate/prepare.ts
|
|
4791
|
-
import { join as
|
|
5249
|
+
import { join as join17 } from "node:path";
|
|
4792
5250
|
|
|
4793
5251
|
// src/core/yaml/scalar.ts
|
|
4794
5252
|
var ESCAPES = {
|
|
@@ -4826,16 +5284,16 @@ var unescapeDouble = (body2) => {
|
|
|
4826
5284
|
}
|
|
4827
5285
|
return accept(out);
|
|
4828
5286
|
};
|
|
4829
|
-
var quotedEnd = (
|
|
4830
|
-
const quote =
|
|
4831
|
-
for (let index = start + 1; index <
|
|
4832
|
-
const char =
|
|
5287
|
+
var quotedEnd = (text9, start) => {
|
|
5288
|
+
const quote = text9[start];
|
|
5289
|
+
for (let index = start + 1; index < text9.length; index += 1) {
|
|
5290
|
+
const char = text9[index];
|
|
4833
5291
|
if (quote === '"' && char === "\\") {
|
|
4834
5292
|
index += 1;
|
|
4835
5293
|
continue;
|
|
4836
5294
|
}
|
|
4837
5295
|
if (char !== quote) continue;
|
|
4838
|
-
if (quote === "'" &&
|
|
5296
|
+
if (quote === "'" && text9[index + 1] === "'") {
|
|
4839
5297
|
index += 1;
|
|
4840
5298
|
continue;
|
|
4841
5299
|
}
|
|
@@ -4843,20 +5301,20 @@ var quotedEnd = (text8, start) => {
|
|
|
4843
5301
|
}
|
|
4844
5302
|
return -1;
|
|
4845
5303
|
};
|
|
4846
|
-
var parseQuoted = (
|
|
4847
|
-
const end = quotedEnd(
|
|
4848
|
-
if (end !==
|
|
4849
|
-
const body2 =
|
|
4850
|
-
return
|
|
5304
|
+
var parseQuoted = (text9) => {
|
|
5305
|
+
const end = quotedEnd(text9, 0);
|
|
5306
|
+
if (end !== text9.length - 1) return reject(`unterminated or trailing text after quoted string`);
|
|
5307
|
+
const body2 = text9.slice(1, -1);
|
|
5308
|
+
return text9.startsWith('"') ? unescapeDouble(body2) : accept(body2.replace(/''/g, "'"));
|
|
4851
5309
|
};
|
|
4852
5310
|
var NUMBER = /^-?(0|[1-9]\d*)(\.\d+)?([eE][-+]?\d+)?$/;
|
|
4853
5311
|
var UNSUPPORTED = /^[&*!|>%@`]/;
|
|
4854
|
-
var parsePlain = (
|
|
4855
|
-
if (UNSUPPORTED.test(
|
|
4856
|
-
if (
|
|
4857
|
-
if (
|
|
4858
|
-
if (NUMBER.test(
|
|
4859
|
-
return accept(
|
|
5312
|
+
var parsePlain = (text9) => {
|
|
5313
|
+
if (UNSUPPORTED.test(text9)) return reject(`unsupported YAML syntax "${text9.slice(0, 1)}"`);
|
|
5314
|
+
if (text9 === "true" || text9 === "false") return accept(text9 === "true");
|
|
5315
|
+
if (text9 === "null" || text9 === "~") return accept(null);
|
|
5316
|
+
if (NUMBER.test(text9)) return accept(Number(text9));
|
|
5317
|
+
return accept(text9);
|
|
4860
5318
|
};
|
|
4861
5319
|
|
|
4862
5320
|
// src/core/yaml/flow.ts
|
|
@@ -4883,17 +5341,17 @@ var splitTop = (body2) => {
|
|
|
4883
5341
|
if (last.trim() !== "" || parts.length > 0) parts.push(last);
|
|
4884
5342
|
return accept(parts.map((part2) => part2.trim()));
|
|
4885
5343
|
};
|
|
4886
|
-
var splitKey = (
|
|
5344
|
+
var splitKey = (text9) => {
|
|
4887
5345
|
let index = 0;
|
|
4888
|
-
if (
|
|
4889
|
-
index = quotedEnd(
|
|
5346
|
+
if (text9.startsWith('"') || text9.startsWith("'")) {
|
|
5347
|
+
index = quotedEnd(text9, 0) + 1;
|
|
4890
5348
|
if (index <= 0) return void 0;
|
|
4891
5349
|
}
|
|
4892
|
-
for (; index <
|
|
4893
|
-
const char =
|
|
4894
|
-
if (char === "#" &&
|
|
4895
|
-
if (char === ":" && (index + 1 ===
|
|
4896
|
-
return { key:
|
|
5350
|
+
for (; index < text9.length; index += 1) {
|
|
5351
|
+
const char = text9[index];
|
|
5352
|
+
if (char === "#" && text9[index - 1] === " ") return void 0;
|
|
5353
|
+
if (char === ":" && (index + 1 === text9.length || text9[index + 1] === " ")) {
|
|
5354
|
+
return { key: text9.slice(0, index).trim(), rest: text9.slice(index + 1).trim() };
|
|
4897
5355
|
}
|
|
4898
5356
|
}
|
|
4899
5357
|
return void 0;
|
|
@@ -4922,39 +5380,39 @@ var flowSeq = (parts) => {
|
|
|
4922
5380
|
}
|
|
4923
5381
|
return accept(out);
|
|
4924
5382
|
};
|
|
4925
|
-
var parseValue = (
|
|
4926
|
-
if (
|
|
4927
|
-
const first =
|
|
4928
|
-
if (first === '"' || first === "'") return parseQuoted(
|
|
5383
|
+
var parseValue = (text9) => {
|
|
5384
|
+
if (text9 === "") return accept(null);
|
|
5385
|
+
const first = text9[0] ?? "";
|
|
5386
|
+
if (first === '"' || first === "'") return parseQuoted(text9);
|
|
4929
5387
|
const close2 = OPEN[first];
|
|
4930
|
-
if (close2 === void 0) return parsePlain(
|
|
4931
|
-
if (!
|
|
4932
|
-
const parts = splitTop(
|
|
5388
|
+
if (close2 === void 0) return parsePlain(text9);
|
|
5389
|
+
if (!text9.endsWith(close2)) return reject(`flow collection must end on the same line with "${close2}"`);
|
|
5390
|
+
const parts = splitTop(text9.slice(1, -1));
|
|
4933
5391
|
if (!parts.ok) return parts;
|
|
4934
5392
|
return first === "[" ? flowSeq(parts.value) : flowMap(parts.value);
|
|
4935
5393
|
};
|
|
4936
5394
|
|
|
4937
5395
|
// src/core/yaml/parse.ts
|
|
4938
5396
|
var BOM = new RegExp(`^${String.fromCharCode(65279)}`);
|
|
4939
|
-
var stripComment2 = (
|
|
4940
|
-
for (let index = 0; index <
|
|
4941
|
-
const char =
|
|
5397
|
+
var stripComment2 = (text9) => {
|
|
5398
|
+
for (let index = 0; index < text9.length; index += 1) {
|
|
5399
|
+
const char = text9[index];
|
|
4942
5400
|
if (char === '"' || char === "'") {
|
|
4943
|
-
const end = quotedEnd(
|
|
4944
|
-
if (end < 0) return
|
|
5401
|
+
const end = quotedEnd(text9, index);
|
|
5402
|
+
if (end < 0) return text9;
|
|
4945
5403
|
index = end;
|
|
4946
|
-
} else if (char === "#" && (index === 0 ||
|
|
5404
|
+
} else if (char === "#" && (index === 0 || text9[index - 1] === " ")) return text9.slice(0, index);
|
|
4947
5405
|
}
|
|
4948
|
-
return
|
|
5406
|
+
return text9;
|
|
4949
5407
|
};
|
|
4950
5408
|
var toLines = (source) => {
|
|
4951
5409
|
const lines3 = [];
|
|
4952
5410
|
const raw = source.replace(BOM, "").split(/\r?\n/);
|
|
4953
5411
|
for (const [index, full] of raw.entries()) {
|
|
4954
|
-
const
|
|
4955
|
-
const body2 =
|
|
5412
|
+
const text9 = stripComment2(full).trimEnd();
|
|
5413
|
+
const body2 = text9.trimStart();
|
|
4956
5414
|
if (body2 === "" || body2 === "---" && lines3.length === 0) continue;
|
|
4957
|
-
const indentText =
|
|
5415
|
+
const indentText = text9.slice(0, text9.length - body2.length);
|
|
4958
5416
|
if (indentText.includes(" ")) return reject({ line: index + 1, message: "tabs are not allowed for indentation" });
|
|
4959
5417
|
if (body2 === "---" || body2 === "...")
|
|
4960
5418
|
return reject({ line: index + 1, message: "only one YAML document is allowed" });
|
|
@@ -4963,11 +5421,11 @@ var toLines = (source) => {
|
|
|
4963
5421
|
return accept(lines3);
|
|
4964
5422
|
};
|
|
4965
5423
|
var fail = (line2, message) => reject({ line: line2?.number ?? 0, message });
|
|
4966
|
-
var scalar = (line2,
|
|
4967
|
-
const value = parseValue(
|
|
5424
|
+
var scalar = (line2, text9) => {
|
|
5425
|
+
const value = parseValue(text9);
|
|
4968
5426
|
return value.ok ? value : fail(line2, value.error);
|
|
4969
5427
|
};
|
|
4970
|
-
var isItem = (
|
|
5428
|
+
var isItem = (text9) => text9 === "-" || text9.startsWith("- ");
|
|
4971
5429
|
var child = (lines3, index, parent, sameIndentSeq) => {
|
|
4972
5430
|
const next2 = lines3[index];
|
|
4973
5431
|
if (next2 !== void 0 && next2.indent > parent.indent) return block(lines3, index, next2.indent);
|
|
@@ -5210,8 +5668,8 @@ var validateContract = (value, implicit = false) => {
|
|
|
5210
5668
|
// src/task/load.ts
|
|
5211
5669
|
var contractHash = (contract) => sha256Hex(JSON.stringify(contract.acceptance.map((check2) => [check2.id, check2.kind, check2.run ?? ""])));
|
|
5212
5670
|
var taskRelative = (id) => `.finitemonkey/tasks/${id}.yml`;
|
|
5213
|
-
var parseContract = (id,
|
|
5214
|
-
const yaml = parseYaml(
|
|
5671
|
+
var parseContract = (id, text9) => {
|
|
5672
|
+
const yaml = parseYaml(text9);
|
|
5215
5673
|
if (!yaml.ok)
|
|
5216
5674
|
return reject({ kind: "invalid", id, issues: [`line ${String(yaml.error.line)}: ${yaml.error.message}`] });
|
|
5217
5675
|
const valid = validateContract(yaml.value);
|
|
@@ -5285,7 +5743,7 @@ var readBaseline = (runtime, paths) => {
|
|
|
5285
5743
|
var writeBaseline = (runtime, paths, treeHash2, checks) => writeJsonFile(runtime, paths.cache(BASELINE_FILE), { treeHash: treeHash2, checks: { ...checks } }).ok;
|
|
5286
5744
|
|
|
5287
5745
|
// src/gate/diff-shape.ts
|
|
5288
|
-
import { join as
|
|
5746
|
+
import { join as join16 } from "node:path";
|
|
5289
5747
|
|
|
5290
5748
|
// src/gate/git.ts
|
|
5291
5749
|
var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
@@ -5321,10 +5779,10 @@ var resolveBase = (git2, declared) => {
|
|
|
5321
5779
|
const merged2 = remote === void 0 ? void 0 : git2.out(["merge-base", "HEAD", remote])?.trim();
|
|
5322
5780
|
return nonEmpty(merged2) ?? nonEmpty(headSha(git2)) ?? EMPTY_TREE;
|
|
5323
5781
|
};
|
|
5324
|
-
var nulFields = (
|
|
5782
|
+
var nulFields = (text9) => text9.split("\0").filter((part2) => part2 !== "");
|
|
5325
5783
|
var untracked = (git2) => nulFields(git2.out(["ls-files", "--others", "--exclude-standard", "-z", "--", ".", EXCLUDE_FM]) ?? "");
|
|
5326
|
-
var parseNameStatus = (
|
|
5327
|
-
const fields = nulFields(
|
|
5784
|
+
var parseNameStatus = (text9) => {
|
|
5785
|
+
const fields = nulFields(text9);
|
|
5328
5786
|
const changes = [];
|
|
5329
5787
|
for (let index = 0; index < fields.length; index += 1) {
|
|
5330
5788
|
const code = fields[index] ?? "";
|
|
@@ -5392,8 +5850,8 @@ var aheadOfUpstream = (git2) => {
|
|
|
5392
5850
|
|
|
5393
5851
|
// src/gate/diff-shape.ts
|
|
5394
5852
|
var untrackedLines = (runtime, root, changes) => changes.filter((change) => change.status === "A").reduce((sum, change) => {
|
|
5395
|
-
const
|
|
5396
|
-
return sum + (
|
|
5853
|
+
const text9 = runtime.readText(toAbsolutePath(join16(root, change.path)));
|
|
5854
|
+
return sum + (text9.ok ? text9.value.split("\n").length : 0);
|
|
5397
5855
|
}, 0);
|
|
5398
5856
|
var diffShape = (runtime, git2, base, changes) => ({
|
|
5399
5857
|
files: changes.map((change) => change.path),
|
|
@@ -5416,8 +5874,8 @@ var ciChecks = (value) => Array.isArray(value) ? value.flatMap((item2) => {
|
|
|
5416
5874
|
const name = optionalString(item2, "name") ?? optionalString(item2, "context") ?? "?";
|
|
5417
5875
|
return [{ name, state: ciState(item2) }];
|
|
5418
5876
|
}) : [];
|
|
5419
|
-
var parsePrView = (
|
|
5420
|
-
const parsed = parseJsonObject(
|
|
5877
|
+
var parsePrView = (text9) => {
|
|
5878
|
+
const parsed = parseJsonObject(text9);
|
|
5421
5879
|
if (!parsed.ok) return void 0;
|
|
5422
5880
|
const record3 = parsed.value;
|
|
5423
5881
|
const number2 = record3["number"];
|
|
@@ -5598,10 +6056,10 @@ var applySize = (contract, verdict) => {
|
|
|
5598
6056
|
// src/gate/prepare.ts
|
|
5599
6057
|
var diskFiles = (runtime, root) => ({
|
|
5600
6058
|
read: (relative11) => {
|
|
5601
|
-
const
|
|
5602
|
-
return
|
|
6059
|
+
const text9 = runtime.readText(toAbsolutePath(join17(root, relative11)));
|
|
6060
|
+
return text9.ok ? text9.value : void 0;
|
|
5603
6061
|
},
|
|
5604
|
-
exists: (relative11) => runtime.stat(toAbsolutePath(
|
|
6062
|
+
exists: (relative11) => runtime.stat(toAbsolutePath(join17(root, relative11))) !== void 0
|
|
5605
6063
|
});
|
|
5606
6064
|
var baseFiles = (git2, base, disk) => ({
|
|
5607
6065
|
read: (relative11) => base === EMPTY_TREE ? disk.read(relative11) : fileAt(git2, base, relative11),
|
|
@@ -5613,7 +6071,7 @@ var loadContract = (options, paths, git2, base) => {
|
|
|
5613
6071
|
if (id === void 0) return accept(loadImplicit(options.runtime, paths, baseFiles(git2, base, disk)));
|
|
5614
6072
|
return loadTask(options.runtime, paths, { disk: disk.read, base: (relative11) => fileAt(git2, base, relative11) }, id);
|
|
5615
6073
|
};
|
|
5616
|
-
var
|
|
6074
|
+
var memo2 = (make) => {
|
|
5617
6075
|
let value;
|
|
5618
6076
|
return () => {
|
|
5619
6077
|
value ??= { v: make() };
|
|
@@ -5652,7 +6110,7 @@ var prepareGate = (options) => {
|
|
|
5652
6110
|
if (tree === void 0) return { kind: "failed", failure: { kind: "no_tree_hash" } };
|
|
5653
6111
|
const skip = options.noSkip === true ? void 0 : skipReason(options.runtime, paths, loaded.value, changes, tree, options.sessionId);
|
|
5654
6112
|
if (skip !== void 0) return { kind: "skipped", reason: skip };
|
|
5655
|
-
const pr =
|
|
6113
|
+
const pr = memo2(() => prStatus(options.runtime, git2, paths));
|
|
5656
6114
|
const size = measureSize(options.runtime, git2, base, changes, loaded.value.contract);
|
|
5657
6115
|
const ctx = {
|
|
5658
6116
|
runtime: options.runtime,
|
|
@@ -5720,11 +6178,11 @@ var trust = (input, paths, task) => {
|
|
|
5720
6178
|
});
|
|
5721
6179
|
if (ready.kind !== "ready" || ready.prepared.ctx.contract.id !== task) return false;
|
|
5722
6180
|
const lock = readTrust(runtime, paths);
|
|
5723
|
-
const
|
|
6181
|
+
const text9 = runtime.readText(paths.task(task));
|
|
5724
6182
|
return writeTrust(runtime, paths, {
|
|
5725
6183
|
...lock,
|
|
5726
6184
|
contracts: { ...lock.contracts, [task]: ready.prepared.loaded.hash },
|
|
5727
|
-
approved: task === IMPLICIT_ID || !
|
|
6185
|
+
approved: task === IMPLICIT_ID || !text9.ok ? lock.approved : { ...lock.approved, [task]: text9.value }
|
|
5728
6186
|
}).ok;
|
|
5729
6187
|
};
|
|
5730
6188
|
var apply = (input, paths, markers) => {
|
|
@@ -5776,8 +6234,8 @@ var promptRelease = (input) => {
|
|
|
5776
6234
|
var PRS_CACHE_MS = 10 * 6e4;
|
|
5777
6235
|
var PRS_TIMEOUT_MS = 3e3;
|
|
5778
6236
|
var PRS_AUTH_TIMEOUT_MS = 2e3;
|
|
5779
|
-
var parsePrList = (
|
|
5780
|
-
const parsed = parseJson(
|
|
6237
|
+
var parsePrList = (text9) => {
|
|
6238
|
+
const parsed = parseJson(text9);
|
|
5781
6239
|
if (!parsed.ok || !Array.isArray(parsed.value)) return void 0;
|
|
5782
6240
|
return parsed.value.flatMap((item2) => {
|
|
5783
6241
|
if (!isRecord(item2) || typeof item2["number"] !== "number") return [];
|
|
@@ -5863,16 +6321,16 @@ var params = (value) => isRecord(value) ? Object.fromEntries(
|
|
|
5863
6321
|
var notes = (value) => Array.isArray(value) ? value.flatMap(
|
|
5864
6322
|
(item2) => isRecord(item2) && typeof item2["key"] === "string" ? [{ key: item2["key"], params: params(item2["params"]) }] : []
|
|
5865
6323
|
) : [];
|
|
5866
|
-
var
|
|
6324
|
+
var text4 = (record3, key) => {
|
|
5867
6325
|
const value = record3[key];
|
|
5868
6326
|
return typeof value === "string" ? value : void 0;
|
|
5869
6327
|
};
|
|
5870
6328
|
var optional = (record3) => {
|
|
5871
6329
|
const exitCode = record3["exitCode"];
|
|
5872
6330
|
const durationMs = record3["durationMs"];
|
|
5873
|
-
const command =
|
|
5874
|
-
const stdoutSha256 =
|
|
5875
|
-
const releasedBy =
|
|
6331
|
+
const command = text4(record3, "command");
|
|
6332
|
+
const stdoutSha256 = text4(record3, "stdoutSha256");
|
|
6333
|
+
const releasedBy = text4(record3, "releasedBy");
|
|
5876
6334
|
const reason = oneOf2(UNVERIFIED_REASONS, record3["reason"]);
|
|
5877
6335
|
const failures = record3["failures"];
|
|
5878
6336
|
const testCount = record3["testCount"];
|
|
@@ -5892,11 +6350,11 @@ var parseEvidence = (line2) => {
|
|
|
5892
6350
|
if (!parsed.ok) return void 0;
|
|
5893
6351
|
const record3 = parsed.value;
|
|
5894
6352
|
const status = oneOf2(EVIDENCE_STATUSES, record3["status"]);
|
|
5895
|
-
const task =
|
|
5896
|
-
const check2 =
|
|
5897
|
-
const kind =
|
|
5898
|
-
const treeHash2 =
|
|
5899
|
-
const at =
|
|
6353
|
+
const task = text4(record3, "task");
|
|
6354
|
+
const check2 = text4(record3, "check");
|
|
6355
|
+
const kind = text4(record3, "kind");
|
|
6356
|
+
const treeHash2 = text4(record3, "treeHash");
|
|
6357
|
+
const at = text4(record3, "at");
|
|
5900
6358
|
if (record3["schema"] !== EVIDENCE_SCHEMA || status === void 0) return void 0;
|
|
5901
6359
|
if (task === void 0 || check2 === void 0 || kind === void 0 || treeHash2 === void 0 || at === void 0) {
|
|
5902
6360
|
return void 0;
|
|
@@ -5913,7 +6371,7 @@ var parseEvidence = (line2) => {
|
|
|
5913
6371
|
...optional(record3)
|
|
5914
6372
|
};
|
|
5915
6373
|
};
|
|
5916
|
-
var
|
|
6374
|
+
var readLedger2 = (runtime, path) => {
|
|
5917
6375
|
const content = runtime.readText(path);
|
|
5918
6376
|
if (!content.ok) return [];
|
|
5919
6377
|
return content.value.split("\n").flatMap((line2) => {
|
|
@@ -6595,7 +7053,28 @@ var en_default = {
|
|
|
6595
7053
|
"gain.row.gate_time": "{minutes, plural, one {# minute} other {# minutes}} spent in the gate",
|
|
6596
7054
|
"gain.up": "up {delta} on the window before",
|
|
6597
7055
|
"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."
|
|
7056
|
+
"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.",
|
|
7057
|
+
"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",
|
|
7058
|
+
"critique.title": "Critique",
|
|
7059
|
+
"critique.group.must": "Must fix",
|
|
7060
|
+
"critique.group.should": "Should fix",
|
|
7061
|
+
"critique.group.nice": "Nice to have",
|
|
7062
|
+
"critique.group.already-there": "Already there (fails on base too)",
|
|
7063
|
+
"critique.group.not-reproduced": "Not reproduced",
|
|
7064
|
+
"critique.finding": "{id}. {what}. Problem: {problem}. Fix: {fix}. If not fixed: {risk}.",
|
|
7065
|
+
"critique.table.command": "reproduction",
|
|
7066
|
+
"critique.table.outcome": "outcome",
|
|
7067
|
+
"critique.outcome.head-only": "fails only on head: kept",
|
|
7068
|
+
"critique.outcome.already-there": "fails on both: already there",
|
|
7069
|
+
"critique.outcome.not-reproduced": "fails on neither: not reproduced",
|
|
7070
|
+
"critique.outcome.not-run": "not run",
|
|
7071
|
+
"critique.demoted": "Finding {id} was marked must fix without a reproduction command and expected result, so it counts as should fix.",
|
|
7072
|
+
"critique.decision": "Decision needed: finding {id} ({what}) already fails on the base branch and is an active risk.",
|
|
7073
|
+
"critique.verdict": "Verdict: {verdict}",
|
|
7074
|
+
"critique.error.invalid": "the findings file is not valid: {detail}",
|
|
7075
|
+
"critique.error.must_fix": "{count, plural, one {# must-fix finding reproduced on head only} other {# must-fix findings reproduced on head only}}",
|
|
7076
|
+
"install.warn.sandbox_offer": "The Claude Code sandbox runs here. Add --sandbox to turn it on for you only, in .claude/settings.local.json: it fails to start without the sandbox, hides ~/.ssh, ~/.aws and .env files from commands, and lets them reach only the hosts in sandbox.hosts.",
|
|
7077
|
+
"install.warn.sandbox_unsupported": "--sandbox was ignored: native Windows has no Claude Code sandbox, so the guards are the only protection here. Run Claude Code inside WSL2 to use it."
|
|
6599
7078
|
};
|
|
6600
7079
|
|
|
6601
7080
|
// src/i18n/es.json
|
|
@@ -7265,7 +7744,28 @@ var es_default = {
|
|
|
7265
7744
|
"gain.row.gate_time": "{minutes, plural, one {# minuto} other {# minutos}} en la compuerta",
|
|
7266
7745
|
"gain.up": "+{delta} respecto a la ventana anterior",
|
|
7267
7746
|
"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."
|
|
7747
|
+
"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.",
|
|
7748
|
+
"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",
|
|
7749
|
+
"critique.title": "Crítica",
|
|
7750
|
+
"critique.group.must": "Corregir sí o sí",
|
|
7751
|
+
"critique.group.should": "Debería corregirse",
|
|
7752
|
+
"critique.group.nice": "Sería bueno",
|
|
7753
|
+
"critique.group.already-there": "Ya estaba (falla también en base)",
|
|
7754
|
+
"critique.group.not-reproduced": "No reproducido",
|
|
7755
|
+
"critique.finding": "{id}. {what}. Problema: {problem}. Arreglo: {fix}. Si no se corrige: {risk}.",
|
|
7756
|
+
"critique.table.command": "reproducción",
|
|
7757
|
+
"critique.table.outcome": "resultado",
|
|
7758
|
+
"critique.outcome.head-only": "falla solo en head: se mantiene",
|
|
7759
|
+
"critique.outcome.already-there": "falla en ambos: ya estaba",
|
|
7760
|
+
"critique.outcome.not-reproduced": "no falla en ninguno: no reproducido",
|
|
7761
|
+
"critique.outcome.not-run": "no se ejecutó",
|
|
7762
|
+
"critique.demoted": "El hallazgo {id} venía como obligatorio sin comando de reproducción ni resultado esperado, así que cuenta como debería corregirse.",
|
|
7763
|
+
"critique.decision": "Decisión necesaria: el hallazgo {id} ({what}) ya falla en la rama base y es un riesgo activo.",
|
|
7764
|
+
"critique.verdict": "Veredicto: {verdict}",
|
|
7765
|
+
"critique.error.invalid": "el archivo de hallazgos no es válido: {detail}",
|
|
7766
|
+
"critique.error.must_fix": "{count, plural, one {# hallazgo obligatorio reproducido solo en head} other {# hallazgos obligatorios reproducidos solo en head}}",
|
|
7767
|
+
"install.warn.sandbox_offer": "El sandbox de Claude Code funciona aquí. Añade --sandbox para activarlo solo para ti, en .claude/settings.local.json: no arranca sin el sandbox, oculta ~/.ssh, ~/.aws y los archivos .env a los comandos y solo deja llegar a los hosts de sandbox.hosts.",
|
|
7768
|
+
"install.warn.sandbox_unsupported": "Se ignoró --sandbox: Windows nativo no tiene sandbox de Claude Code, así que aquí solo protegen los bloqueos. Ejecuta Claude Code dentro de WSL2 para usarlo."
|
|
7269
7769
|
};
|
|
7270
7770
|
|
|
7271
7771
|
// src/i18n/pt-BR.json
|
|
@@ -7935,7 +8435,28 @@ var pt_BR_default = {
|
|
|
7935
8435
|
"gain.row.gate_time": "{minutes, plural, one {# minuto} other {# minutos}} no portão",
|
|
7936
8436
|
"gain.up": "+{delta} em relação à janela anterior",
|
|
7937
8437
|
"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."
|
|
8438
|
+
"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.",
|
|
8439
|
+
"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",
|
|
8440
|
+
"critique.title": "Crítica",
|
|
8441
|
+
"critique.group.must": "Corrigir obrigatoriamente",
|
|
8442
|
+
"critique.group.should": "Deveria corrigir",
|
|
8443
|
+
"critique.group.nice": "Seria bom",
|
|
8444
|
+
"critique.group.already-there": "Já existia (falha na base também)",
|
|
8445
|
+
"critique.group.not-reproduced": "Não reproduzido",
|
|
8446
|
+
"critique.finding": "{id}. {what}. Problema: {problem}. Conserto: {fix}. Se não corrigir: {risk}.",
|
|
8447
|
+
"critique.table.command": "reprodução",
|
|
8448
|
+
"critique.table.outcome": "resultado",
|
|
8449
|
+
"critique.outcome.head-only": "falha só no head: mantido",
|
|
8450
|
+
"critique.outcome.already-there": "falha nos dois: já existia",
|
|
8451
|
+
"critique.outcome.not-reproduced": "não falha em nenhum: não reproduzido",
|
|
8452
|
+
"critique.outcome.not-run": "não rodou",
|
|
8453
|
+
"critique.demoted": "O achado {id} veio como obrigatório sem comando de reprodução e resultado esperado, então conta como deveria corrigir.",
|
|
8454
|
+
"critique.decision": "Decisão necessária: o achado {id} ({what}) já falha na branch base e é um risco ativo.",
|
|
8455
|
+
"critique.verdict": "Veredito: {verdict}",
|
|
8456
|
+
"critique.error.invalid": "o arquivo de achados não é válido: {detail}",
|
|
8457
|
+
"critique.error.must_fix": "{count, plural, one {# achado obrigatório reproduzido só no head} other {# achados obrigatórios reproduzidos só no head}}",
|
|
8458
|
+
"install.warn.sandbox_offer": "O sandbox do Claude Code roda aqui. Acrescente --sandbox para ligar só para você, em .claude/settings.local.json: ele se recusa a iniciar sem o sandbox, esconde ~/.ssh, ~/.aws e os arquivos .env dos comandos e só deixa chegar aos hosts de sandbox.hosts.",
|
|
8459
|
+
"install.warn.sandbox_unsupported": "--sandbox foi ignorado: o Windows nativo não tem sandbox do Claude Code, então aqui só as travas protegem. Rode o Claude Code dentro do WSL2 para usar."
|
|
7939
8460
|
};
|
|
7940
8461
|
|
|
7941
8462
|
// src/i18n/review.json
|
|
@@ -8606,7 +9127,28 @@ var review_default = {
|
|
|
8606
9127
|
"gain.row.gate_time": "machine",
|
|
8607
9128
|
"gain.up": "machine",
|
|
8608
9129
|
"gain.down": "machine",
|
|
8609
|
-
"gain.footer": "machine"
|
|
9130
|
+
"gain.footer": "machine",
|
|
9131
|
+
"critique.command.verify": "machine",
|
|
9132
|
+
"critique.title": "machine",
|
|
9133
|
+
"critique.group.must": "machine",
|
|
9134
|
+
"critique.group.should": "machine",
|
|
9135
|
+
"critique.group.nice": "machine",
|
|
9136
|
+
"critique.group.already-there": "machine",
|
|
9137
|
+
"critique.group.not-reproduced": "machine",
|
|
9138
|
+
"critique.finding": "machine",
|
|
9139
|
+
"critique.table.command": "machine",
|
|
9140
|
+
"critique.table.outcome": "machine",
|
|
9141
|
+
"critique.outcome.head-only": "machine",
|
|
9142
|
+
"critique.outcome.already-there": "machine",
|
|
9143
|
+
"critique.outcome.not-reproduced": "machine",
|
|
9144
|
+
"critique.outcome.not-run": "machine",
|
|
9145
|
+
"critique.demoted": "machine",
|
|
9146
|
+
"critique.decision": "machine",
|
|
9147
|
+
"critique.verdict": "machine",
|
|
9148
|
+
"critique.error.invalid": "machine",
|
|
9149
|
+
"critique.error.must_fix": "machine",
|
|
9150
|
+
"install.warn.sandbox_offer": "machine",
|
|
9151
|
+
"install.warn.sandbox_unsupported": "machine"
|
|
8610
9152
|
},
|
|
8611
9153
|
es: {
|
|
8612
9154
|
"command.init.summary": "machine",
|
|
@@ -9274,7 +9816,28 @@ var review_default = {
|
|
|
9274
9816
|
"gain.row.gate_time": "machine",
|
|
9275
9817
|
"gain.up": "machine",
|
|
9276
9818
|
"gain.down": "machine",
|
|
9277
|
-
"gain.footer": "machine"
|
|
9819
|
+
"gain.footer": "machine",
|
|
9820
|
+
"critique.command.verify": "machine",
|
|
9821
|
+
"critique.title": "machine",
|
|
9822
|
+
"critique.group.must": "machine",
|
|
9823
|
+
"critique.group.should": "machine",
|
|
9824
|
+
"critique.group.nice": "machine",
|
|
9825
|
+
"critique.group.already-there": "machine",
|
|
9826
|
+
"critique.group.not-reproduced": "machine",
|
|
9827
|
+
"critique.finding": "machine",
|
|
9828
|
+
"critique.table.command": "machine",
|
|
9829
|
+
"critique.table.outcome": "machine",
|
|
9830
|
+
"critique.outcome.head-only": "machine",
|
|
9831
|
+
"critique.outcome.already-there": "machine",
|
|
9832
|
+
"critique.outcome.not-reproduced": "machine",
|
|
9833
|
+
"critique.outcome.not-run": "machine",
|
|
9834
|
+
"critique.demoted": "machine",
|
|
9835
|
+
"critique.decision": "machine",
|
|
9836
|
+
"critique.verdict": "machine",
|
|
9837
|
+
"critique.error.invalid": "machine",
|
|
9838
|
+
"critique.error.must_fix": "machine",
|
|
9839
|
+
"install.warn.sandbox_offer": "machine",
|
|
9840
|
+
"install.warn.sandbox_unsupported": "machine"
|
|
9278
9841
|
}
|
|
9279
9842
|
};
|
|
9280
9843
|
|
|
@@ -9431,7 +9994,7 @@ var currentReport = (runtime, paths, sessionId, taskId) => {
|
|
|
9431
9994
|
const { ctx, loaded } = ready.prepared;
|
|
9432
9995
|
return buildReport({
|
|
9433
9996
|
contract: ctx.contract,
|
|
9434
|
-
ledger:
|
|
9997
|
+
ledger: readLedger2(runtime, paths.evidence(ctx.contract.id)),
|
|
9435
9998
|
treeHash: ctx.treeHash,
|
|
9436
9999
|
base: ctx.base,
|
|
9437
10000
|
source: loaded.source,
|
|
@@ -9457,22 +10020,22 @@ var saveSnapshot = (runtime, paths, t, sessionId) => {
|
|
|
9457
10020
|
const snapshot2 = snapshotOf(t, currentReport(runtime, paths, sessionId), grants, new Date(now).toISOString());
|
|
9458
10021
|
return writeState(runtime, paths, { ...readState(runtime, paths), snapshot: snapshot2 }).ok;
|
|
9459
10022
|
};
|
|
9460
|
-
var
|
|
10023
|
+
var text5 = (snapshot2, key) => {
|
|
9461
10024
|
const value = snapshot2[key];
|
|
9462
10025
|
return typeof value === "string" ? value : "";
|
|
9463
10026
|
};
|
|
9464
10027
|
var snapshotFacts = (t, snapshot2) => {
|
|
9465
|
-
if (snapshot2 === void 0 ||
|
|
9466
|
-
const task =
|
|
10028
|
+
if (snapshot2 === void 0 || text5(snapshot2, "at") === "") return void 0;
|
|
10029
|
+
const task = text5(snapshot2, "task");
|
|
9467
10030
|
return t(task === "" || task === "implicit" ? "gate.context.state_implicit" : "gate.context.state", {
|
|
9468
|
-
at:
|
|
10031
|
+
at: text5(snapshot2, "at"),
|
|
9469
10032
|
task,
|
|
9470
|
-
goal:
|
|
9471
|
-
tree:
|
|
9472
|
-
passed:
|
|
9473
|
-
failed:
|
|
9474
|
-
unverified:
|
|
9475
|
-
grants:
|
|
10033
|
+
goal: text5(snapshot2, "goal"),
|
|
10034
|
+
tree: text5(snapshot2, "tree"),
|
|
10035
|
+
passed: text5(snapshot2, "passed"),
|
|
10036
|
+
failed: text5(snapshot2, "failed"),
|
|
10037
|
+
unverified: text5(snapshot2, "unverified"),
|
|
10038
|
+
grants: text5(snapshot2, "grants")
|
|
9476
10039
|
});
|
|
9477
10040
|
};
|
|
9478
10041
|
|
|
@@ -9564,10 +10127,10 @@ var pairs = (record3) => {
|
|
|
9564
10127
|
return names.filter((name) => typeof name === "string").map((name) => `${task}\0${name}`);
|
|
9565
10128
|
};
|
|
9566
10129
|
var callersRate = (runtime, paths) => {
|
|
9567
|
-
const
|
|
10130
|
+
const text9 = runtime.readText(paths.log);
|
|
9568
10131
|
const flagged = /* @__PURE__ */ new Set();
|
|
9569
10132
|
const released2 = /* @__PURE__ */ new Set();
|
|
9570
|
-
for (const line2 of
|
|
10133
|
+
for (const line2 of text9.ok ? text9.value.split("\n") : []) {
|
|
9571
10134
|
const parsed = line2.trim() === "" ? void 0 : parseJsonObject(line2);
|
|
9572
10135
|
if (parsed === void 0 || !parsed.ok || !isRecord(parsed.value)) continue;
|
|
9573
10136
|
const event = parsed.value["event"];
|
|
@@ -9585,14 +10148,14 @@ var callersRate = (runtime, paths) => {
|
|
|
9585
10148
|
};
|
|
9586
10149
|
|
|
9587
10150
|
// src/gate/callers/scan.ts
|
|
9588
|
-
import { join as
|
|
10151
|
+
import { join as join18 } from "node:path";
|
|
9589
10152
|
|
|
9590
10153
|
// src/gate/callers/imports.ts
|
|
9591
10154
|
import { posix as posix2 } from "node:path";
|
|
9592
10155
|
var JS_SPECIFIER = /(?:\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)["']([^"']+)["']/g;
|
|
9593
10156
|
var JS_EXTENSION = /\.(?:[cm]?[jt]sx?)$/;
|
|
9594
10157
|
var stripModule = (path) => path.replace(JS_EXTENSION, "").replace(/\/index$/, "");
|
|
9595
|
-
var jsSpecifiers = (
|
|
10158
|
+
var jsSpecifiers = (text9) => [...text9.matchAll(JS_SPECIFIER)].map((match) => match[1] ?? "");
|
|
9596
10159
|
var jsImports = (callerPath, callerText, origin) => {
|
|
9597
10160
|
const target2 = stripModule(origin);
|
|
9598
10161
|
return jsSpecifiers(callerText).some((specifier) => {
|
|
@@ -9694,9 +10257,9 @@ var languageOf = (path) => {
|
|
|
9694
10257
|
const extension = extname(path).toLowerCase();
|
|
9695
10258
|
return LANGUAGES2.find((language) => language.extensions.includes(extension));
|
|
9696
10259
|
};
|
|
9697
|
-
var definitionsIn = (language,
|
|
10260
|
+
var definitionsIn = (language, text9) => {
|
|
9698
10261
|
const names = /* @__PURE__ */ new Set();
|
|
9699
|
-
for (const line2 of
|
|
10262
|
+
for (const line2 of text9.split(/\r?\n/)) {
|
|
9700
10263
|
for (const pattern of language.definitions) {
|
|
9701
10264
|
const name = pattern.exec(line2)?.[1];
|
|
9702
10265
|
if (name !== void 0 && !KEYWORDS.has(name)) names.add(name);
|
|
@@ -9709,8 +10272,8 @@ var definesName = (language, line2, name) => language.definitions.some((pattern)
|
|
|
9709
10272
|
// src/gate/callers/scan.ts
|
|
9710
10273
|
var MAX_SYMBOLS = 200;
|
|
9711
10274
|
var current = (runtime, root, path) => {
|
|
9712
|
-
const
|
|
9713
|
-
return
|
|
10275
|
+
const text9 = runtime.readText(toAbsolutePath(join18(root, path)));
|
|
10276
|
+
return text9.ok ? text9.value : "";
|
|
9714
10277
|
};
|
|
9715
10278
|
var keyOf = (language, name) => `${language.id}\0${name}`;
|
|
9716
10279
|
var redefinedNames = (runtime, git2, changes) => {
|
|
@@ -9751,7 +10314,7 @@ var grep = (git2, names) => {
|
|
|
9751
10314
|
git2.out(["grep", "-n", "-z", "-w", "-I", "--untracked", "--no-color", ...patterns, "--", ".", EXCLUDE_FM]) ?? ""
|
|
9752
10315
|
);
|
|
9753
10316
|
};
|
|
9754
|
-
var mentions = (
|
|
10317
|
+
var mentions = (text9, name) => new RegExp(`(?<![\\w$])${name.replace(/[$?!]/g, "\\$&")}(?![\\w$])`).test(text9);
|
|
9755
10318
|
var callSites = (runtime, git2, symbols) => {
|
|
9756
10319
|
const hits = grep(git2, [...new Set(symbols.map((symbol) => symbol.name))]);
|
|
9757
10320
|
const texts2 = /* @__PURE__ */ new Map();
|
|
@@ -9826,31 +10389,31 @@ var ZERO = [
|
|
|
9826
10389
|
/^ℹ tests 0$/m,
|
|
9827
10390
|
/No tests found/
|
|
9828
10391
|
];
|
|
9829
|
-
var pyTotal = (
|
|
9830
|
-
const line2 = /^=+ (.*) in [\d.]+s.*=+$/m.exec(
|
|
10392
|
+
var pyTotal = (text9) => {
|
|
10393
|
+
const line2 = /^=+ (.*) in [\d.]+s.*=+$/m.exec(text9)?.[1];
|
|
9831
10394
|
if (line2 === void 0) return void 0;
|
|
9832
10395
|
return [...line2.matchAll(/(\d+) (passed|failed|error|errors|skipped|xfailed|xpassed)/g)].reduce(
|
|
9833
10396
|
(sum, match) => sum + Number(match[1] ?? "0"),
|
|
9834
10397
|
0
|
|
9835
10398
|
);
|
|
9836
10399
|
};
|
|
9837
|
-
var counted = (
|
|
10400
|
+
var counted = (text9) => {
|
|
9838
10401
|
for (const counter of COUNTERS) {
|
|
9839
|
-
const total = counter.total.exec(
|
|
10402
|
+
const total = counter.total.exec(text9)?.[1];
|
|
9840
10403
|
if (total === void 0) continue;
|
|
9841
|
-
const failed4 = counter.failed?.exec(
|
|
10404
|
+
const failed4 = counter.failed?.exec(text9)?.[1];
|
|
9842
10405
|
return { total: Number(total), ...failed4 === void 0 ? {} : { failed: Number(failed4) } };
|
|
9843
10406
|
}
|
|
9844
|
-
const py = pyTotal(
|
|
10407
|
+
const py = pyTotal(text9);
|
|
9845
10408
|
return py === void 0 ? {} : { total: py };
|
|
9846
10409
|
};
|
|
9847
10410
|
var parseTestSummary = (output) => {
|
|
9848
|
-
const
|
|
10411
|
+
const text9 = output.replace(ANSI2, "");
|
|
9849
10412
|
const failures = [
|
|
9850
|
-
...new Set(FAILURE_LINES.flatMap((pattern) => [...
|
|
10413
|
+
...new Set(FAILURE_LINES.flatMap((pattern) => [...text9.matchAll(pattern)].map((match) => (match[1] ?? "").trim())))
|
|
9851
10414
|
].filter((name) => name !== "" && !/^failing tests:?$/i.test(name));
|
|
9852
|
-
if (ZERO.some((pattern) => pattern.test(
|
|
9853
|
-
return { ...counted(
|
|
10415
|
+
if (ZERO.some((pattern) => pattern.test(text9))) return { total: 0, failures };
|
|
10416
|
+
return { ...counted(text9), failures };
|
|
9854
10417
|
};
|
|
9855
10418
|
|
|
9856
10419
|
// src/gate/checks/command.ts
|
|
@@ -9861,8 +10424,8 @@ var REWRITERS = /^\s*(rtk)\b/;
|
|
|
9861
10424
|
var TRIVIAL = /* @__PURE__ */ new Set(["echo", "true", ":", "printf", "exit", "sleep", "cat", "yes", "pwd", "ls", "date", "env"]);
|
|
9862
10425
|
var TAIL = 160;
|
|
9863
10426
|
var MAX_FAILURES2 = 5;
|
|
9864
|
-
var tail = (
|
|
9865
|
-
const lines3 =
|
|
10427
|
+
var tail = (text9) => {
|
|
10428
|
+
const lines3 = text9.split(/\r?\n/).filter((line2) => line2.trim() !== "");
|
|
9866
10429
|
const last = lines3[lines3.length - 1]?.trim() ?? "";
|
|
9867
10430
|
return last.length > TAIL ? `${last.slice(0, TAIL)}…` : last;
|
|
9868
10431
|
};
|
|
@@ -9982,10 +10545,10 @@ var gitCheck = (check2, ctx) => {
|
|
|
9982
10545
|
};
|
|
9983
10546
|
|
|
9984
10547
|
// src/gate/checks/look.ts
|
|
9985
|
-
import { join as
|
|
10548
|
+
import { join as join21 } from "node:path";
|
|
9986
10549
|
|
|
9987
10550
|
// src/look/config.ts
|
|
9988
|
-
import { join as
|
|
10551
|
+
import { join as join19 } from "node:path";
|
|
9989
10552
|
|
|
9990
10553
|
// src/look/types.ts
|
|
9991
10554
|
var LOOK_RULES = [
|
|
@@ -10020,7 +10583,7 @@ var DEFAULT_SCREENS = [
|
|
|
10020
10583
|
"**/*.sass",
|
|
10021
10584
|
"**/*.less"
|
|
10022
10585
|
];
|
|
10023
|
-
var
|
|
10586
|
+
var text6 = (value) => typeof value === "string" && value !== "" ? value : void 0;
|
|
10024
10587
|
var number = (value) => {
|
|
10025
10588
|
const parsed = typeof value === "string" ? Number(value) : Number.NaN;
|
|
10026
10589
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
|
|
@@ -10037,8 +10600,8 @@ var parseLookConfig = (source) => {
|
|
|
10037
10600
|
const top = configMap(tree);
|
|
10038
10601
|
return {
|
|
10039
10602
|
port: number(top["port"]),
|
|
10040
|
-
start:
|
|
10041
|
-
host:
|
|
10603
|
+
start: text6(top["start"]),
|
|
10604
|
+
host: text6(top["host"]) ?? "127.0.0.1",
|
|
10042
10605
|
routes: configList(tree, "routes").length === 0 ? ["/"] : configList(tree, "routes"),
|
|
10043
10606
|
mocks: configList(tree, "mocks"),
|
|
10044
10607
|
screens: screens === void 0 ? DEFAULT_SCREENS : configList(tree, "screens"),
|
|
@@ -10047,18 +10610,18 @@ var parseLookConfig = (source) => {
|
|
|
10047
10610
|
};
|
|
10048
10611
|
};
|
|
10049
10612
|
var loadLookConfig = (runtime, root) => {
|
|
10050
|
-
const source = runtime.readText(toAbsolutePath(
|
|
10613
|
+
const source = runtime.readText(toAbsolutePath(join19(root, LOOK_CONFIG)));
|
|
10051
10614
|
return source.ok ? parseLookConfig(source.value) : void 0;
|
|
10052
10615
|
};
|
|
10053
10616
|
var withMock = (route, mock) => `${route}${route.includes("?") ? "&" : "?"}mock=${encodeURIComponent(mock)}`;
|
|
10054
10617
|
var lookRoutes = (config) => config.routes.flatMap((route) => [route, ...config.mocks.map((mock) => withMock(route, mock))]);
|
|
10055
10618
|
|
|
10056
10619
|
// src/look/paths.ts
|
|
10057
|
-
import { join as
|
|
10620
|
+
import { join as join20, relative as relative5 } from "node:path";
|
|
10058
10621
|
var LOOK_DIR = "look";
|
|
10059
10622
|
var SUMMARY_FILE = "summary.json";
|
|
10060
|
-
var lookDir = (paths, treeHash2) => toAbsolutePath(
|
|
10061
|
-
var summaryPath = (paths, treeHash2) => toAbsolutePath(
|
|
10623
|
+
var lookDir = (paths, treeHash2) => toAbsolutePath(join20(paths.dir, LOOK_DIR, treeHash2));
|
|
10624
|
+
var summaryPath = (paths, treeHash2) => toAbsolutePath(join20(lookDir(paths, treeHash2), SUMMARY_FILE));
|
|
10062
10625
|
var projectRelative = (paths, absolute) => toPosixPath(relative5(paths.root, absolute));
|
|
10063
10626
|
|
|
10064
10627
|
// src/gate/checks/look.ts
|
|
@@ -10076,8 +10639,8 @@ var findingNote = (value) => {
|
|
|
10076
10639
|
];
|
|
10077
10640
|
};
|
|
10078
10641
|
var readSummary = (ctx) => {
|
|
10079
|
-
const
|
|
10080
|
-
const parsed =
|
|
10642
|
+
const text9 = ctx.runtime.readText(summaryPath(ctx.paths, ctx.treeHash));
|
|
10643
|
+
const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
|
|
10081
10644
|
if (!parsed?.ok) return void 0;
|
|
10082
10645
|
const { at, captures, findings, ignoredAllows } = parsed.value;
|
|
10083
10646
|
if (typeof at !== "string" || !Array.isArray(captures) || !Array.isArray(findings)) return void 0;
|
|
@@ -10093,7 +10656,7 @@ var readSummary = (ctx) => {
|
|
|
10093
10656
|
var lastScreenEdit = (ctx) => {
|
|
10094
10657
|
const screens = loadLookConfig(ctx.runtime, ctx.paths.root)?.screens ?? DEFAULT_SCREENS;
|
|
10095
10658
|
return ctx.changes.filter((change) => change.status !== "D" && matchesAny(change.path, screens)).reduce((latest, change) => {
|
|
10096
|
-
const stat = ctx.runtime.stat(toAbsolutePath(
|
|
10659
|
+
const stat = ctx.runtime.stat(toAbsolutePath(join21(ctx.paths.root, change.path)));
|
|
10097
10660
|
return Math.max(latest, stat?.mtimeMs ?? 0);
|
|
10098
10661
|
}, 0);
|
|
10099
10662
|
};
|
|
@@ -10140,14 +10703,14 @@ var manualCheck = (check2, ctx) => {
|
|
|
10140
10703
|
};
|
|
10141
10704
|
|
|
10142
10705
|
// src/gate/checks/placeholders.ts
|
|
10143
|
-
import { join as
|
|
10706
|
+
import { join as join22 } from "node:path";
|
|
10144
10707
|
var MAX_LISTED3 = 10;
|
|
10145
10708
|
var SKIP_FILES = /\.(md|mdx|txt|rst|lock|snap|svg|png|jpg|gif|json)$|(^|\/)(CHANGELOG|LICENSE)/i;
|
|
10146
10709
|
var PLACEHOLDER = /\b(TODO|FIXME|XXX)\b|not implemented|NotImplementedError|unimplemented!|todo!\(|lorem ipsum/i;
|
|
10147
10710
|
var findPlaceholders = (lines3) => lines3.filter((line2) => !isTestFile(line2.path) && !SKIP_FILES.test(line2.path) && PLACEHOLDER.test(line2.text));
|
|
10148
10711
|
var untrackedAdded = (ctx) => ctx.changes.filter((change) => change.status === "A" && !SKIP_FILES.test(change.path)).flatMap((change) => {
|
|
10149
|
-
const
|
|
10150
|
-
return
|
|
10712
|
+
const text9 = ctx.runtime.readText(toAbsolutePath(join22(ctx.paths.root, change.path)));
|
|
10713
|
+
return text9.ok ? text9.value.split("\n").map((line2, index) => ({ path: change.path, line: index + 1, text: line2 })) : [];
|
|
10151
10714
|
});
|
|
10152
10715
|
var placeholdersCheck = (_check, ctx) => {
|
|
10153
10716
|
const tracked = addedLines(ctx.git, ctx.base);
|
|
@@ -10206,9 +10769,9 @@ var prOutcome = (check2, ctx) => {
|
|
|
10206
10769
|
|
|
10207
10770
|
// src/prose/preset.ts
|
|
10208
10771
|
var PRESET_LINE = /^preset:\s*([A-Za-z0-9_-]+)\s*$/m;
|
|
10209
|
-
var presetFromConfig = (
|
|
10772
|
+
var presetFromConfig = (text9) => parsePreset(PRESET_LINE.exec(text9)?.[1]) ?? "neutral";
|
|
10210
10773
|
var projectPreset = (runtime, root) => {
|
|
10211
|
-
const config = runtime.readText(toAbsolutePath(
|
|
10774
|
+
const config = runtime.readText(toAbsolutePath(CONFIG_FILE2, root));
|
|
10212
10775
|
return config.ok ? presetFromConfig(config.value) : "neutral";
|
|
10213
10776
|
};
|
|
10214
10777
|
|
|
@@ -10383,16 +10946,16 @@ var ANY_MARKER = /<!--\s*finitemonkey:prose-allow(?:-file)?\s*-->/g;
|
|
|
10383
10946
|
var INLINE_CODE = /`[^`]*`/g;
|
|
10384
10947
|
var FENCE2 = /^\s*(```|~~~)/;
|
|
10385
10948
|
var outsideCode = (line2) => line2.replace(INLINE_CODE, "");
|
|
10386
|
-
var unfencedLines = (
|
|
10949
|
+
var unfencedLines = (text9) => {
|
|
10387
10950
|
let fenced = false;
|
|
10388
|
-
return
|
|
10951
|
+
return text9.split("\n").filter((line2) => {
|
|
10389
10952
|
if (FENCE2.test(line2)) fenced = !fenced;
|
|
10390
10953
|
return !fenced && !FENCE2.test(line2);
|
|
10391
10954
|
});
|
|
10392
10955
|
};
|
|
10393
|
-
var allowsWholeFile = (
|
|
10956
|
+
var allowsWholeFile = (text9) => unfencedLines(text9).some((line2) => FILE_MARKER.test(outsideCode(line2)));
|
|
10394
10957
|
var allowsLine = (line2) => LINE_MARKER.test(outsideCode(line2));
|
|
10395
|
-
var markerCount = (
|
|
10958
|
+
var markerCount = (text9) => text9.match(ANY_MARKER)?.length ?? 0;
|
|
10396
10959
|
var isMarkerOnlyLine = (line2) => allowsLine(line2) && outsideCode(line2).replace(LINE_MARKER, "").trim() === "";
|
|
10397
10960
|
|
|
10398
10961
|
// src/prose/extract.ts
|
|
@@ -10412,7 +10975,7 @@ var MASKS = [
|
|
|
10412
10975
|
[/<[^>]+>/g, ""],
|
|
10413
10976
|
[/\*\*|__/g, ""]
|
|
10414
10977
|
];
|
|
10415
|
-
var maskInline = (line2) => MASKS.reduce((
|
|
10978
|
+
var maskInline = (line2) => MASKS.reduce((text9, [pattern, replacement]) => text9.replace(pattern, replacement), line2);
|
|
10416
10979
|
var BlockReader = class {
|
|
10417
10980
|
blocks = [];
|
|
10418
10981
|
current;
|
|
@@ -10503,8 +11066,8 @@ var splitBlock = (block4) => {
|
|
|
10503
11066
|
return joined.split(SENTENCE_END).flatMap((sentence) => {
|
|
10504
11067
|
const at = joined.indexOf(sentence, offset);
|
|
10505
11068
|
offset = at + sentence.length;
|
|
10506
|
-
const
|
|
10507
|
-
return
|
|
11069
|
+
const text9 = sentence.trim();
|
|
11070
|
+
return text9 === "" ? [] : [{ text: text9, line: lineAt(at) }];
|
|
10508
11071
|
});
|
|
10509
11072
|
};
|
|
10510
11073
|
var unitsOf = (blocks) => {
|
|
@@ -10525,9 +11088,9 @@ var unitsOf = (blocks) => {
|
|
|
10525
11088
|
}));
|
|
10526
11089
|
});
|
|
10527
11090
|
};
|
|
10528
|
-
var extractProse = (
|
|
10529
|
-
if (allowsWholeFile(
|
|
10530
|
-
const lines3 =
|
|
11091
|
+
var extractProse = (text9) => {
|
|
11092
|
+
if (allowsWholeFile(text9)) return { units: [], allowed: true };
|
|
11093
|
+
const lines3 = text9.split("\n").map(withoutCarriageReturn);
|
|
10531
11094
|
return { units: unitsOf(readBlocks(lines3)), allowed: false };
|
|
10532
11095
|
};
|
|
10533
11096
|
|
|
@@ -10652,7 +11215,7 @@ var CONNECTIVES = new Set(
|
|
|
10652
11215
|
" "
|
|
10653
11216
|
)
|
|
10654
11217
|
);
|
|
10655
|
-
var wordsOf = (
|
|
11218
|
+
var wordsOf = (text9) => text9.split(/\s+/).filter((word2) => WORD.test(word2));
|
|
10656
11219
|
var hit = (rule, units, index, weight) => {
|
|
10657
11220
|
const unit = units[index];
|
|
10658
11221
|
return { rule, unit: index, line: unit?.line ?? 0, weight, excerpt: unit?.text ?? "" };
|
|
@@ -10780,8 +11343,8 @@ var mergeSpans = (hits, windows) => {
|
|
|
10780
11343
|
return ranges.map(([from, to]) => spanOf2(hits, from, to)).filter((span) => span.hits.length > 0);
|
|
10781
11344
|
};
|
|
10782
11345
|
var byPosition = (left, right) => left.unit - right.unit || left.line - right.line;
|
|
10783
|
-
var scoreProse = (
|
|
10784
|
-
const { units, allowed: allowed2 } = extractProse(
|
|
11346
|
+
var scoreProse = (text9, options = {}) => {
|
|
11347
|
+
const { units, allowed: allowed2 } = extractProse(text9);
|
|
10785
11348
|
const size = options.window ?? DENSITY_WINDOW;
|
|
10786
11349
|
const threshold2 = options.threshold ?? DENSITY_THRESHOLD;
|
|
10787
11350
|
const catalog = options.context === "record" ? [...phraseRules(), ...recordRules()] : phraseRules();
|
|
@@ -10813,9 +11376,9 @@ var changedMarkdown = (runtime, root) => {
|
|
|
10813
11376
|
var scoreFiles = (runtime, root, files, forced) => {
|
|
10814
11377
|
const reports = [];
|
|
10815
11378
|
for (const file of files) {
|
|
10816
|
-
const
|
|
10817
|
-
if (!
|
|
10818
|
-
reports.push({ file: toPosixPath(file), report: scoreProse(
|
|
11379
|
+
const text9 = runtime.readText(toAbsolutePath(file, root));
|
|
11380
|
+
if (!text9.ok) return reject(`${file}: ${text9.error}`);
|
|
11381
|
+
reports.push({ file: toPosixPath(file), report: scoreProse(text9.value, { context: forced ?? contextOf(file) }) });
|
|
10819
11382
|
}
|
|
10820
11383
|
return accept(reports);
|
|
10821
11384
|
};
|
|
@@ -10914,8 +11477,8 @@ var parseAppCheck = (stdout) => {
|
|
|
10914
11477
|
var PROBE_MARGIN_MS = 15e3;
|
|
10915
11478
|
var MIN_REMAINING_MS2 = 5e3;
|
|
10916
11479
|
var TAIL2 = 160;
|
|
10917
|
-
var lastLine = (
|
|
10918
|
-
const rows =
|
|
11480
|
+
var lastLine = (text9) => {
|
|
11481
|
+
const rows = text9.split(/\r?\n/).filter((row) => row.trim() !== "" && !/^\s*at\s|^Node\.js v\d/.test(row));
|
|
10919
11482
|
const line2 = (rows.findLast((row) => /error/i.test(row)) ?? rows.at(-1))?.trim() ?? "";
|
|
10920
11483
|
return line2.length > TAIL2 ? `${line2.slice(0, TAIL2)}…` : line2;
|
|
10921
11484
|
};
|
|
@@ -10993,14 +11556,14 @@ ${run4.stderr}`)) })]);
|
|
|
10993
11556
|
};
|
|
10994
11557
|
|
|
10995
11558
|
// src/secrets/baseline.ts
|
|
10996
|
-
import { join as
|
|
11559
|
+
import { join as join23 } from "node:path";
|
|
10997
11560
|
var BASELINE_FILE2 = ".finitemonkey/findings/baseline.json";
|
|
10998
11561
|
var BASELINE_SCHEMA = "finitemonkey/secrets-baseline@1";
|
|
10999
11562
|
var BASELINE_STATES = ["aceito", "falso-positivo"];
|
|
11000
11563
|
var EXCEPTION_MAX_DAYS = 90;
|
|
11001
11564
|
var EMPTY_BASELINE = { schema: BASELINE_SCHEMA, entries: [] };
|
|
11002
|
-
var baselinePath = (root) => toAbsolutePath(
|
|
11003
|
-
var
|
|
11565
|
+
var baselinePath = (root) => toAbsolutePath(join23(root, BASELINE_FILE2));
|
|
11566
|
+
var text7 = (record3, key) => {
|
|
11004
11567
|
const value = record3[key];
|
|
11005
11568
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
11006
11569
|
};
|
|
@@ -11008,7 +11571,7 @@ var stateOf = (value) => BASELINE_STATES.find((state) => state === value);
|
|
|
11008
11571
|
var parseEntry2 = (value) => {
|
|
11009
11572
|
if (!isRecord(value)) return void 0;
|
|
11010
11573
|
const fields = ["id", "rule", "file", "fingerprint", "owner", "reason", "acceptedAt", "expiresAt"];
|
|
11011
|
-
const [id, rule, file, fingerprint, owner, reason, acceptedAt, expiresAt] = fields.map((key) =>
|
|
11574
|
+
const [id, rule, file, fingerprint, owner, reason, acceptedAt, expiresAt] = fields.map((key) => text7(value, key));
|
|
11012
11575
|
const state = stateOf(value["state"]);
|
|
11013
11576
|
const line2 = value["line"];
|
|
11014
11577
|
if (id === void 0 || rule === void 0 || file === void 0 || fingerprint === void 0) return void 0;
|
|
@@ -11059,14 +11622,14 @@ var withoutFixed = (baseline, fixed) => {
|
|
|
11059
11622
|
var acceptedFingerprints = (baseline, now) => new Set(baseline.entries.filter((entry2) => entry2.expiresAt >= now).map((entry2) => entry2.fingerprint));
|
|
11060
11623
|
|
|
11061
11624
|
// src/secrets/incidents.ts
|
|
11062
|
-
import { join as
|
|
11625
|
+
import { join as join24 } from "node:path";
|
|
11063
11626
|
var INCIDENTS_DIR = ".finitemonkey/incidents";
|
|
11064
11627
|
var INCIDENT_STATES = ["aberto", "exposicao-local", "rotacionado", "fechado"];
|
|
11065
11628
|
var INCIDENT_SOURCES = ["tool-output", "prompt", "history", "scan"];
|
|
11066
11629
|
var ID = /^inc-[0-9a-f]{12}$/;
|
|
11067
11630
|
var incidentId = (rule, fingerprint) => `inc-${sha256Hex(`${rule}\0${fingerprint}`).slice(0, 12)}`;
|
|
11068
|
-
var incidentsDir = (root) => toAbsolutePath(
|
|
11069
|
-
var incidentFile = (root, id) => toAbsolutePath(
|
|
11631
|
+
var incidentsDir = (root) => toAbsolutePath(join24(root, INCIDENTS_DIR));
|
|
11632
|
+
var incidentFile = (root, id) => toAbsolutePath(join24(incidentsDir(root), `${id}.json`));
|
|
11070
11633
|
var pick3 = (options, value) => options.find((option) => option === value);
|
|
11071
11634
|
var str2 = (record3, key) => {
|
|
11072
11635
|
const value = record3[key];
|
|
@@ -11121,7 +11684,7 @@ var listIncidents = (runtime, root) => {
|
|
|
11121
11684
|
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
11685
|
};
|
|
11123
11686
|
var saveIncident = (runtime, root, incident) => {
|
|
11124
|
-
const ignore = runtime.writeText(toAbsolutePath(
|
|
11687
|
+
const ignore = runtime.writeText(toAbsolutePath(join24(incidentsDir(root), ".gitignore")), "*\n");
|
|
11125
11688
|
if (!ignore.ok) return ignore;
|
|
11126
11689
|
return runtime.writeText(incidentFile(root, incident.id), `${JSON.stringify(incident, null, 2)}
|
|
11127
11690
|
`);
|
|
@@ -11168,7 +11731,7 @@ var moveIncident = (incident, to, now, by) => ({
|
|
|
11168
11731
|
var isRotated = (incident) => incident.state === "rotacionado" || incident.state === "fechado";
|
|
11169
11732
|
|
|
11170
11733
|
// src/secrets/scan.ts
|
|
11171
|
-
import { join as
|
|
11734
|
+
import { join as join25, relative as relative6 } from "node:path";
|
|
11172
11735
|
|
|
11173
11736
|
// src/secrets/located.ts
|
|
11174
11737
|
var findingId = (ruleId, file, fingerprint) => `fm-${sha256Hex(`${ruleId}\0${file}\0${fingerprint}`).slice(0, 16)}`;
|
|
@@ -11258,7 +11821,7 @@ var diffScanner = () => {
|
|
|
11258
11821
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
11259
11822
|
var SKIPPED = /(?:^|\/)(?:\.git|node_modules|\.finitemonkey)\//;
|
|
11260
11823
|
var DIFF_FLAGS = ["-U0", "--no-color", "--no-ext-diff", "--no-renames"];
|
|
11261
|
-
var binary = (
|
|
11824
|
+
var binary = (text9) => text9.slice(0, 8e3).includes("\0");
|
|
11262
11825
|
var listed2 = (runtime, root, target2) => {
|
|
11263
11826
|
const git2 = runtime.git(["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", target2], root);
|
|
11264
11827
|
if (git2.ok)
|
|
@@ -11268,12 +11831,12 @@ var listed2 = (runtime, root, target2) => {
|
|
|
11268
11831
|
const base = toAbsolutePath(target2, root);
|
|
11269
11832
|
const walked = runtime.walkFiles(base);
|
|
11270
11833
|
if (!walked.ok) return walked;
|
|
11271
|
-
return accept(walked.value.map((name) => toPosixPath(relative6(root,
|
|
11834
|
+
return accept(walked.value.map((name) => toPosixPath(relative6(root, join25(base, toPosixPath(name))))));
|
|
11272
11835
|
};
|
|
11273
11836
|
var scanFile = (runtime, root, file) => {
|
|
11274
|
-
const
|
|
11275
|
-
if (!
|
|
11276
|
-
return scanText(
|
|
11837
|
+
const text9 = runtime.readText(toAbsolutePath(file, root));
|
|
11838
|
+
if (!text9.ok || text9.value.length > MAX_FILE_BYTES || binary(text9.value)) return [];
|
|
11839
|
+
return scanText(text9.value, { path: file }).map((finding4) => locate(finding4, file, finding4.line));
|
|
11277
11840
|
};
|
|
11278
11841
|
var scanPath = (runtime, root, target2) => {
|
|
11279
11842
|
const files = listed2(runtime, root, target2);
|
|
@@ -11338,7 +11901,7 @@ var secretsCheck = (_check, ctx) => {
|
|
|
11338
11901
|
};
|
|
11339
11902
|
|
|
11340
11903
|
// src/gate/checks/tests.ts
|
|
11341
|
-
import { join as
|
|
11904
|
+
import { join as join26 } from "node:path";
|
|
11342
11905
|
|
|
11343
11906
|
// src/guards/tests/tree.ts
|
|
11344
11907
|
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 +11933,8 @@ var treeViolations = (ctx) => {
|
|
|
11370
11933
|
changes: relevant,
|
|
11371
11934
|
before: (path) => fileAt(ctx.git, ctx.base, path),
|
|
11372
11935
|
after: (path) => {
|
|
11373
|
-
const
|
|
11374
|
-
return
|
|
11936
|
+
const text9 = ctx.runtime.readText(toAbsolutePath(join26(ctx.paths.root, path)));
|
|
11937
|
+
return text9.ok ? text9.value : void 0;
|
|
11375
11938
|
},
|
|
11376
11939
|
acceptedConfigs: ctx.trust.configs,
|
|
11377
11940
|
scopePaths: ctx.contract.scope.paths,
|
|
@@ -11391,7 +11954,7 @@ var testsCheck = (check2, ctx) => {
|
|
|
11391
11954
|
};
|
|
11392
11955
|
|
|
11393
11956
|
// src/git/tree.ts
|
|
11394
|
-
import { join as
|
|
11957
|
+
import { join as join27 } from "node:path";
|
|
11395
11958
|
|
|
11396
11959
|
// src/secrets/guards/paths.ts
|
|
11397
11960
|
var SAFE_ENV = /^\.env\.(?:example|sample|template|dist|defaults|schema)$/i;
|
|
@@ -11430,7 +11993,7 @@ var dropSensitive = (runtime, repo, env, options) => {
|
|
|
11430
11993
|
return removed.ok ? accept(void 0) : removed;
|
|
11431
11994
|
};
|
|
11432
11995
|
var withTempIndex = (runtime, repo, options, work) => {
|
|
11433
|
-
const path = toAbsolutePath(
|
|
11996
|
+
const path = toAbsolutePath(join27(repo.gitDir, `finitemonkey-${options.stamp}.index`));
|
|
11434
11997
|
const env = { GIT_INDEX_FILE: path, GIT_OPTIONAL_LOCKS: "0" };
|
|
11435
11998
|
try {
|
|
11436
11999
|
return work(env);
|
|
@@ -11535,10 +12098,10 @@ var bodyLine = (draft, line2) => {
|
|
|
11535
12098
|
else if (line2.startsWith("+")) draft.current.added.push(line2.slice(1));
|
|
11536
12099
|
};
|
|
11537
12100
|
var DIFF_GIT = /^diff --git a\/(.+) b\/(.+)$/;
|
|
11538
|
-
var parseUnifiedDiff = (
|
|
12101
|
+
var parseUnifiedDiff = (text9) => {
|
|
11539
12102
|
const out = [];
|
|
11540
12103
|
let draft;
|
|
11541
|
-
for (const raw of
|
|
12104
|
+
for (const raw of text9.split("\n")) {
|
|
11542
12105
|
const line2 = withoutCarriageReturn(raw);
|
|
11543
12106
|
const start = DIFF_GIT.exec(line2);
|
|
11544
12107
|
if (start !== null) {
|
|
@@ -11637,59 +12200,15 @@ var mergeCommitsSince = (runtime, repo, since) => {
|
|
|
11637
12200
|
};
|
|
11638
12201
|
var mergeInProgress = (runtime, repo) => revParse(runtime, repo, "MERGE_HEAD") !== void 0;
|
|
11639
12202
|
|
|
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
12203
|
// src/git/merge/dropped.ts
|
|
11685
|
-
import { join as
|
|
12204
|
+
import { join as join28 } from "node:path";
|
|
11686
12205
|
|
|
11687
12206
|
// src/git/merge/lines.ts
|
|
11688
12207
|
var normalizeLine = (line2) => line2.replace(/\r$/, "").trim().replace(/\s*,$/, "");
|
|
11689
12208
|
var WEAK = /^[\p{P}\p{S}\s]+$/u;
|
|
11690
12209
|
var isWeak = (line2) => WEAK.test(line2);
|
|
11691
12210
|
var meaningful = (lines3) => lines3.map(normalizeLine).filter((line2) => line2 !== "");
|
|
11692
|
-
var contentLines = (
|
|
12211
|
+
var contentLines = (text9) => text9 === void 0 ? [] : meaningful(text9.split("\n"));
|
|
11693
12212
|
var countLines = (lines3) => {
|
|
11694
12213
|
const counts = /* @__PURE__ */ new Map();
|
|
11695
12214
|
for (const line2 of lines3) counts.set(line2, (counts.get(line2) ?? 0) + 1);
|
|
@@ -11738,16 +12257,16 @@ var itemOf = (value) => {
|
|
|
11738
12257
|
approvedBy: optionalString(value, "approved_by")
|
|
11739
12258
|
};
|
|
11740
12259
|
};
|
|
11741
|
-
var invalidFinding = (file,
|
|
12260
|
+
var invalidFinding = (file, text9) => ({
|
|
11742
12261
|
kind: "invalid-dropped",
|
|
11743
12262
|
file,
|
|
11744
12263
|
side: void 0,
|
|
11745
12264
|
line: void 0,
|
|
11746
|
-
text:
|
|
12265
|
+
text: text9,
|
|
11747
12266
|
weak: false
|
|
11748
12267
|
});
|
|
11749
|
-
var parseDropped = (
|
|
11750
|
-
const parsed = parseJson(
|
|
12268
|
+
var parseDropped = (text9) => {
|
|
12269
|
+
const parsed = parseJson(text9);
|
|
11751
12270
|
if (!parsed.ok) return parsed;
|
|
11752
12271
|
const list4 = isRecord(parsed.value) ? parsed.value["dropped"] : parsed.value;
|
|
11753
12272
|
if (!Array.isArray(list4)) return reject("expected a list of dropped items");
|
|
@@ -11762,17 +12281,17 @@ var parseDropped = (text8) => {
|
|
|
11762
12281
|
return accept({ items, invalid });
|
|
11763
12282
|
};
|
|
11764
12283
|
var loadDropped = (runtime, root, file) => {
|
|
11765
|
-
const path = toAbsolutePath(file ??
|
|
11766
|
-
const
|
|
11767
|
-
if (!
|
|
11768
|
-
return parseDropped(
|
|
12284
|
+
const path = toAbsolutePath(file ?? join28(root, DROPPED_FILE), root);
|
|
12285
|
+
const text9 = runtime.readText(path);
|
|
12286
|
+
if (!text9.ok) return file === void 0 ? accept({ items: [], invalid: [] }) : reject(`${file}: ${text9.error}`);
|
|
12287
|
+
return parseDropped(text9.value);
|
|
11769
12288
|
};
|
|
11770
12289
|
var isDropped = (finding4, items) => items.some(
|
|
11771
12290
|
(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
12291
|
);
|
|
11773
12292
|
|
|
11774
12293
|
// src/git/merge/generated.ts
|
|
11775
|
-
import { join as
|
|
12294
|
+
import { join as join29 } from "node:path";
|
|
11776
12295
|
var LOCKFILES3 = /* @__PURE__ */ new Set([
|
|
11777
12296
|
"package-lock.json",
|
|
11778
12297
|
"npm-shrinkwrap.json",
|
|
@@ -11790,11 +12309,11 @@ var LOCKFILES3 = /* @__PURE__ */ new Set([
|
|
|
11790
12309
|
]);
|
|
11791
12310
|
var GENERATOR_TIMEOUT_MS = 3e5;
|
|
11792
12311
|
var DOUBLE_STAR = "";
|
|
11793
|
-
var
|
|
12312
|
+
var globRegex2 = (pattern) => new RegExp(
|
|
11794
12313
|
`^${pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").split("**").join(DOUBLE_STAR).replace(/\*/g, "[^/]*").split(DOUBLE_STAR).join(".*")}$`
|
|
11795
12314
|
);
|
|
11796
12315
|
var generatorFor = (path, generators) => Object.entries(generators).find(
|
|
11797
|
-
([pattern]) =>
|
|
12316
|
+
([pattern]) => globRegex2(pattern).test(path) || globRegex2(pattern).test(path.split("/").pop() ?? "")
|
|
11798
12317
|
)?.[1];
|
|
11799
12318
|
var generatedPaths = (runtime, repo, paths, generators) => {
|
|
11800
12319
|
const out = new Set(
|
|
@@ -11835,7 +12354,7 @@ var verifyGenerated = (runtime, repo, inputs, paths, generators) => {
|
|
|
11835
12354
|
if (declared.length === 0) return accept([]);
|
|
11836
12355
|
const commit2 = resultCommit(runtime, repo, inputs);
|
|
11837
12356
|
if (!commit2.ok) return commit2;
|
|
11838
|
-
const dir =
|
|
12357
|
+
const dir = join29(repo.gitDir, `finitemonkey-generate-${runtime.now().replace(/[:.]/g, "-")}`);
|
|
11839
12358
|
if (!runtime.git(["worktree", "add", "--detach", dir, commit2.value], repo.root).ok)
|
|
11840
12359
|
return reject("could not check out the merge result");
|
|
11841
12360
|
try {
|
|
@@ -11845,7 +12364,7 @@ var verifyGenerated = (runtime, repo, inputs, paths, generators) => {
|
|
|
11845
12364
|
}
|
|
11846
12365
|
return accept(
|
|
11847
12366
|
declared.flatMap(({ path }) => {
|
|
11848
|
-
const fresh3 = runtime.readText(toAbsolutePath(
|
|
12367
|
+
const fresh3 = runtime.readText(toAbsolutePath(join29(dir, path)));
|
|
11849
12368
|
const merged2 = blobText(runtime, repo, commit2.value, path);
|
|
11850
12369
|
if (fresh3.ok && fresh3.value === merged2) return [];
|
|
11851
12370
|
return [{ kind: "generated-differs", file: path, side: void 0, line: void 0, text: path, weak: false }];
|
|
@@ -11888,13 +12407,13 @@ var removalOf = (ours, theirs, paired) => {
|
|
|
11888
12407
|
var missingAdditions = (hunks, file, elsewhere) => {
|
|
11889
12408
|
const present2 = new Set(file.result);
|
|
11890
12409
|
return hunks.flatMap(({ side, hunk, added }) => {
|
|
11891
|
-
const
|
|
11892
|
-
if (
|
|
12410
|
+
const text9 = added.find((line2) => !isWeak(line2) && !present2.has(line2));
|
|
12411
|
+
if (text9 === void 0 || containsSequence(file.result, added) || elsewhere(added)) return [];
|
|
11893
12412
|
const offset = Math.max(
|
|
11894
12413
|
0,
|
|
11895
|
-
hunk.added.findIndex((line2) => normalizeLine(line2) ===
|
|
12414
|
+
hunk.added.findIndex((line2) => normalizeLine(line2) === text9)
|
|
11896
12415
|
);
|
|
11897
|
-
return [{ kind: "missing-addition", file: file.path, side, line: hunk.newStart + offset, text:
|
|
12416
|
+
return [{ kind: "missing-addition", file: file.path, side, line: hunk.newStart + offset, text: text9, weak: false }];
|
|
11898
12417
|
});
|
|
11899
12418
|
};
|
|
11900
12419
|
var expectations = (file, hunks, removal) => {
|
|
@@ -11967,8 +12486,8 @@ var symbolsOf = (line2) => {
|
|
|
11967
12486
|
...title === void 0 ? [] : [{ name: title, test: true }]
|
|
11968
12487
|
];
|
|
11969
12488
|
};
|
|
11970
|
-
var escapeRegex = (
|
|
11971
|
-
var containsSymbol = (
|
|
12489
|
+
var escapeRegex = (text9) => text9.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
12490
|
+
var containsSymbol = (text9, symbol) => symbol.test ? text9.includes(symbol.name) : new RegExp(`(?<![\\w$])${escapeRegex(symbol.name)}(?![\\w$])`).test(text9);
|
|
11972
12491
|
var missingSymbols = (file, side, hunks, present2) => {
|
|
11973
12492
|
const seen = /* @__PURE__ */ new Set();
|
|
11974
12493
|
return hunks.flatMap(
|
|
@@ -11987,12 +12506,12 @@ var created2 = (diff) => diff?.status === "added" || diff?.status === "copied";
|
|
|
11987
12506
|
var resultPathOf = (context, pair) => [pair.ours?.newPath, pair.theirs?.newPath, pair.key].find(
|
|
11988
12507
|
(path) => path !== void 0 && context.resultText(path) !== void 0
|
|
11989
12508
|
);
|
|
11990
|
-
var finding2 = (kind, file, side,
|
|
12509
|
+
var finding2 = (kind, file, side, text9) => ({
|
|
11991
12510
|
kind,
|
|
11992
12511
|
file,
|
|
11993
12512
|
side,
|
|
11994
12513
|
line: void 0,
|
|
11995
|
-
text:
|
|
12514
|
+
text: text9,
|
|
11996
12515
|
weak: false
|
|
11997
12516
|
});
|
|
11998
12517
|
var binaryFindings = (context, pair, path) => {
|
|
@@ -12011,8 +12530,8 @@ var binaryFindings = (context, pair, path) => {
|
|
|
12011
12530
|
return result === expected ? [] : [finding2("binary", file, side, file)];
|
|
12012
12531
|
};
|
|
12013
12532
|
var elsewhereOf = (context) => (lines3) => context.resultFiles().some((path) => containsSequence(contentLines(context.resultText(path)), lines3));
|
|
12014
|
-
var symbolFindings = (context, pair, path,
|
|
12015
|
-
const present2 = (symbol) =>
|
|
12533
|
+
var symbolFindings = (context, pair, path, text9) => {
|
|
12534
|
+
const present2 = (symbol) => text9 !== void 0 && containsSymbol(text9, symbol) || context.resultFiles().some((other) => containsSymbol(context.resultText(other) ?? "", symbol));
|
|
12016
12535
|
return [
|
|
12017
12536
|
...missingSymbols(path, "ours", pair.ours?.hunks ?? [], present2),
|
|
12018
12537
|
...missingSymbols(path, "theirs", pair.theirs?.hunks ?? [], present2)
|
|
@@ -12028,19 +12547,19 @@ var verifyFile = (context, pair) => {
|
|
|
12028
12547
|
const copied = [pair.ours, pair.theirs].find((diff) => diff?.status === "copied");
|
|
12029
12548
|
const baseSource = copied?.oldPath ?? (created2(pair.ours) || created2(pair.theirs) ? void 0 : pair.key);
|
|
12030
12549
|
const baseText = baseSource === void 0 ? void 0 : blobText(context.runtime, context.repo, context.inputs.base, baseSource);
|
|
12031
|
-
const
|
|
12550
|
+
const text9 = path === void 0 ? void 0 : context.resultText(path);
|
|
12032
12551
|
const file = path ?? pair.key;
|
|
12033
12552
|
const lines3 = checkFile(
|
|
12034
12553
|
{
|
|
12035
12554
|
path: file,
|
|
12036
12555
|
base: contentLines(baseText),
|
|
12037
|
-
result: contentLines(
|
|
12556
|
+
result: contentLines(text9),
|
|
12038
12557
|
ours: pair.ours?.hunks ?? [],
|
|
12039
12558
|
theirs: pair.theirs?.hunks ?? []
|
|
12040
12559
|
},
|
|
12041
12560
|
elsewhereOf(context)
|
|
12042
12561
|
);
|
|
12043
|
-
return [...lines3, ...symbolFindings(context, pair, file,
|
|
12562
|
+
return [...lines3, ...symbolFindings(context, pair, file, text9)];
|
|
12044
12563
|
};
|
|
12045
12564
|
|
|
12046
12565
|
// src/git/merge/verify.ts
|
|
@@ -12069,13 +12588,13 @@ var pairsOf = (ours, theirs) => {
|
|
|
12069
12588
|
}));
|
|
12070
12589
|
};
|
|
12071
12590
|
var contextOf2 = (runtime, repo, inputs, pairs2) => {
|
|
12072
|
-
const
|
|
12591
|
+
const resultText2 = memoBy((path) => blobText(runtime, repo, inputs.result, path));
|
|
12073
12592
|
const context = {
|
|
12074
12593
|
runtime,
|
|
12075
12594
|
repo,
|
|
12076
12595
|
inputs,
|
|
12077
|
-
resultText,
|
|
12078
|
-
resultFiles:
|
|
12596
|
+
resultText: resultText2,
|
|
12597
|
+
resultFiles: memo(
|
|
12079
12598
|
() => pairs2.flatMap((pair) => {
|
|
12080
12599
|
const path = resultPathOf(context, pair);
|
|
12081
12600
|
return path === void 0 ? [] : [path];
|
|
@@ -12131,19 +12650,19 @@ var verifyMerge = (runtime, repo, options = {}) => {
|
|
|
12131
12650
|
};
|
|
12132
12651
|
|
|
12133
12652
|
// src/git/ownership.ts
|
|
12134
|
-
import { join as
|
|
12653
|
+
import { join as join30 } from "node:path";
|
|
12135
12654
|
var OWNERSHIP_DIR = ".finitemonkey/git";
|
|
12136
12655
|
var ownerKey = (raw) => {
|
|
12137
12656
|
const cleaned = (raw ?? "").replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 80);
|
|
12138
12657
|
return cleaned === "" ? "unknown" : cleaned;
|
|
12139
12658
|
};
|
|
12140
|
-
var fileOf = (repo, kind, owner) => toAbsolutePath(
|
|
12659
|
+
var fileOf = (repo, kind, owner) => toAbsolutePath(join30(repo.root, OWNERSHIP_DIR, kind, `${ownerKey(owner)}.json`));
|
|
12141
12660
|
var readDirty = (runtime, root) => {
|
|
12142
12661
|
const status = runtime.git(["status", "--porcelain=v2", "-z", "--untracked-files=all"], root);
|
|
12143
12662
|
return status.ok ? parsePorcelainV2(status.stdout) : void 0;
|
|
12144
12663
|
};
|
|
12145
12664
|
var hashDirty = (runtime, repo, entries) => {
|
|
12146
|
-
const present2 = entries.filter((entry2) => runtime.stat(toAbsolutePath(
|
|
12665
|
+
const present2 = entries.filter((entry2) => runtime.stat(toAbsolutePath(join30(repo.root, entry2.path)))?.kind === "file");
|
|
12147
12666
|
const hashed = present2.length === 0 ? void 0 : runtime.gitWith(["hash-object", "--stdin-paths"], repo.root, {
|
|
12148
12667
|
input: `${present2.map((entry2) => entry2.path).join("\n")}
|
|
12149
12668
|
`
|
|
@@ -12160,8 +12679,8 @@ var hashesOf = (value) => isRecord(value) ? Object.fromEntries(
|
|
|
12160
12679
|
Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
|
|
12161
12680
|
) : {};
|
|
12162
12681
|
var readObject = (runtime, path) => {
|
|
12163
|
-
const
|
|
12164
|
-
return
|
|
12682
|
+
const text9 = runtime.readText(path);
|
|
12683
|
+
return text9.ok ? parseJsonObject(text9.value) : text9;
|
|
12165
12684
|
};
|
|
12166
12685
|
var readBaseline2 = (runtime, repo, owner) => {
|
|
12167
12686
|
const parsed = readObject(runtime, fileOf(repo, "baseline", owner));
|
|
@@ -12227,7 +12746,7 @@ var foreignFiles = (runtime, repo, owner, targets2) => {
|
|
|
12227
12746
|
};
|
|
12228
12747
|
|
|
12229
12748
|
// src/git/gate-status.ts
|
|
12230
|
-
var lines = (
|
|
12749
|
+
var lines = (text9) => text9.split("\n").filter((line2) => line2.trim() !== "");
|
|
12231
12750
|
var unpushedSince = (runtime, repo, since) => {
|
|
12232
12751
|
if (lines(runtime.git(["remote"], repo.root).stdout).length === 0) return [];
|
|
12233
12752
|
const result = runtime.git(["rev-list", `${since}..HEAD`, "--not", "--remotes"], repo.root);
|
|
@@ -12255,97 +12774,17 @@ var findingLabel = (finding4) => {
|
|
|
12255
12774
|
return `${where} ${finding4.text === "" ? finding4.kind : `"${finding4.text}"`}`;
|
|
12256
12775
|
};
|
|
12257
12776
|
|
|
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
12777
|
// src/gate/checks/worktree.ts
|
|
12339
12778
|
var MAX_LISTED7 = 8;
|
|
12340
12779
|
var listed3 = (items) => {
|
|
12341
12780
|
const head = items.slice(0, MAX_LISTED7).join(", ");
|
|
12342
12781
|
return items.length > MAX_LISTED7 ? `${head} (+${String(items.length - MAX_LISTED7)})` : head;
|
|
12343
12782
|
};
|
|
12344
|
-
var
|
|
12783
|
+
var repoOf2 = (ctx) => openRepo(ctx.runtime, ctx.paths.root);
|
|
12345
12784
|
var ownerOf = (ctx) => ownerKey(ctx.sessionId);
|
|
12346
12785
|
var declaresDestination = (ctx) => ctx.contract.acceptance.some((check2) => check2.kind === "git" && (check2.require ?? []).includes("pushed"));
|
|
12347
12786
|
var worktreeCheck = (_check, ctx) => {
|
|
12348
|
-
const repo =
|
|
12787
|
+
const repo = repoOf2(ctx);
|
|
12349
12788
|
if (repo === void 0) return unverified("tool_missing", [note("gate.worktree.unavailable")]);
|
|
12350
12789
|
const status = worktreeGateStatus(ctx.runtime, repo, ownerOf(ctx));
|
|
12351
12790
|
const unpushed = declaresDestination(ctx) ? [] : status.unpushed;
|
|
@@ -12356,7 +12795,7 @@ var worktreeCheck = (_check, ctx) => {
|
|
|
12356
12795
|
return notes2.length > 0 ? failed(notes2) : passed([note("gate.worktree.clean")]);
|
|
12357
12796
|
};
|
|
12358
12797
|
var mergeCheck = (_check, ctx) => {
|
|
12359
|
-
const repo =
|
|
12798
|
+
const repo = repoOf2(ctx);
|
|
12360
12799
|
if (repo === void 0) return unverified("tool_missing", [note("gate.merge.unavailable")]);
|
|
12361
12800
|
const items = mergeGateStatus(ctx.runtime, repo, ownerOf(ctx));
|
|
12362
12801
|
const short3 = (commit2) => commit2 === "MERGE_HEAD" ? commit2 : commit2.slice(0, 12);
|
|
@@ -12467,15 +12906,15 @@ var runGate = (options, registry = DEFAULT_CHECKS) => {
|
|
|
12467
12906
|
if (ready.kind !== "ready") return ready;
|
|
12468
12907
|
const { prepared } = ready;
|
|
12469
12908
|
const path = prepared.ctx.paths.evidence(prepared.ctx.contract.id);
|
|
12470
|
-
const before =
|
|
12909
|
+
const before = readLedger2(options.runtime, path);
|
|
12471
12910
|
if (options.fast && fastPathHit(prepared, before)) return { kind: "done", report: report(prepared, before, true) };
|
|
12472
12911
|
const fresh3 = runChecks(prepared.ctx, before, registry);
|
|
12473
12912
|
if (!appendEvidence(options.runtime, path, fresh3)) return { kind: "failed", failure: { kind: "ledger_unwritable" } };
|
|
12474
|
-
const withChecks =
|
|
12913
|
+
const withChecks = readLedger2(options.runtime, path);
|
|
12475
12914
|
if (!appendEvidence(options.runtime, path, [summaryEntry(prepared, withChecks)])) {
|
|
12476
12915
|
return { kind: "failed", failure: { kind: "ledger_unwritable" } };
|
|
12477
12916
|
}
|
|
12478
|
-
return { kind: "done", report: report(prepared,
|
|
12917
|
+
return { kind: "done", report: report(prepared, readLedger2(options.runtime, path), false) };
|
|
12479
12918
|
};
|
|
12480
12919
|
|
|
12481
12920
|
// src/gate/hooks/stop.ts
|
|
@@ -12593,7 +13032,7 @@ var subagentStopGate = (input) => {
|
|
|
12593
13032
|
};
|
|
12594
13033
|
|
|
12595
13034
|
// src/gate/hooks/subagent.ts
|
|
12596
|
-
import { join as
|
|
13035
|
+
import { join as join31 } from "node:path";
|
|
12597
13036
|
var NO_CONTRACT_AGENTS = /(^|:)(reviewer|verifier|explore|plan|claude-code-guide|statusline-setup)$/i;
|
|
12598
13037
|
var KNOWN_KINDS = /* @__PURE__ */ new Set([
|
|
12599
13038
|
"command",
|
|
@@ -12631,12 +13070,12 @@ var subagentStart = (input) => {
|
|
|
12631
13070
|
const active = readState(runtime, paths).active;
|
|
12632
13071
|
if (active === void 0) return { kind: "allow" };
|
|
12633
13072
|
const disk = (relative11) => {
|
|
12634
|
-
const
|
|
12635
|
-
return
|
|
13073
|
+
const text9 = runtime.readText(toAbsolutePath(join31(paths.root, relative11)));
|
|
13074
|
+
return text9.ok ? text9.value : void 0;
|
|
12636
13075
|
};
|
|
12637
13076
|
const loaded = loadTask(runtime, paths, { disk, base: () => void 0 }, active);
|
|
12638
13077
|
if (!loaded.ok) return { kind: "allow" };
|
|
12639
|
-
const briefing = runtime.stat(toAbsolutePath(
|
|
13078
|
+
const briefing = runtime.stat(toAbsolutePath(join31(paths.dir, "briefing.md"))) !== void 0;
|
|
12640
13079
|
return { kind: "allow", output: contextOutput("SubagentStart", contractFacts(t, loaded.value.contract, briefing)) };
|
|
12641
13080
|
};
|
|
12642
13081
|
|
|
@@ -12654,12 +13093,12 @@ var PORTAO_HANDLERS = {
|
|
|
12654
13093
|
};
|
|
12655
13094
|
|
|
12656
13095
|
// src/prod/guard.ts
|
|
12657
|
-
import { join as
|
|
13096
|
+
import { join as join33 } from "node:path";
|
|
12658
13097
|
|
|
12659
13098
|
// src/git/command-text.ts
|
|
12660
13099
|
var MAX_SHOWN = 160;
|
|
12661
|
-
var shown = (
|
|
12662
|
-
const flat =
|
|
13100
|
+
var shown = (text9) => {
|
|
13101
|
+
const flat = text9.replace(/\s+/g, " ").trim();
|
|
12663
13102
|
return flat.length > MAX_SHOWN ? `${flat.slice(0, MAX_SHOWN - 3)}...` : flat;
|
|
12664
13103
|
};
|
|
12665
13104
|
var commandText = (command) => shown([command.program, ...command.args].join(" "));
|
|
@@ -12996,7 +13435,7 @@ var remoteHostsOf = (command) => {
|
|
|
12996
13435
|
};
|
|
12997
13436
|
|
|
12998
13437
|
// src/prod/targets.ts
|
|
12999
|
-
import { join as
|
|
13438
|
+
import { join as join32 } from "node:path";
|
|
13000
13439
|
var DEPLOY_FILES = [
|
|
13001
13440
|
"deploy.sh",
|
|
13002
13441
|
"scripts/deploy.sh",
|
|
@@ -13009,9 +13448,9 @@ var SSH_URL = /ssh:\/\/(?:[^@\s/]+@)?([A-Za-z0-9.-]+)/g;
|
|
|
13009
13448
|
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
13449
|
var hostKey = (raw) => raw.trim().toLowerCase().replace(/^[^@]*@/, "").replace(/^\[([^\]]+)\].*$/, "$1").replace(/:\d+$/, "").replace(/\.$/, "");
|
|
13011
13450
|
var shellLine = (line2) => line2.replace(/^\s*(?:-\s*)?(?:run:\s*\|?)?/, "");
|
|
13012
|
-
var hostsInText = (
|
|
13013
|
-
const urls = [...
|
|
13014
|
-
const lines3 =
|
|
13451
|
+
var hostsInText = (text9) => {
|
|
13452
|
+
const urls = [...text9.matchAll(SSH_URL)].map((match) => match[1] ?? "");
|
|
13453
|
+
const lines3 = text9.split(/\r?\n/);
|
|
13015
13454
|
const keyed = lines3.flatMap((line2) => {
|
|
13016
13455
|
const match = HOST_KEY.exec(line2);
|
|
13017
13456
|
return match?.[1] === void 0 ? [] : [match[1]];
|
|
@@ -13020,13 +13459,13 @@ var hostsInText = (text8) => {
|
|
|
13020
13459
|
return [...urls, ...keyed, ...commands].filter((host) => host !== "" && !host.includes("$"));
|
|
13021
13460
|
};
|
|
13022
13461
|
var workflowFiles = (runtime, root) => {
|
|
13023
|
-
const listed4 = runtime.listDir(toAbsolutePath(
|
|
13462
|
+
const listed4 = runtime.listDir(toAbsolutePath(join32(root, ".github", "workflows")));
|
|
13024
13463
|
if (!listed4.ok) return [];
|
|
13025
13464
|
return listed4.value.filter((name) => /deploy/i.test(name) && /\.ya?ml$/i.test(name)).map((name) => `.github/workflows/${name}`);
|
|
13026
13465
|
};
|
|
13027
13466
|
var detectedHosts = (runtime, root, files) => files.flatMap((file) => {
|
|
13028
|
-
const
|
|
13029
|
-
return
|
|
13467
|
+
const text9 = runtime.readText(toAbsolutePath(join32(root, file)));
|
|
13468
|
+
return text9.ok ? hostsInText(text9.value) : [];
|
|
13030
13469
|
});
|
|
13031
13470
|
var loadProdTargets = (runtime, root, config) => {
|
|
13032
13471
|
const workflows = workflowFiles(runtime, root);
|
|
@@ -13037,7 +13476,7 @@ var loadProdTargets = (runtime, root, config) => {
|
|
|
13037
13476
|
...detectedHosts(runtime, root, deployFiles)
|
|
13038
13477
|
];
|
|
13039
13478
|
const declaredDeploy = configAt(config, "prod", "deploy");
|
|
13040
|
-
const existing = deployFiles.find((file) => runtime.stat(toAbsolutePath(
|
|
13479
|
+
const existing = deployFiles.find((file) => runtime.stat(toAbsolutePath(join32(root, file))) !== void 0);
|
|
13041
13480
|
return {
|
|
13042
13481
|
hosts: new Set(hosts.map(hostKey).filter((host) => host !== "")),
|
|
13043
13482
|
kubeContexts: configList(config, "prod", "kube_contexts"),
|
|
@@ -13193,15 +13632,15 @@ var blockOf = (t, hit3, command, deploy) => ({
|
|
|
13193
13632
|
})
|
|
13194
13633
|
});
|
|
13195
13634
|
var prodGuard = (runtime, root, t) => {
|
|
13196
|
-
const targets2 =
|
|
13635
|
+
const targets2 = memo(() => loadProdTargets(runtime, root, loadProjectConfig(runtime, root)));
|
|
13197
13636
|
const isProd = isProdHostOf(runtime, root, targets2);
|
|
13198
13637
|
return {
|
|
13199
13638
|
decide: (command) => {
|
|
13200
13639
|
if (!PROD_PROGRAMS.has(command.program)) return void 0;
|
|
13201
13640
|
const hit3 = prodHit(command, targets2(), isProd);
|
|
13202
13641
|
if (hit3 === void 0) return void 0;
|
|
13203
|
-
const
|
|
13204
|
-
return hit3.effect === "read" ? { kind: "read", hit: hit3, command:
|
|
13642
|
+
const text9 = commandText(command);
|
|
13643
|
+
return hit3.effect === "read" ? { kind: "read", hit: hit3, command: text9 } : blockOf(t, hit3, text9, targets2().deployPath);
|
|
13205
13644
|
}
|
|
13206
13645
|
};
|
|
13207
13646
|
};
|
|
@@ -13218,11 +13657,11 @@ var recordEvidence = (runtime, root, owner, reads) => {
|
|
|
13218
13657
|
] : []
|
|
13219
13658
|
);
|
|
13220
13659
|
if (lines3.length === 0) return;
|
|
13221
|
-
const path = toAbsolutePath(
|
|
13222
|
-
runtime.mkdir(toAbsolutePath(
|
|
13660
|
+
const path = toAbsolutePath(join33(root, EVIDENCE_FILE));
|
|
13661
|
+
runtime.mkdir(toAbsolutePath(join33(root, ".finitemonkey")));
|
|
13223
13662
|
const before = runtime.readText(path);
|
|
13224
|
-
const
|
|
13225
|
-
runtime.writeAtomic(path, `${
|
|
13663
|
+
const text9 = before.ok ? before.value : "";
|
|
13664
|
+
runtime.writeAtomic(path, `${text9}${text9 === "" || text9.endsWith("\n") ? "" : "\n"}${lines3.join("\n")}
|
|
13226
13665
|
`);
|
|
13227
13666
|
};
|
|
13228
13667
|
|
|
@@ -13237,15 +13676,15 @@ var editTexts = (input, file) => {
|
|
|
13237
13676
|
if (!Array.isArray(edits)) return [];
|
|
13238
13677
|
return edits.flatMap((edit) => {
|
|
13239
13678
|
if (!isRecord(edit)) return [];
|
|
13240
|
-
const
|
|
13241
|
-
return
|
|
13679
|
+
const text9 = optionalString(edit, "new_string");
|
|
13680
|
+
return text9 === void 0 ? [] : [{ file, text: text9, whole: false }];
|
|
13242
13681
|
});
|
|
13243
13682
|
};
|
|
13244
13683
|
var writtenTexts = (tool, input) => {
|
|
13245
13684
|
const file = optionalString(input, "file_path") ?? optionalString(input, "notebook_path") ?? "";
|
|
13246
13685
|
const single2 = (key, whole) => {
|
|
13247
|
-
const
|
|
13248
|
-
return
|
|
13686
|
+
const text9 = optionalString(input, key);
|
|
13687
|
+
return text9 === void 0 ? [] : [{ file, text: text9, whole }];
|
|
13249
13688
|
};
|
|
13250
13689
|
switch (tool) {
|
|
13251
13690
|
case "Write":
|
|
@@ -13927,24 +14366,24 @@ var withChain = (facts, chain) => {
|
|
|
13927
14366
|
const branch = chain?.branch;
|
|
13928
14367
|
return branch === void 0 ? facts : { ...facts, currentBranch: () => branch };
|
|
13929
14368
|
};
|
|
13930
|
-
var decide = (context, repo, verdict, facts,
|
|
14369
|
+
var decide = (context, repo, verdict, facts, text9) => {
|
|
13931
14370
|
switch (verdict.kind) {
|
|
13932
14371
|
case "pass":
|
|
13933
14372
|
return { kind: "pass" };
|
|
13934
14373
|
case "block":
|
|
13935
|
-
return { kind: "block", rule: verdict.rule, subject: verdict.subject, command:
|
|
14374
|
+
return { kind: "block", rule: verdict.rule, subject: verdict.subject, command: text9 };
|
|
13936
14375
|
case "snapshot":
|
|
13937
|
-
return { kind: "snapshot", repo, op: verdict.op, command:
|
|
14376
|
+
return { kind: "snapshot", repo, op: verdict.op, command: text9 };
|
|
13938
14377
|
case "discard": {
|
|
13939
14378
|
const entries = facts.dirty();
|
|
13940
|
-
if (entries === void 0) return { kind: "unknown", command:
|
|
14379
|
+
if (entries === void 0) return { kind: "unknown", command: text9 };
|
|
13941
14380
|
const foreign = foreignFiles(
|
|
13942
14381
|
context.runtime,
|
|
13943
14382
|
repo,
|
|
13944
14383
|
context.owner,
|
|
13945
14384
|
targetsOf(entries, verdict.discard, facts.prefix)
|
|
13946
14385
|
);
|
|
13947
|
-
return foreign.length > 0 ? { kind: "foreign", files: foreign, command:
|
|
14386
|
+
return foreign.length > 0 ? { kind: "foreign", files: foreign, command: text9 } : { kind: "snapshot", repo, op: verdict.op, command: text9 };
|
|
13948
14387
|
}
|
|
13949
14388
|
}
|
|
13950
14389
|
};
|
|
@@ -14031,10 +14470,10 @@ var listWorktrees = (runtime, cwd) => {
|
|
|
14031
14470
|
};
|
|
14032
14471
|
|
|
14033
14472
|
// src/git/journal.ts
|
|
14034
|
-
import { join as
|
|
14473
|
+
import { join as join34 } from "node:path";
|
|
14035
14474
|
var JOURNAL_FILE = ".finitemonkey/git-journal.jsonl";
|
|
14036
14475
|
var JOURNAL_SCHEMA = "finitemonkey/git-journal@1";
|
|
14037
|
-
var journalPath = (root) => toAbsolutePath(
|
|
14476
|
+
var journalPath = (root) => toAbsolutePath(join34(root, JOURNAL_FILE));
|
|
14038
14477
|
var headsOf = (value) => isRecord(value) ? Object.fromEntries(
|
|
14039
14478
|
Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
|
|
14040
14479
|
) : {};
|
|
@@ -14061,16 +14500,16 @@ var parseJournalLine = (line2) => {
|
|
|
14061
14500
|
};
|
|
14062
14501
|
};
|
|
14063
14502
|
var readJournal = (runtime, root) => {
|
|
14064
|
-
const
|
|
14065
|
-
if (!
|
|
14066
|
-
return
|
|
14503
|
+
const text9 = runtime.readText(journalPath(root));
|
|
14504
|
+
if (!text9.ok) return [];
|
|
14505
|
+
return text9.value.split("\n").flatMap((line2) => {
|
|
14067
14506
|
const entry2 = line2.trim() === "" ? void 0 : parseJournalLine(line2);
|
|
14068
14507
|
return entry2 === void 0 ? [] : [entry2];
|
|
14069
14508
|
});
|
|
14070
14509
|
};
|
|
14071
14510
|
var appendJournal = (runtime, root, entry2) => {
|
|
14072
14511
|
const path = journalPath(root);
|
|
14073
|
-
const made = runtime.mkdir(toAbsolutePath(
|
|
14512
|
+
const made = runtime.mkdir(toAbsolutePath(join34(root, ".finitemonkey")));
|
|
14074
14513
|
if (!made.ok) return made;
|
|
14075
14514
|
const previous = runtime.readText(path);
|
|
14076
14515
|
const before = previous.ok ? previous.value : "";
|
|
@@ -14234,9 +14673,9 @@ var snapshotsOf = (input, owner, pending) => {
|
|
|
14234
14673
|
return { kind: "allow", output: { hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext: context } } };
|
|
14235
14674
|
};
|
|
14236
14675
|
var review = (input, commands, owner) => {
|
|
14237
|
-
const worktrees =
|
|
14238
|
-
const repo =
|
|
14239
|
-
const prod =
|
|
14676
|
+
const worktrees = memo(() => listWorktrees(input.runtime, input.cwd));
|
|
14677
|
+
const repo = memo(() => openRepo(input.runtime, input.cwd));
|
|
14678
|
+
const prod = memo(() => prodGuard(input.runtime, repo()?.root ?? input.cwd, input.t));
|
|
14240
14679
|
const snapshots = [];
|
|
14241
14680
|
const reads = [];
|
|
14242
14681
|
const chain = { branch: void 0 };
|
|
@@ -14255,9 +14694,9 @@ var review = (input, commands, owner) => {
|
|
|
14255
14694
|
return { blocked: void 0, snapshots, reads };
|
|
14256
14695
|
};
|
|
14257
14696
|
var gitPreToolUse = (input) => {
|
|
14258
|
-
const
|
|
14259
|
-
if (
|
|
14260
|
-
const commands = normalizeCommand(
|
|
14697
|
+
const text9 = shellText(input.payload);
|
|
14698
|
+
if (text9 === void 0) return ALLOW;
|
|
14699
|
+
const commands = normalizeCommand(text9, dialectOf(toolName2(input.payload))).commands;
|
|
14261
14700
|
if (commands.length === 0) return ALLOW;
|
|
14262
14701
|
const owner = ownerOf2(input.payload);
|
|
14263
14702
|
const result = review(input, commands, owner);
|
|
@@ -14319,8 +14758,8 @@ var GIT_HANDLERS = {
|
|
|
14319
14758
|
var SHADOW_FILE = "shadow.json";
|
|
14320
14759
|
var numberOf = (value) => typeof value === "number" && Number.isInteger(value) ? value : 0;
|
|
14321
14760
|
var readShadow = (runtime, paths) => {
|
|
14322
|
-
const
|
|
14323
|
-
const parsed =
|
|
14761
|
+
const text9 = runtime.readText(paths.cache(SHADOW_FILE));
|
|
14762
|
+
const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
|
|
14324
14763
|
if (parsed?.ok !== true) return {};
|
|
14325
14764
|
return Object.fromEntries(
|
|
14326
14765
|
Object.entries(parsed.value).flatMap(
|
|
@@ -14404,11 +14843,11 @@ var ASSIGNMENT3 = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
|
14404
14843
|
var WRAPPERS2 = /* @__PURE__ */ new Set(["sudo", "command", "exec", "time", "nice", "nohup", "builtin", "env"]);
|
|
14405
14844
|
var liftHeredocs = (command) => {
|
|
14406
14845
|
const heredocs = [];
|
|
14407
|
-
const
|
|
14846
|
+
const text9 = command.replace(HEREDOC2, (_match, _quote, _tag, body2) => {
|
|
14408
14847
|
heredocs.push(body2);
|
|
14409
14848
|
return `<<__FM_HEREDOC_${String(heredocs.length - 1)}__`;
|
|
14410
14849
|
});
|
|
14411
|
-
return { text:
|
|
14850
|
+
return { text: text9, heredocs };
|
|
14412
14851
|
};
|
|
14413
14852
|
var endWord = (scan) => {
|
|
14414
14853
|
if (scan.word !== void 0) scan.words.push(scan.word);
|
|
@@ -14419,23 +14858,23 @@ var endSegment = (scan) => {
|
|
|
14419
14858
|
if (scan.words.length > 0) scan.segments.push(scan.words);
|
|
14420
14859
|
scan.words = [];
|
|
14421
14860
|
};
|
|
14422
|
-
var quotedEnd2 = (
|
|
14861
|
+
var quotedEnd2 = (text9, from, quote) => {
|
|
14423
14862
|
let index = from;
|
|
14424
|
-
while (index <
|
|
14863
|
+
while (index < text9.length && text9[index] !== quote) index += quote === '"' && text9[index] === "\\" ? 2 : 1;
|
|
14425
14864
|
return index;
|
|
14426
14865
|
};
|
|
14427
14866
|
var unescape = (value) => value.replace(/\\(["\\$`])/g, "$1");
|
|
14428
|
-
var splitWords = (
|
|
14867
|
+
var splitWords = (text9) => {
|
|
14429
14868
|
const scan = { words: [], segments: [], word: void 0 };
|
|
14430
|
-
for (let index = 0; index <
|
|
14431
|
-
const char =
|
|
14869
|
+
for (let index = 0; index < text9.length; index += 1) {
|
|
14870
|
+
const char = text9[index] ?? "";
|
|
14432
14871
|
if (char === '"' || char === "'") {
|
|
14433
|
-
const end = quotedEnd2(
|
|
14434
|
-
const inner =
|
|
14872
|
+
const end = quotedEnd2(text9, index + 1, char);
|
|
14873
|
+
const inner = text9.slice(index + 1, end);
|
|
14435
14874
|
scan.word = (scan.word ?? "") + (char === '"' ? unescape(inner) : inner);
|
|
14436
14875
|
index = end;
|
|
14437
|
-
} else if (char === "\\" && index + 1 <
|
|
14438
|
-
scan.word = (scan.word ?? "") + (
|
|
14876
|
+
} else if (char === "\\" && index + 1 < text9.length) {
|
|
14877
|
+
scan.word = (scan.word ?? "") + (text9[index + 1] === "\n" ? "" : text9[index + 1] ?? "");
|
|
14439
14878
|
index += 1;
|
|
14440
14879
|
} else if (SEPARATOR.test(char)) {
|
|
14441
14880
|
endSegment(scan);
|
|
@@ -14456,8 +14895,8 @@ var segmentOf = (words) => {
|
|
|
14456
14895
|
return { program: program.split(/[\\/]/).pop() ?? program, args: words.slice(index + 1) };
|
|
14457
14896
|
};
|
|
14458
14897
|
var shellWords = (command) => {
|
|
14459
|
-
const { text:
|
|
14460
|
-
const segments = splitWords(
|
|
14898
|
+
const { text: text9, heredocs } = liftHeredocs(command);
|
|
14899
|
+
const segments = splitWords(text9).flatMap((words) => {
|
|
14461
14900
|
const segment = segmentOf(words);
|
|
14462
14901
|
return segment === void 0 ? [] : [segment];
|
|
14463
14902
|
});
|
|
@@ -14562,7 +15001,7 @@ var sourceOf = (value, isFile, segment, heredocs) => {
|
|
|
14562
15001
|
if (inline3 !== void 0) return { kind: "inline", text: inline3 };
|
|
14563
15002
|
if (!isFile) return { kind: "inline", text: value };
|
|
14564
15003
|
if (value !== "-") return { kind: "file", path: value };
|
|
14565
|
-
const piped = segment.args.map((arg) => heredocIn(arg, heredocs)).find((
|
|
15004
|
+
const piped = segment.args.map((arg) => heredocIn(arg, heredocs)).find((text9) => text9 !== void 0);
|
|
14566
15005
|
return { kind: "inline", text: piped ?? "" };
|
|
14567
15006
|
};
|
|
14568
15007
|
var collect3 = (target2, segment, args2, spec2, heredocs) => {
|
|
@@ -14659,15 +15098,15 @@ var textOf2 = (input, found) => {
|
|
|
14659
15098
|
return accept(parts.join("\n\n"));
|
|
14660
15099
|
};
|
|
14661
15100
|
var decisionFor = (input, found, blocking2) => {
|
|
14662
|
-
const
|
|
14663
|
-
if (!
|
|
15101
|
+
const text9 = textOf2(input, found);
|
|
15102
|
+
if (!text9.ok) {
|
|
14664
15103
|
return blocking2 ? {
|
|
14665
15104
|
kind: "block",
|
|
14666
|
-
reason: modelReason(input.t, "prose.block.unreadable", { ...
|
|
15105
|
+
reason: modelReason(input.t, "prose.block.unreadable", { ...text9.error }),
|
|
14667
15106
|
human: input.t("prose.block.human")
|
|
14668
15107
|
} : void 0;
|
|
14669
15108
|
}
|
|
14670
|
-
const report2 = scoreProse(
|
|
15109
|
+
const report2 = scoreProse(text9.value, { context: "record" });
|
|
14671
15110
|
if (report2.allowed || report2.spans.length === 0) return void 0;
|
|
14672
15111
|
const params2 = {
|
|
14673
15112
|
target: input.t(TARGET_KEYS[found.target]),
|
|
@@ -14714,7 +15153,7 @@ var HOOK_ACTOR = "finitemonkey";
|
|
|
14714
15153
|
var liveFindings = (input, texts2) => {
|
|
14715
15154
|
const baseline = loadBaseline(input.runtime, input.cwd);
|
|
14716
15155
|
const accepted2 = acceptedFingerprints(baseline.ok ? baseline.value : EMPTY_BASELINE, input.runtime.now());
|
|
14717
|
-
return texts2.flatMap((
|
|
15156
|
+
return texts2.flatMap((text9) => scanText(text9, { rules: CREDENTIAL_RULES })).filter((finding4) => !accepted2.has(finding4.fingerprint));
|
|
14718
15157
|
};
|
|
14719
15158
|
var record2 = (input, findings, draft, where) => {
|
|
14720
15159
|
const ids2 = findings.map((finding4) => {
|
|
@@ -14975,12 +15414,12 @@ var secretsSessionStart = (input) => {
|
|
|
14975
15414
|
const remotes = remotesWithCredentials(input.runtime, input.cwd);
|
|
14976
15415
|
if (remotes.length === 0) return { kind: "allow" };
|
|
14977
15416
|
const params2 = { remotes: remotes.join(", "), remote: remotes[0] ?? "origin" };
|
|
14978
|
-
const
|
|
15417
|
+
const text9 = input.t("secrets.session.remote_credential", params2);
|
|
14979
15418
|
return {
|
|
14980
15419
|
kind: "allow",
|
|
14981
15420
|
output: {
|
|
14982
|
-
systemMessage: `finitemonkey: ${
|
|
14983
|
-
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext:
|
|
15421
|
+
systemMessage: `finitemonkey: ${text9}`,
|
|
15422
|
+
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: text9 }
|
|
14984
15423
|
}
|
|
14985
15424
|
};
|
|
14986
15425
|
};
|
|
@@ -15080,9 +15519,9 @@ var EMPTY_PAYLOAD = {
|
|
|
15080
15519
|
stopHookActive: false,
|
|
15081
15520
|
raw: {}
|
|
15082
15521
|
};
|
|
15083
|
-
var parseHookPayload = (event,
|
|
15084
|
-
if (interactive &&
|
|
15085
|
-
const parsed = parseJsonObject(
|
|
15522
|
+
var parseHookPayload = (event, text9, interactive) => {
|
|
15523
|
+
if (interactive && text9.trim() === "") return accept(EMPTY_PAYLOAD);
|
|
15524
|
+
const parsed = parseJsonObject(text9);
|
|
15086
15525
|
if (!parsed.ok) return reject(`stdin: ${parsed.error}`);
|
|
15087
15526
|
const payload = {
|
|
15088
15527
|
hookEventName: optionalString(parsed.value, "hook_event_name"),
|
|
@@ -15097,53 +15536,13 @@ var parseHookPayload = (event, text8, interactive) => {
|
|
|
15097
15536
|
return accept(payload);
|
|
15098
15537
|
};
|
|
15099
15538
|
|
|
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
15539
|
// src/hooks/runner.ts
|
|
15141
15540
|
var HOOK_PROBE_ENV = "FINITEMONKEY_HOOK_PROBE";
|
|
15142
15541
|
var messageOf2 = (error) => error instanceof Error ? error.message : String(error);
|
|
15143
15542
|
var decide2 = (input, handlers) => composeDecisions(input, handlers[input.event] ?? [allow]);
|
|
15144
15543
|
var evaluate = async (event, options) => {
|
|
15145
|
-
const
|
|
15146
|
-
const parsed = parseHookPayload(event,
|
|
15544
|
+
const text9 = await readAll(options.stdin);
|
|
15545
|
+
const parsed = parseHookPayload(event, text9, options.stdin.isTTY === true);
|
|
15147
15546
|
if (!parsed.ok) return { kind: "failed", payload: void 0, detail: parsed.error };
|
|
15148
15547
|
const payload = parsed.value;
|
|
15149
15548
|
if (options.env?.[HOOK_PROBE_ENV] === "1") return { kind: "decided", payload, decision: { kind: "allow" } };
|
|
@@ -15220,7 +15619,7 @@ var runHook = async (event, options) => {
|
|
|
15220
15619
|
var PROBE_TIMEOUT_MS2 = 1e4;
|
|
15221
15620
|
var SLOW_HOOK_MS = 1500;
|
|
15222
15621
|
var DEFAULT_TIMEOUT_S = 60;
|
|
15223
|
-
var expand = (
|
|
15622
|
+
var expand = (text9, vars) => text9.replace(/\$\{(\w+)\}|\$(\w+)/g, (match, braced, bare) => {
|
|
15224
15623
|
const name = braced ?? bare ?? "";
|
|
15225
15624
|
return vars[name] ?? match;
|
|
15226
15625
|
});
|
|
@@ -15269,7 +15668,7 @@ var run2 = (exec, invocation, cwd, env, input) => exec({
|
|
|
15269
15668
|
env: { ...env, [HOOK_PROBE_ENV]: "1" },
|
|
15270
15669
|
...input === void 0 ? {} : { input }
|
|
15271
15670
|
});
|
|
15272
|
-
var firstLine = (
|
|
15671
|
+
var firstLine = (text9) => maskText(text9.trim().split("\n")[0] ?? "").slice(0, 160);
|
|
15273
15672
|
var classify2 = (result) => {
|
|
15274
15673
|
if (!result.found) return { kind: "failed", problem: "missing", detail: "" };
|
|
15275
15674
|
if (result.timedOut) return { kind: "failed", problem: "timeout", detail: "" };
|
|
@@ -15481,7 +15880,7 @@ var hookSchemaProblems = (hooks, claude) => {
|
|
|
15481
15880
|
};
|
|
15482
15881
|
|
|
15483
15882
|
// src/doctor/hook-sources.ts
|
|
15484
|
-
import { join as
|
|
15883
|
+
import { join as join35 } from "node:path";
|
|
15485
15884
|
var entriesOf = (source, hooks, own) => Object.entries(asRecord(hooks)).flatMap(
|
|
15486
15885
|
([event, groups]) => asList(groups).flatMap((group) => {
|
|
15487
15886
|
const record3 = asRecord(group);
|
|
@@ -15494,31 +15893,31 @@ var projectHookFiles = (env) => readSettings(env).flatMap(({ scope: scope2, path
|
|
|
15494
15893
|
const source = PROJECT_SCOPES[scope2];
|
|
15495
15894
|
return source === void 0 || !("hooks" in settings) ? [] : [{ source, path, hooks: settings["hooks"] }];
|
|
15496
15895
|
});
|
|
15497
|
-
var pluginsDir = ({ env, runtime }) =>
|
|
15896
|
+
var pluginsDir = ({ env, runtime }) => join35(env["CLAUDE_CONFIG_DIR"] ?? join35(runtime.homeDir(), ".claude"), "plugins");
|
|
15498
15897
|
var installRecord = (value, root) => {
|
|
15499
15898
|
const records = Array.isArray(value) ? value.filter(isRecord) : isRecord(value) ? [value] : [];
|
|
15500
15899
|
return records.find((record3) => record3["projectPath"] === root) ?? records[0];
|
|
15501
15900
|
};
|
|
15502
15901
|
var readJson = (env, path) => {
|
|
15503
|
-
const
|
|
15504
|
-
const parsed =
|
|
15902
|
+
const text9 = env.runtime.readText(path);
|
|
15903
|
+
const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
|
|
15505
15904
|
return parsed?.ok === true ? parsed.value : void 0;
|
|
15506
15905
|
};
|
|
15507
15906
|
var installedPlugin = (env) => {
|
|
15508
|
-
const index = readJson(env, toAbsolutePath(
|
|
15907
|
+
const index = readJson(env, toAbsolutePath(join35(pluginsDir(env), "installed_plugins.json")));
|
|
15509
15908
|
const record3 = installRecord(asRecord(index?.["plugins"])[PLUGIN_ID], env.root);
|
|
15510
15909
|
const installPath = record3 === void 0 ? void 0 : optionalString(record3, "installPath");
|
|
15511
15910
|
if (record3 === void 0 || installPath === void 0) return void 0;
|
|
15512
15911
|
const root = toAbsolutePath(installPath);
|
|
15513
|
-
const manifest = readJson(env, toAbsolutePath(
|
|
15514
|
-
const hooksPath = toAbsolutePath(
|
|
15515
|
-
const
|
|
15516
|
-
const parsed =
|
|
15912
|
+
const manifest = readJson(env, toAbsolutePath(join35(root, ".claude-plugin", "plugin.json")));
|
|
15913
|
+
const hooksPath = toAbsolutePath(join35(root, "hooks", "hooks.json"));
|
|
15914
|
+
const text9 = env.runtime.readText(hooksPath);
|
|
15915
|
+
const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
|
|
15517
15916
|
return {
|
|
15518
15917
|
root,
|
|
15519
15918
|
version: (manifest === void 0 ? void 0 : optionalString(manifest, "version")) ?? optionalString(record3, "version"),
|
|
15520
15919
|
hooksFile: parsed?.ok === true ? { source: "plugin", path: hooksPath, hooks: parsed.value["hooks"] } : void 0,
|
|
15521
|
-
hooksText:
|
|
15920
|
+
hooksText: text9.ok ? text9.value : void 0
|
|
15522
15921
|
};
|
|
15523
15922
|
};
|
|
15524
15923
|
var PRECEDENCE2 = ["managed", "local", "project", "user"];
|
|
@@ -15548,15 +15947,15 @@ var schemaFinding = (file, problems, claude) => problems.length === 0 ? [] : [
|
|
|
15548
15947
|
)
|
|
15549
15948
|
];
|
|
15550
15949
|
var unreadableProjectFiles = (env) => settingsPaths(env).filter(({ scope: scope2 }) => scope2 === "project" || scope2 === "local").flatMap(({ path }) => {
|
|
15551
|
-
const
|
|
15552
|
-
return
|
|
15950
|
+
const text9 = env.runtime.readText(path);
|
|
15951
|
+
return text9.ok && text9.value.trim() !== "" && !parseJsonObject(text9.value).ok ? [path] : [];
|
|
15553
15952
|
});
|
|
15554
15953
|
var schemaFindings = (env, plugin, claude) => {
|
|
15555
15954
|
const label2 = claude === void 0 ? "?" : claude.join(".");
|
|
15556
15955
|
const files = [...projectHookFiles(env), ...plugin?.hooksFile === void 0 ? [] : [plugin.hooksFile]];
|
|
15557
15956
|
const invalidJson = [
|
|
15558
15957
|
...unreadableProjectFiles(env),
|
|
15559
|
-
...plugin?.hooksText !== void 0 && plugin.hooksFile === void 0 ? [toAbsolutePath(
|
|
15958
|
+
...plugin?.hooksText !== void 0 && plugin.hooksFile === void 0 ? [toAbsolutePath(join36(plugin.root, "hooks", "hooks.json"))] : []
|
|
15560
15959
|
];
|
|
15561
15960
|
return [
|
|
15562
15961
|
...invalidJson.flatMap((file) => schemaFinding(file, ["not valid JSON"], label2)),
|
|
@@ -15577,7 +15976,7 @@ var pluginFindings = (env, projectOwn) => {
|
|
|
15577
15976
|
];
|
|
15578
15977
|
};
|
|
15579
15978
|
var coverageFindings = (env, entries) => {
|
|
15580
|
-
const installed = env.runtime.stat(toAbsolutePath(
|
|
15979
|
+
const installed = env.runtime.stat(toAbsolutePath(join36(env.root, ...LOCK_FILE.split("/")))) !== void 0;
|
|
15581
15980
|
if (entries.length === 0 && !installed) {
|
|
15582
15981
|
return [finding("hooks.registered", "warn", "doctor.hooks.none", {}, "doctor.hooks.fix_none")];
|
|
15583
15982
|
}
|
|
@@ -15629,21 +16028,21 @@ var checkHooks = (env) => {
|
|
|
15629
16028
|
};
|
|
15630
16029
|
|
|
15631
16030
|
// src/doctor/instructions.ts
|
|
15632
|
-
import { join as
|
|
16031
|
+
import { join as join37 } from "node:path";
|
|
15633
16032
|
var INSTRUCTION_LINE_CEILING = 200;
|
|
15634
16033
|
var TOP_INSTRUCTIONS = ["AGENTS.md", "CLAUDE.md", ".claude/CLAUDE.md", "CLAUDE.local.md"];
|
|
15635
16034
|
var RULES_DIR = ".claude/rules";
|
|
15636
16035
|
var SHOWN = 3;
|
|
15637
16036
|
var readProjectFile = (env, relative11) => {
|
|
15638
|
-
const
|
|
15639
|
-
return
|
|
16037
|
+
const text9 = env.runtime.readText(toAbsolutePath(join37(env.root, ...relative11.split("/"))));
|
|
16038
|
+
return text9.ok ? text9.value : void 0;
|
|
15640
16039
|
};
|
|
15641
16040
|
var instructionFiles = (env) => {
|
|
15642
|
-
const listed4 = env.runtime.listDir(toAbsolutePath(
|
|
16041
|
+
const listed4 = env.runtime.listDir(toAbsolutePath(join37(env.root, ...RULES_DIR.split("/"))));
|
|
15643
16042
|
const rules = listed4.ok ? listed4.value.filter((name) => name.endsWith(".md")).map((name) => `${RULES_DIR}/${name}`) : [];
|
|
15644
16043
|
return [...TOP_INSTRUCTIONS, ...rules].flatMap((path) => {
|
|
15645
|
-
const
|
|
15646
|
-
return
|
|
16044
|
+
const text9 = readProjectFile(env, path);
|
|
16045
|
+
return text9 === void 0 ? [] : [{ path, text: text9 }];
|
|
15647
16046
|
});
|
|
15648
16047
|
};
|
|
15649
16048
|
var shortList = (items) => items.length <= SHOWN ? items.join(", ") : `${items.slice(0, SHOWN).join(", ")} (+${String(items.length - SHOWN)})`;
|
|
@@ -15691,10 +16090,10 @@ var hiddenInstructions = (env, deny2) => {
|
|
|
15691
16090
|
];
|
|
15692
16091
|
};
|
|
15693
16092
|
var localFile = (env) => readProjectFile(env, "CLAUDE.local.md") === void 0 ? [] : [finding("instructions.local", "warn", "doctor.instructions.local", {}, "doctor.instructions.fix_local")];
|
|
15694
|
-
var lineCount = (
|
|
16093
|
+
var lineCount = (text9) => text9.split("\n").length - (text9.endsWith("\n") ? 1 : 0);
|
|
15695
16094
|
var oversized = (env) => ["AGENTS.md", "CLAUDE.md"].flatMap((file) => {
|
|
15696
|
-
const
|
|
15697
|
-
const lines3 =
|
|
16095
|
+
const text9 = readProjectFile(env, file);
|
|
16096
|
+
const lines3 = text9 === void 0 ? 0 : lineCount(text9);
|
|
15698
16097
|
if (lines3 <= INSTRUCTION_LINE_CEILING) return [];
|
|
15699
16098
|
return [
|
|
15700
16099
|
finding(
|
|
@@ -15707,13 +16106,13 @@ var oversized = (env) => ["AGENTS.md", "CLAUDE.md"].flatMap((file) => {
|
|
|
15707
16106
|
];
|
|
15708
16107
|
});
|
|
15709
16108
|
var MIN_RULE_WORDS = 6;
|
|
15710
|
-
var ruleLines = (
|
|
16109
|
+
var ruleLines = (text9) => text9.split("\n").map((raw) => withoutCarriageReturn(raw).trim()).filter((line2) => !/^(#|\||```|@|>)/.test(line2)).map(
|
|
15711
16110
|
(line2) => line2.replace(/^([-*+]|\d+[.)])\s+/, "").replace(/\s+/g, " ").toLowerCase()
|
|
15712
16111
|
).filter((line2) => line2.split(" ").length >= MIN_RULE_WORDS);
|
|
15713
16112
|
var duplicatedRules = (files) => {
|
|
15714
16113
|
const seen = /* @__PURE__ */ new Map();
|
|
15715
|
-
for (const { path, text:
|
|
15716
|
-
for (const line2 of ruleLines(
|
|
16114
|
+
for (const { path, text: text9 } of files) {
|
|
16115
|
+
for (const line2 of ruleLines(text9)) seen.set(line2, (seen.get(line2) ?? /* @__PURE__ */ new Set()).add(path));
|
|
15717
16116
|
}
|
|
15718
16117
|
return [...seen].filter(([, paths]) => paths.size > 1).map(([line2]) => line2);
|
|
15719
16118
|
};
|
|
@@ -15744,12 +16143,12 @@ var checkInstructions = (env) => {
|
|
|
15744
16143
|
};
|
|
15745
16144
|
|
|
15746
16145
|
// src/doctor/line-endings.ts
|
|
15747
|
-
import { join as
|
|
16146
|
+
import { join as join38 } from "node:path";
|
|
15748
16147
|
var LF_RULE = /^\*\s.*\beol=lf\b/m;
|
|
15749
16148
|
var checkLineEndings = ({ root, runtime }) => {
|
|
15750
16149
|
const autocrlf = runtime.git(["config", "--get", "core.autocrlf"], root);
|
|
15751
16150
|
if (!autocrlf.ok || autocrlf.stdout.trim().toLowerCase() !== "true") return [];
|
|
15752
|
-
const attributes = runtime.readText(toAbsolutePath(
|
|
16151
|
+
const attributes = runtime.readText(toAbsolutePath(join38(root, ".gitattributes")));
|
|
15753
16152
|
if (attributes.ok && LF_RULE.test(attributes.value)) return [];
|
|
15754
16153
|
return [
|
|
15755
16154
|
finding("git.autocrlf", "warn", "doctor.git.autocrlf", { file: ".gitattributes" }, "doctor.git.fix_autocrlf")
|
|
@@ -15757,25 +16156,25 @@ var checkLineEndings = ({ root, runtime }) => {
|
|
|
15757
16156
|
};
|
|
15758
16157
|
|
|
15759
16158
|
// src/doctor/memory.ts
|
|
15760
|
-
import { join as
|
|
16159
|
+
import { join as join39 } from "node:path";
|
|
15761
16160
|
var ceilingOf = (config) => {
|
|
15762
16161
|
const value = configAt(parseConfigYaml(config ?? ""), "memory", "maxLines");
|
|
15763
16162
|
const parsed = typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
|
|
15764
16163
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : AGENTS_MAX_LINES;
|
|
15765
16164
|
};
|
|
15766
|
-
var lineCount2 = (
|
|
16165
|
+
var lineCount2 = (text9) => text9.replace(/\r?\n$/, "").split(/\r?\n/).length;
|
|
15767
16166
|
var checkAgentsFile = ({ root, runtime }) => {
|
|
15768
|
-
const
|
|
15769
|
-
if (!
|
|
15770
|
-
const config = runtime.readText(toAbsolutePath(
|
|
16167
|
+
const text9 = runtime.readText(toAbsolutePath(join39(root, AGENTS_FILE)));
|
|
16168
|
+
if (!text9.ok) return [];
|
|
16169
|
+
const config = runtime.readText(toAbsolutePath(join39(root, CONFIG_FILE2)));
|
|
15771
16170
|
const max = ceilingOf(config.ok ? config.value : void 0);
|
|
15772
|
-
const lines3 = lineCount2(
|
|
16171
|
+
const lines3 = lineCount2(text9.value);
|
|
15773
16172
|
const params2 = { file: AGENTS_FILE, lines: lines3, max };
|
|
15774
16173
|
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
16174
|
};
|
|
15776
16175
|
|
|
15777
16176
|
// src/doctor/project-files.ts
|
|
15778
|
-
import { join as
|
|
16177
|
+
import { join as join40 } from "node:path";
|
|
15779
16178
|
var variants = (base, extensions) => extensions.map((extension) => `${base}${extension}`);
|
|
15780
16179
|
var SCRIPT = [".js", ".cjs", ".mjs", ".ts", ".cts", ".mts"];
|
|
15781
16180
|
var DATA = ["", ".json", ".yml", ".yaml"];
|
|
@@ -15806,7 +16205,7 @@ var CONFIG_GROUPS = [
|
|
|
15806
16205
|
files: [...variants("commitlint.config", SCRIPT), ...variants(".commitlintrc", [...DATA, ".js"])]
|
|
15807
16206
|
}
|
|
15808
16207
|
];
|
|
15809
|
-
var present = (env, files) => files.filter((file) => env.runtime.stat(toAbsolutePath(
|
|
16208
|
+
var present = (env, files) => files.filter((file) => env.runtime.stat(toAbsolutePath(join40(env.root, file)))?.kind === "file");
|
|
15810
16209
|
var crowded = (env, groups, build) => groups.flatMap(({ name, files }) => {
|
|
15811
16210
|
const found = present(env, files);
|
|
15812
16211
|
return found.length > 1 ? [build(name, found.join(", "))] : [];
|
|
@@ -15950,9 +16349,9 @@ var manualLines = (ctx, report2) => report2.checks.some((item2) => item2.status
|
|
|
15950
16349
|
var writeReport = (ctx, data, result) => {
|
|
15951
16350
|
const env = doctorEnvFrom(ctx);
|
|
15952
16351
|
const path = reportPath(env);
|
|
15953
|
-
const
|
|
16352
|
+
const text9 = reportMarkdown(env, ctx, toEnvelope("doctor", result));
|
|
15954
16353
|
const dir = ctx.runtime.mkdir(toAbsolutePath(dirname7(path)));
|
|
15955
|
-
const written = dir.ok ? ctx.runtime.writeAtomic(path,
|
|
16354
|
+
const written = dir.ok ? ctx.runtime.writeAtomic(path, text9) : dir;
|
|
15956
16355
|
if (!written.ok) return failureFrom(ctx.t, applyProblem({ path, detail: written.error }));
|
|
15957
16356
|
const human2 = [...result.human ?? [], ctx.t("doctor.report.written", { path })];
|
|
15958
16357
|
return { ...result, data: { ...data, reportPath: path }, human: human2 };
|
|
@@ -15984,14 +16383,14 @@ var doctorCommand = {
|
|
|
15984
16383
|
};
|
|
15985
16384
|
|
|
15986
16385
|
// src/gain/gain.ts
|
|
15987
|
-
import { join as
|
|
16386
|
+
import { join as join41 } from "node:path";
|
|
15988
16387
|
var GAIN_SCHEMA = "finitemonkey/gain@1";
|
|
15989
16388
|
var DAY_MS2 = 864e5;
|
|
15990
16389
|
var SAVE_WINDOW_MS = DAY_MS2;
|
|
15991
16390
|
var readLog = (runtime, paths) => {
|
|
15992
|
-
const
|
|
15993
|
-
if (!
|
|
15994
|
-
return
|
|
16391
|
+
const text9 = runtime.readText(paths.log);
|
|
16392
|
+
if (!text9.ok) return [];
|
|
16393
|
+
return text9.value.split("\n").flatMap((line2) => {
|
|
15995
16394
|
const parsed = parseJsonObject(line2);
|
|
15996
16395
|
if (!parsed.ok || !isRecord(parsed.value)) return [];
|
|
15997
16396
|
const { at, event } = parsed.value;
|
|
@@ -16000,9 +16399,9 @@ var readLog = (runtime, paths) => {
|
|
|
16000
16399
|
});
|
|
16001
16400
|
};
|
|
16002
16401
|
var readGates = (runtime, paths) => {
|
|
16003
|
-
const listed4 = runtime.listDir(toAbsolutePath(
|
|
16402
|
+
const listed4 = runtime.listDir(toAbsolutePath(join41(paths.dir, "evidence")));
|
|
16004
16403
|
const files = listed4.ok ? listed4.value.filter((name) => name.endsWith(".jsonl")) : [];
|
|
16005
|
-
return files.flatMap((name) =>
|
|
16404
|
+
return files.flatMap((name) => readLedger2(runtime, paths.evidence(name.replace(/\.jsonl$/, "")))).filter((entry2) => entry2.check === GATE_CHECK);
|
|
16006
16405
|
};
|
|
16007
16406
|
var countsIn = (log, gates, from, to) => {
|
|
16008
16407
|
const inWindow = (at) => at >= from && at < to;
|
|
@@ -16341,17 +16740,6 @@ var checksResult = (t, schema, code, checks) => {
|
|
|
16341
16740
|
};
|
|
16342
16741
|
};
|
|
16343
16742
|
|
|
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
16743
|
// src/commands/git-checks.ts
|
|
16356
16744
|
var COMMIT_MSG_SCHEMA = "finitemonkey/commit-msg@1";
|
|
16357
16745
|
var PUSH_CHECK_SCHEMA = "finitemonkey/push-check@1";
|
|
@@ -16372,15 +16760,15 @@ var commitMsgCommand = {
|
|
|
16372
16760
|
usageProblem("invalid_arguments", "error.invalid_arguments", COMMIT_MSG_USAGE, { detail: "<message-file>" })
|
|
16373
16761
|
)
|
|
16374
16762
|
);
|
|
16375
|
-
const
|
|
16376
|
-
if (!
|
|
16763
|
+
const text9 = ctx.runtime.readText(toAbsolutePath(file, ctx.cwd));
|
|
16764
|
+
if (!text9.ok)
|
|
16377
16765
|
return Promise.resolve(
|
|
16378
16766
|
failureFrom(
|
|
16379
16767
|
ctx.t,
|
|
16380
16768
|
usageProblem("invalid_arguments", "error.invalid_arguments", COMMIT_MSG_USAGE, { detail: file })
|
|
16381
16769
|
)
|
|
16382
16770
|
);
|
|
16383
|
-
const failures = commitMessageProblems(
|
|
16771
|
+
const failures = commitMessageProblems(text9.value);
|
|
16384
16772
|
const fix = "git commit --amend, using type(scope): subject and no AI attribution lines";
|
|
16385
16773
|
return Promise.resolve(
|
|
16386
16774
|
checksResult(ctx.t, COMMIT_MSG_SCHEMA, "githook_failed", [{ id: "commit-messages", failures, fix }])
|
|
@@ -16394,7 +16782,7 @@ var pushCheckCommand = {
|
|
|
16394
16782
|
status: "ready",
|
|
16395
16783
|
maxPositionals: 2,
|
|
16396
16784
|
run: async (ctx, args2) => {
|
|
16397
|
-
const repo =
|
|
16785
|
+
const repo = repoOf(ctx);
|
|
16398
16786
|
if (repo === void 0) return failureFrom(ctx.t, notRepository(ctx.cwd));
|
|
16399
16787
|
const piped = parsePushUpdates(ctx.io.stdin.isTTY === true ? "" : await readAll(ctx.io.stdin));
|
|
16400
16788
|
const updates = piped.length > 0 ? piped : updatesFromEnv(ctx.env);
|
|
@@ -16409,7 +16797,7 @@ var ciGateCommand = {
|
|
|
16409
16797
|
status: "ready",
|
|
16410
16798
|
options: { base: { type: "string" } },
|
|
16411
16799
|
run: (ctx, args2) => {
|
|
16412
|
-
const repo =
|
|
16800
|
+
const repo = repoOf(ctx);
|
|
16413
16801
|
if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
|
|
16414
16802
|
const base = ciBase(ctx.runtime, repo, ctx.env, stringOption(args2.values, "base"));
|
|
16415
16803
|
if (!baseExists(ctx.runtime, repo, base))
|
|
@@ -16495,7 +16883,7 @@ var mergeVerifyCommand = {
|
|
|
16495
16883
|
options: { since: { type: "string" }, dropped: { type: "string" } },
|
|
16496
16884
|
maxPositionals: 1,
|
|
16497
16885
|
run: (ctx, args2) => {
|
|
16498
|
-
const repo =
|
|
16886
|
+
const repo = repoOf(ctx);
|
|
16499
16887
|
if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
|
|
16500
16888
|
const dropped = stringOption(args2.values, "dropped");
|
|
16501
16889
|
const targets2 = targetsOf2(ctx, repo, args2.positionals[0], stringOption(args2.values, "since"));
|
|
@@ -16518,7 +16906,7 @@ var mergeVerifyCommand = {
|
|
|
16518
16906
|
var REFLOG_LIMIT = 300;
|
|
16519
16907
|
var LIST_LIMIT = 50;
|
|
16520
16908
|
var SHA = /^[0-9a-f]{40,64}$/;
|
|
16521
|
-
var linesOf2 = (
|
|
16909
|
+
var linesOf2 = (text9) => text9.split("\n").filter((line2) => line2.trim() !== "");
|
|
16522
16910
|
var reflogLines = (runtime, repo) => {
|
|
16523
16911
|
const result = runtime.git(["reflog", "show", "--all", `-n${String(REFLOG_LIMIT)}`, "--format=%H %gD"], repo.root);
|
|
16524
16912
|
if (!result.ok) return [];
|
|
@@ -16607,7 +16995,7 @@ var rescueCommand = {
|
|
|
16607
16995
|
status: "ready",
|
|
16608
16996
|
options: { restore: { type: "string" }, branch: { type: "string" } },
|
|
16609
16997
|
run: (ctx, args2) => {
|
|
16610
|
-
const repo =
|
|
16998
|
+
const repo = repoOf(ctx);
|
|
16611
16999
|
if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
|
|
16612
17000
|
const sha = stringOption(args2.values, "restore");
|
|
16613
17001
|
const branch = stringOption(args2.values, "branch");
|
|
@@ -16639,7 +17027,7 @@ var rescueCommand = {
|
|
|
16639
17027
|
};
|
|
16640
17028
|
|
|
16641
17029
|
// src/git/undo.ts
|
|
16642
|
-
import { basename as basename3, dirname as dirname8, join as
|
|
17030
|
+
import { basename as basename3, dirname as dirname8, join as join42 } from "node:path";
|
|
16643
17031
|
var INDEX_SUBJECT = "finitemonkey index:";
|
|
16644
17032
|
var gitLine = (runtime, repo, args2) => {
|
|
16645
17033
|
const result = runtime.git(args2, repo.root);
|
|
@@ -16682,7 +17070,7 @@ var findSnapshot = (views, wanted) => {
|
|
|
16682
17070
|
};
|
|
16683
17071
|
var stampOf2 = (iso) => iso.replace(/[:.]/g, "-");
|
|
16684
17072
|
var worktreePath = (runtime, repo) => {
|
|
16685
|
-
const base =
|
|
17073
|
+
const base = join42(dirname8(repo.root), `${basename3(repo.root)}-undo-${stampOf2(runtime.now())}`);
|
|
16686
17074
|
return toAbsolutePath(runtime.stat(toAbsolutePath(base)) === void 0 ? base : `${base}-2`);
|
|
16687
17075
|
};
|
|
16688
17076
|
var intoWorktree = (runtime, repo, view2) => {
|
|
@@ -16726,7 +17114,7 @@ var snapshotCommand = {
|
|
|
16726
17114
|
status: "ready",
|
|
16727
17115
|
options: { message: { type: "string", short: "m" } },
|
|
16728
17116
|
run: (ctx, args2) => {
|
|
16729
|
-
const repo =
|
|
17117
|
+
const repo = repoOf(ctx);
|
|
16730
17118
|
if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
|
|
16731
17119
|
const message = args2.values["message"];
|
|
16732
17120
|
const op = typeof message === "string" && message.trim() !== "" ? message.trim() : "manual";
|
|
@@ -16759,7 +17147,7 @@ var undoCommand = {
|
|
|
16759
17147
|
options: { list: { type: "boolean" }, "in-place": { type: "boolean" } },
|
|
16760
17148
|
maxPositionals: 1,
|
|
16761
17149
|
run: (ctx, args2) => {
|
|
16762
|
-
const repo =
|
|
17150
|
+
const repo = repoOf(ctx);
|
|
16763
17151
|
if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
|
|
16764
17152
|
const views = snapshotViews(ctx.runtime, repo);
|
|
16765
17153
|
if (args2.values["list"] === true) return Promise.resolve(listResult(ctx, views));
|
|
@@ -16807,7 +17195,7 @@ var GLOBAL_FLAGS = [
|
|
|
16807
17195
|
{ flag: "--help, -h", key: "flag.help" }
|
|
16808
17196
|
];
|
|
16809
17197
|
var exitCodeHelp = (ctx) => EXIT_CODE_NAMES.map((name) => ({ code: ExitCode[name], name, summary: ctx.t(exitCodeDocs[ExitCode[name]]) }));
|
|
16810
|
-
var pad = (
|
|
17198
|
+
var pad = (text9, width) => text9.padEnd(Math.max(width, text9.length + 1));
|
|
16811
17199
|
var describe2 = (ctx, spec2) => ({
|
|
16812
17200
|
name: commandName(spec2),
|
|
16813
17201
|
usage: spec2.usage,
|
|
@@ -16917,7 +17305,7 @@ var hookCommand = {
|
|
|
16917
17305
|
};
|
|
16918
17306
|
|
|
16919
17307
|
// src/commands/i18n-check.ts
|
|
16920
|
-
import { join as
|
|
17308
|
+
import { join as join43 } from "node:path";
|
|
16921
17309
|
|
|
16922
17310
|
// src/i18n/message.ts
|
|
16923
17311
|
var ARG = /^\s*([a-zA-Z][a-zA-Z0-9_]*)\s*(?:,\s*(plural|select)\s*,)?/;
|
|
@@ -16949,10 +17337,10 @@ var parseBranches = (body2, inPlural) => {
|
|
|
16949
17337
|
};
|
|
16950
17338
|
var parseNodes = (source, inPlural) => {
|
|
16951
17339
|
const nodes = [];
|
|
16952
|
-
let
|
|
17340
|
+
let text9 = "";
|
|
16953
17341
|
const flush2 = () => {
|
|
16954
|
-
if (
|
|
16955
|
-
|
|
17342
|
+
if (text9 !== "") nodes.push({ kind: "text", text: text9 });
|
|
17343
|
+
text9 = "";
|
|
16956
17344
|
};
|
|
16957
17345
|
for (let index = 0; index < source.length; index += 1) {
|
|
16958
17346
|
const char = source.charAt(index);
|
|
@@ -16962,7 +17350,7 @@ var parseNodes = (source, inPlural) => {
|
|
|
16962
17350
|
continue;
|
|
16963
17351
|
}
|
|
16964
17352
|
if (char !== "{") {
|
|
16965
|
-
|
|
17353
|
+
text9 += char;
|
|
16966
17354
|
continue;
|
|
16967
17355
|
}
|
|
16968
17356
|
const cursor = { source, index: index + 1 };
|
|
@@ -16970,18 +17358,18 @@ var parseNodes = (source, inPlural) => {
|
|
|
16970
17358
|
const inner = end < 0 ? void 0 : source.slice(index + 1, end);
|
|
16971
17359
|
const match = inner === void 0 ? null : ARG.exec(inner);
|
|
16972
17360
|
if (inner === void 0 || match === null) {
|
|
16973
|
-
|
|
17361
|
+
text9 += char;
|
|
16974
17362
|
continue;
|
|
16975
17363
|
}
|
|
16976
17364
|
const name = match[1] ?? "";
|
|
16977
17365
|
const type = match[2] === "plural" || match[2] === "select" ? match[2] : void 0;
|
|
16978
17366
|
if (type === void 0 && match[0].trim().length !== inner.trim().length) {
|
|
16979
|
-
|
|
17367
|
+
text9 += char;
|
|
16980
17368
|
continue;
|
|
16981
17369
|
}
|
|
16982
17370
|
const branches = type === void 0 ? void 0 : parseBranches(inner.slice(match[0].length), type === "plural");
|
|
16983
17371
|
if (type !== void 0 && branches === void 0) {
|
|
16984
|
-
|
|
17372
|
+
text9 += char;
|
|
16985
17373
|
continue;
|
|
16986
17374
|
}
|
|
16987
17375
|
flush2();
|
|
@@ -17104,8 +17492,8 @@ var RELEASE_HINTS = [
|
|
|
17104
17492
|
/\bFINITEMONKEY_[A-Z_]+/,
|
|
17105
17493
|
/\b[A-Z][A-Z0-9_]{2,}=/
|
|
17106
17494
|
];
|
|
17107
|
-
var releaseHints = (
|
|
17108
|
-
const match = pattern.exec(
|
|
17495
|
+
var releaseHints = (text9) => RELEASE_HINTS.flatMap((pattern) => {
|
|
17496
|
+
const match = pattern.exec(text9);
|
|
17109
17497
|
return match === null ? [] : [match[0]];
|
|
17110
17498
|
});
|
|
17111
17499
|
var hintProblems = (lang, catalog) => Object.keys(catalog).filter((key) => MODEL_FACING.test(key)).sort().flatMap((key) => {
|
|
@@ -17143,10 +17531,10 @@ var unreadable = (dir, file, detail) => ({
|
|
|
17143
17531
|
exitCode: ExitCode.NotConfigured
|
|
17144
17532
|
});
|
|
17145
17533
|
var readCatalog = (ctx, dir, lang) => {
|
|
17146
|
-
const file = toAbsolutePath(
|
|
17147
|
-
const
|
|
17148
|
-
if (!
|
|
17149
|
-
const parsed = parseJsonObject(
|
|
17534
|
+
const file = toAbsolutePath(join43(dir, `${lang}.json`));
|
|
17535
|
+
const text9 = ctx.runtime.readText(file);
|
|
17536
|
+
if (!text9.ok) return reject(unreadable(dir, file, text9.error));
|
|
17537
|
+
const parsed = parseJsonObject(text9.value);
|
|
17150
17538
|
return parsed.ok ? parsed : reject(unreadable(dir, file, parsed.error));
|
|
17151
17539
|
};
|
|
17152
17540
|
var loadCatalogs = (ctx, dir) => {
|
|
@@ -17166,8 +17554,8 @@ var describeProblem = (ctx, problem4) => {
|
|
|
17166
17554
|
return ` ${problem4.lang} ${problem4.key} ${kind}`;
|
|
17167
17555
|
};
|
|
17168
17556
|
var loadReview = (ctx, dir) => {
|
|
17169
|
-
const
|
|
17170
|
-
const parsed =
|
|
17557
|
+
const text9 = ctx.runtime.readText(toAbsolutePath(join43(dir, "review.json")));
|
|
17558
|
+
const parsed = text9.ok ? parseJsonObject(text9.value) : void 0;
|
|
17171
17559
|
if (parsed?.ok !== true) return void 0;
|
|
17172
17560
|
const statuses = (lang) => {
|
|
17173
17561
|
const value = parsed.value[lang];
|
|
@@ -17207,7 +17595,7 @@ var i18nCheckCommand = {
|
|
|
17207
17595
|
};
|
|
17208
17596
|
|
|
17209
17597
|
// src/history/look-back.ts
|
|
17210
|
-
import { join as
|
|
17598
|
+
import { join as join44 } from "node:path";
|
|
17211
17599
|
|
|
17212
17600
|
// src/history/patterns.ts
|
|
17213
17601
|
import { posix as posix5 } from "node:path";
|
|
@@ -17312,8 +17700,8 @@ var addResults = (draft, blocks) => {
|
|
|
17312
17700
|
let any = false;
|
|
17313
17701
|
for (const block4 of blocks) {
|
|
17314
17702
|
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(
|
|
17703
|
+
const text9 = textOf3(block4["content"]);
|
|
17704
|
+
draft.results.set(block4["tool_use_id"], { failed: block4["is_error"] === true || FAILED_EXIT.test(text9), text: text9 });
|
|
17317
17705
|
any = true;
|
|
17318
17706
|
}
|
|
17319
17707
|
return any;
|
|
@@ -17343,7 +17731,7 @@ var parseTranscript = (jsonl) => {
|
|
|
17343
17731
|
// src/history/look-back.ts
|
|
17344
17732
|
var SAMPLES = 3;
|
|
17345
17733
|
var projectLogKey = (root) => root.replace(/[^A-Za-z0-9-]/g, "-");
|
|
17346
|
-
var projectLogDir = (runtime, root, env) =>
|
|
17734
|
+
var projectLogDir = (runtime, root, env) => join44(env["CLAUDE_CONFIG_DIR"] ?? join44(runtime.homeDir(), ".claude"), "projects", projectLogKey(root));
|
|
17347
17735
|
var lookBack = (runtime, root, env) => {
|
|
17348
17736
|
const dir = projectLogDir(runtime, root, env);
|
|
17349
17737
|
const listed4 = runtime.listDir(toAbsolutePath(dir));
|
|
@@ -17364,9 +17752,9 @@ var lookBack = (runtime, root, env) => {
|
|
|
17364
17752
|
};
|
|
17365
17753
|
let turns = 0;
|
|
17366
17754
|
for (const name of files) {
|
|
17367
|
-
const
|
|
17368
|
-
if (!
|
|
17369
|
-
for (const turn of parseTranscript(
|
|
17755
|
+
const text9 = runtime.readText(toAbsolutePath(join44(dir, name)));
|
|
17756
|
+
if (!text9.ok) continue;
|
|
17757
|
+
for (const turn of parseTranscript(text9.value)) {
|
|
17370
17758
|
turns += 1;
|
|
17371
17759
|
for (const hit3 of hitsIn(turn)) {
|
|
17372
17760
|
counts[hit3.pattern] += 1;
|
|
@@ -17414,7 +17802,7 @@ var walk2 = (a, b) => {
|
|
|
17414
17802
|
}
|
|
17415
17803
|
return ops;
|
|
17416
17804
|
};
|
|
17417
|
-
var splitLines = (
|
|
17805
|
+
var splitLines = (text9) => text9 === "" ? [] : text9.replace(/\r?\n$/, "").split(/\r?\n/);
|
|
17418
17806
|
var sharedPrefix = (a, b) => {
|
|
17419
17807
|
let count4 = 0;
|
|
17420
17808
|
while (count4 < a.length && count4 < b.length && a[count4] === b[count4]) count4 += 1;
|
|
@@ -17468,11 +17856,18 @@ var itemWarning = (t, item2) => {
|
|
|
17468
17856
|
return [{ code: "edited_file", message: t("install.warn.edited", { path: item2.path }) }];
|
|
17469
17857
|
return [];
|
|
17470
17858
|
};
|
|
17859
|
+
var sandboxWarnings = (t, plan) => {
|
|
17860
|
+
if (plan.sandbox === "unsupported")
|
|
17861
|
+
return [{ code: "sandbox_unsupported", message: t("install.warn.sandbox_unsupported") }];
|
|
17862
|
+
if (plan.sandbox === "offer") return [{ code: "sandbox_offer", message: t("install.warn.sandbox_offer") }];
|
|
17863
|
+
return [];
|
|
17864
|
+
};
|
|
17471
17865
|
var planWarnings = (t, plan) => [
|
|
17472
17866
|
...plan.detection.gitRoot ? [] : [{ code: "not_git", message: t("install.warn.not_git") }],
|
|
17473
17867
|
...plan.detection.claude.failClosed ? [] : [{ code: "fail_open", message: t("install.warn.fail_open", { version: plan.detection.claude.version ?? "?" }) }],
|
|
17474
17868
|
...plan.detection.location.wslMount ? [{ code: "wsl_mount", message: t("install.warn.wsl_mount", { path: plan.detection.root }) }] : [],
|
|
17475
17869
|
...plan.detection.location.synced ? [{ code: "synced_folder", message: t("install.warn.synced_folder", { path: plan.detection.root }) }] : [],
|
|
17870
|
+
...sandboxWarnings(t, plan),
|
|
17476
17871
|
...plan.detection.authorities.map((authority) => authorityWarning(t, authority)),
|
|
17477
17872
|
...plan.items.flatMap((item2) => itemWarning(t, item2))
|
|
17478
17873
|
];
|
|
@@ -17491,7 +17886,7 @@ var planData = (t, plan, mode, applied, backup) => ({
|
|
|
17491
17886
|
diff: planDiff(plan),
|
|
17492
17887
|
...backup === void 0 ? {} : { backup }
|
|
17493
17888
|
});
|
|
17494
|
-
var pad2 = (
|
|
17889
|
+
var pad2 = (text9, width) => text9.padEnd(width);
|
|
17495
17890
|
var planLines = (t, data, withDiff) => [
|
|
17496
17891
|
t("install.plan.title", { root: data.root, preset: data.preset }),
|
|
17497
17892
|
...data.actions.map(({ path, action }) => ` ${pad2(action, 13)}${path}`),
|
|
@@ -17513,7 +17908,8 @@ var flagsFor = (request, preset) => [
|
|
|
17513
17908
|
`finitemonkey ${request.mode} --yes`,
|
|
17514
17909
|
...request.mode === "init" && preset !== "neutral" ? [`--preset ${preset}`] : [],
|
|
17515
17910
|
...request.bin === void 0 ? [] : [`--bin "${request.bin}"`],
|
|
17516
|
-
...request.marketplace ? ["--marketplace"] : []
|
|
17911
|
+
...request.marketplace ? ["--marketplace"] : [],
|
|
17912
|
+
...request.sandbox === true ? ["--sandbox"] : []
|
|
17517
17913
|
].join(" ");
|
|
17518
17914
|
var presentPlan = (ctx, request, plan) => {
|
|
17519
17915
|
const data = planData(ctx.t, plan, request.mode, false, void 0);
|
|
@@ -17539,7 +17935,7 @@ var runInstall = (ctx, request, presetFallback) => {
|
|
|
17539
17935
|
const { project, gitRoot: gitRoot2 } = openProject(ctx);
|
|
17540
17936
|
const previous = readPreviousLock(project);
|
|
17541
17937
|
if (!previous.ok) return failureFrom(ctx.t, planProblem(previous.error));
|
|
17542
|
-
const input = planInputFor(ctx, preset, request.bin, request.marketplace);
|
|
17938
|
+
const input = planInputFor(ctx, preset, request.bin, request.marketplace, request.sandbox === true);
|
|
17543
17939
|
const plan = buildPlan(project, detect(project, gitRoot2, input), input, previous.value);
|
|
17544
17940
|
if (!plan.ok) return failureFrom(ctx.t, planProblem(plan.error));
|
|
17545
17941
|
if (ctx.dryRun || !ctx.yes || !needsWrite(plan.value)) return presentPlan(ctx, request, plan.value);
|
|
@@ -17550,6 +17946,7 @@ var initRequest = (args2) => ({
|
|
|
17550
17946
|
preset: stringOption(args2.values, "preset"),
|
|
17551
17947
|
bin: stringOption(args2.values, "bin"),
|
|
17552
17948
|
marketplace: args2.values["marketplace"] === true,
|
|
17949
|
+
sandbox: args2.values["sandbox"] === true,
|
|
17553
17950
|
showDiff: false
|
|
17554
17951
|
});
|
|
17555
17952
|
var withLookBack = (ctx, result) => {
|
|
@@ -17566,12 +17963,13 @@ var withLookBack = (ctx, result) => {
|
|
|
17566
17963
|
var initCommand = {
|
|
17567
17964
|
path: ["init"],
|
|
17568
17965
|
summaryKey: "command.init.summary",
|
|
17569
|
-
usage: "finitemonkey init [--preset neutral|gabriel] [--bin <path>] [--marketplace] [--look-back] [--dry-run] [--yes]",
|
|
17966
|
+
usage: "finitemonkey init [--preset neutral|gabriel] [--bin <path>] [--marketplace] [--sandbox] [--look-back] [--dry-run] [--yes]",
|
|
17570
17967
|
status: "ready",
|
|
17571
17968
|
options: {
|
|
17572
17969
|
preset: { type: "string" },
|
|
17573
17970
|
bin: { type: "string" },
|
|
17574
17971
|
marketplace: { type: "boolean" },
|
|
17972
|
+
sandbox: { type: "boolean" },
|
|
17575
17973
|
"look-back": { type: "boolean" }
|
|
17576
17974
|
},
|
|
17577
17975
|
run: (ctx, args2) => {
|
|
@@ -17616,7 +18014,7 @@ var applyAllows = (findings, allows, decided) => {
|
|
|
17616
18014
|
|
|
17617
18015
|
// src/look/node-capture.ts
|
|
17618
18016
|
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
17619
|
-
import { join as
|
|
18017
|
+
import { join as join45 } from "node:path";
|
|
17620
18018
|
|
|
17621
18019
|
// src/look/measure.ts
|
|
17622
18020
|
var measurePage = (doc) => {
|
|
@@ -17627,8 +18025,8 @@ var measurePage = (doc) => {
|
|
|
17627
18025
|
const walker = doc.createTreeWalker(doc.body, SHOW_TEXT);
|
|
17628
18026
|
const keys = /* @__PURE__ */ new Set();
|
|
17629
18027
|
for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
|
|
17630
|
-
const
|
|
17631
|
-
if (KEY2.test(
|
|
18028
|
+
const text9 = (node.textContent ?? "").trim();
|
|
18029
|
+
if (KEY2.test(text9) && !NOT_KEY.test(text9) || PLACEHOLDER2.test(text9)) keys.add(text9.slice(0, 80));
|
|
17632
18030
|
}
|
|
17633
18031
|
const small = [...doc.querySelectorAll("a, button, input, select, textarea, [role=button], [role=link]")].filter((element) => {
|
|
17634
18032
|
const box = element.getBoundingClientRect();
|
|
@@ -17661,8 +18059,8 @@ var slugOf = (route) => {
|
|
|
17661
18059
|
var watch = (page) => {
|
|
17662
18060
|
const errors = [];
|
|
17663
18061
|
const failed4 = [];
|
|
17664
|
-
const push = (list4,
|
|
17665
|
-
if (list4.length < MAX_EVENTS) list4.push(
|
|
18062
|
+
const push = (list4, text9) => {
|
|
18063
|
+
if (list4.length < MAX_EVENTS) list4.push(text9.slice(0, 200));
|
|
17666
18064
|
};
|
|
17667
18065
|
page.on("console", (message) => {
|
|
17668
18066
|
if (message.type() === "error" && !RESOURCE_ERROR.test(message.text())) push(errors, message.text());
|
|
@@ -17692,7 +18090,7 @@ var captureOne = async (browser, request, route, viewport) => {
|
|
|
17692
18090
|
const context = await browser.newContext({ viewport: { width: viewport, height: HEIGHT }, reducedMotion: "reduce" });
|
|
17693
18091
|
const page = await context.newPage();
|
|
17694
18092
|
const events = watch(page);
|
|
17695
|
-
const path =
|
|
18093
|
+
const path = join45(request.dir, `${slugOf(route)}-${String(viewport)}.png`);
|
|
17696
18094
|
const loadError = await loadAndShoot(page, `${request.baseUrl}${route}`, path, request.timeoutMs);
|
|
17697
18095
|
const measure = loadError === void 0 ? parseMeasure(await page.evaluate(MEASURE_SCRIPT)) : void 0;
|
|
17698
18096
|
await context.close();
|
|
@@ -17709,7 +18107,7 @@ var captureAll = async (browser, request) => {
|
|
|
17709
18107
|
|
|
17710
18108
|
// src/look/node-playwright.ts
|
|
17711
18109
|
import { createRequire } from "node:module";
|
|
17712
|
-
import { join as
|
|
18110
|
+
import { join as join46 } from "node:path";
|
|
17713
18111
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
17714
18112
|
var NAMES = ["playwright", "playwright-core"];
|
|
17715
18113
|
var isPlaywright = (value) => isRecord(value) && isRecord(value["chromium"]) && typeof value["chromium"]["launch"] === "function";
|
|
@@ -17724,7 +18122,7 @@ var tryLoad = async (base, name) => {
|
|
|
17724
18122
|
}
|
|
17725
18123
|
};
|
|
17726
18124
|
var loadPlaywright = async (root) => {
|
|
17727
|
-
for (const base of [
|
|
18125
|
+
for (const base of [join46(root, "package.json"), fileURLToPath3(import.meta.url)]) {
|
|
17728
18126
|
for (const name of NAMES) {
|
|
17729
18127
|
const found = await tryLoad(base, name);
|
|
17730
18128
|
if (found !== void 0) return found;
|
|
@@ -17745,8 +18143,8 @@ var launchBrowser = async (playwright) => {
|
|
|
17745
18143
|
|
|
17746
18144
|
// src/look/node-sheet.ts
|
|
17747
18145
|
import { readFileSync as readFileSync2 } from "node:fs";
|
|
17748
|
-
import { join as
|
|
17749
|
-
var escapeHtml = (
|
|
18146
|
+
import { join as join47 } from "node:path";
|
|
18147
|
+
var escapeHtml = (text9) => text9.replace(/[&<>"]/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[char] ?? char);
|
|
17750
18148
|
var card = (result) => {
|
|
17751
18149
|
const image = (() => {
|
|
17752
18150
|
try {
|
|
@@ -17768,7 +18166,7 @@ var contactSheet = async (browser, results, dir) => {
|
|
|
17768
18166
|
if (shown3.length === 0) return void 0;
|
|
17769
18167
|
const page = await browser.newPage({ viewport: { width: 1600, height: 900 } });
|
|
17770
18168
|
await page.setContent(`<!doctype html><style>${STYLE}</style>${shown3.map(card).join("")}`);
|
|
17771
|
-
const path =
|
|
18169
|
+
const path = join47(dir, "contact-sheet.png");
|
|
17772
18170
|
await page.screenshot({ path, fullPage: true });
|
|
17773
18171
|
await page.close();
|
|
17774
18172
|
return path;
|
|
@@ -18108,12 +18506,12 @@ var proseCheckCommand = {
|
|
|
18108
18506
|
};
|
|
18109
18507
|
|
|
18110
18508
|
// src/review/packet.ts
|
|
18111
|
-
import { join as
|
|
18509
|
+
import { join as join48 } from "node:path";
|
|
18112
18510
|
var REVIEW_STATE_FILE = ".finitemonkey/review/state.json";
|
|
18113
18511
|
var FULL_ROUNDS = 3;
|
|
18114
18512
|
var REVIEW_FILE_MAX_LINES = 200;
|
|
18115
18513
|
var LEDGER_MAX_LINES = 100;
|
|
18116
|
-
var statePath = (root) => toAbsolutePath(
|
|
18514
|
+
var statePath = (root) => toAbsolutePath(join48(root, REVIEW_STATE_FILE));
|
|
18117
18515
|
var readReviewState = (runtime, root) => {
|
|
18118
18516
|
const raw = runtime.readText(statePath(root));
|
|
18119
18517
|
const parsed = raw.ok ? parseJsonObject(raw.value) : void 0;
|
|
@@ -18125,7 +18523,7 @@ var git = (runtime, root, args2) => {
|
|
|
18125
18523
|
const result = runtime.git(args2, root);
|
|
18126
18524
|
return result.ok ? result.stdout : void 0;
|
|
18127
18525
|
};
|
|
18128
|
-
var lastLines = (
|
|
18526
|
+
var lastLines = (text9, max) => text9.split("\n").slice(-max).join("\n");
|
|
18129
18527
|
var section = (title, body2, empty) => `## ${title}
|
|
18130
18528
|
|
|
18131
18529
|
${body2 === void 0 || body2.trim() === "" ? empty : body2.trimEnd()}
|
|
@@ -18135,7 +18533,7 @@ var taskSections = (runtime, root) => {
|
|
|
18135
18533
|
const active = readState(runtime, paths).active;
|
|
18136
18534
|
if (active === void 0)
|
|
18137
18535
|
return [section("Contract", void 0, "(no active task)"), section("Evidence ledger", void 0, "(none)")];
|
|
18138
|
-
const contract = runtime.readText(toAbsolutePath(
|
|
18536
|
+
const contract = runtime.readText(toAbsolutePath(join48(root, taskRelative(active))));
|
|
18139
18537
|
const ledger = runtime.readText(paths.evidence(active));
|
|
18140
18538
|
return [
|
|
18141
18539
|
section("Contract", contract.ok ? contract.value : void 0, "(contract file missing)"),
|
|
@@ -18143,8 +18541,8 @@ var taskSections = (runtime, root) => {
|
|
|
18143
18541
|
];
|
|
18144
18542
|
};
|
|
18145
18543
|
var reviewFile = (runtime, root, base) => {
|
|
18146
|
-
const
|
|
18147
|
-
return
|
|
18544
|
+
const text9 = git(runtime, root, ["show", `${base}:REVIEW.md`]);
|
|
18545
|
+
return text9 === void 0 ? void 0 : text9.split("\n").slice(0, REVIEW_FILE_MAX_LINES).join("\n");
|
|
18148
18546
|
};
|
|
18149
18547
|
var nextState = (previous, base, head) => {
|
|
18150
18548
|
if (previous?.base !== base) return { base, rounds: 1, lastHead: head };
|
|
@@ -18157,7 +18555,7 @@ var buildPacket = (runtime, root, base, head) => {
|
|
|
18157
18555
|
const range = `${from}...${head}`;
|
|
18158
18556
|
const diff = git(runtime, root, ["diff", "--no-color", "--no-ext-diff", range, "--", ".", ":(exclude).finitemonkey"]);
|
|
18159
18557
|
const owners = reviewFile(runtime, root, base);
|
|
18160
|
-
const
|
|
18558
|
+
const text9 = [
|
|
18161
18559
|
`# Review packet (round ${String(state.rounds)}, ${range})`,
|
|
18162
18560
|
"",
|
|
18163
18561
|
...state.rounds > FULL_ROUNDS ? ["Only the diff since the last review round is included.", ""] : [],
|
|
@@ -18165,10 +18563,10 @@ var buildPacket = (runtime, root, base, head) => {
|
|
|
18165
18563
|
...taskSections(runtime, root),
|
|
18166
18564
|
...owners === void 0 ? [] : [section("REVIEW.md (base branch)", owners, "")]
|
|
18167
18565
|
].join("\n");
|
|
18168
|
-
runtime.mkdir(toAbsolutePath(
|
|
18566
|
+
runtime.mkdir(toAbsolutePath(join48(root, ".finitemonkey", "review")));
|
|
18169
18567
|
runtime.writeAtomic(statePath(root), `${JSON.stringify(state, null, 2)}
|
|
18170
18568
|
`);
|
|
18171
|
-
return { round: state.rounds, base, head, range, text:
|
|
18569
|
+
return { round: state.rounds, base, head, range, text: text9 };
|
|
18172
18570
|
};
|
|
18173
18571
|
|
|
18174
18572
|
// src/commands/review.ts
|
|
@@ -18180,7 +18578,7 @@ var reviewPacketCommand = {
|
|
|
18180
18578
|
status: "ready",
|
|
18181
18579
|
options: { base: { type: "string" } },
|
|
18182
18580
|
run: (ctx, args2) => {
|
|
18183
|
-
const repo =
|
|
18581
|
+
const repo = repoOf(ctx);
|
|
18184
18582
|
if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
|
|
18185
18583
|
const base = stringOption(args2.values, "base") ?? repoFacts(ctx.runtime, repo).defaultBranch();
|
|
18186
18584
|
const resolved = ctx.runtime.git(["rev-parse", "--verify", "--quiet", `${base}^{commit}`], repo.root);
|
|
@@ -18320,7 +18718,7 @@ var secretsAcceptCommand = {
|
|
|
18320
18718
|
};
|
|
18321
18719
|
|
|
18322
18720
|
// src/secrets/runbooks.ts
|
|
18323
|
-
import { dirname as dirname9, join as
|
|
18721
|
+
import { dirname as dirname9, join as join49 } from "node:path";
|
|
18324
18722
|
var PROVIDER_OF_RULE = {
|
|
18325
18723
|
"aws-access-token": "aws",
|
|
18326
18724
|
"aws-secret-access-key": "aws",
|
|
@@ -18342,13 +18740,13 @@ var RUNBOOKS_URL = "https://github.com/gabrielvieiraf/finitemonkey/blob/main/run
|
|
|
18342
18740
|
var runbookProvider = (rule) => PROVIDER_OF_RULE[rule] ?? "generic";
|
|
18343
18741
|
var runbookLink = (runtime, rule) => {
|
|
18344
18742
|
const file = `${runbookProvider(rule)}.md`;
|
|
18345
|
-
const local = toAbsolutePath(
|
|
18743
|
+
const local = toAbsolutePath(join49(dirname9(dirname9(runtime.selfPath)), "runbooks", file));
|
|
18346
18744
|
return runtime.stat(local)?.kind === "file" ? local : `${RUNBOOKS_URL}/${file}`;
|
|
18347
18745
|
};
|
|
18348
18746
|
|
|
18349
18747
|
// src/commands/secrets-incident.ts
|
|
18350
18748
|
var INCIDENT_ACTIONS = ["list", "rotated", "close", "purge"];
|
|
18351
|
-
var
|
|
18749
|
+
var USAGE2 = "finitemonkey secrets incident [list | rotated <id> | close <id> | purge <id>]";
|
|
18352
18750
|
var TARGET = { rotated: "rotacionado", close: "fechado" };
|
|
18353
18751
|
var PURGE_STEPS = [
|
|
18354
18752
|
"secrets.purge.step1",
|
|
@@ -18406,7 +18804,7 @@ var move = (ctx, root, incident, action) => {
|
|
|
18406
18804
|
if (!canMove(incident.state, to))
|
|
18407
18805
|
return action === "close" ? notRotated(ctx, incident) : fail2(
|
|
18408
18806
|
ctx,
|
|
18409
|
-
usageProblem("invalid_arguments", "secrets.error.bad_transition",
|
|
18807
|
+
usageProblem("invalid_arguments", "secrets.error.bad_transition", USAGE2, {
|
|
18410
18808
|
id: incident.id,
|
|
18411
18809
|
from: incident.state,
|
|
18412
18810
|
to
|
|
@@ -18429,7 +18827,7 @@ var actionOf = (raw) => INCIDENT_ACTIONS.find((action) => action === (raw ?? "li
|
|
|
18429
18827
|
var secretsIncidentCommand = {
|
|
18430
18828
|
path: ["secrets", "incident"],
|
|
18431
18829
|
summaryKey: "secrets.command.incident",
|
|
18432
|
-
usage:
|
|
18830
|
+
usage: USAGE2,
|
|
18433
18831
|
status: "ready",
|
|
18434
18832
|
maxPositionals: 2,
|
|
18435
18833
|
run: (ctx, args2) => {
|
|
@@ -18439,7 +18837,7 @@ var secretsIncidentCommand = {
|
|
|
18439
18837
|
return Promise.resolve(
|
|
18440
18838
|
fail2(
|
|
18441
18839
|
ctx,
|
|
18442
|
-
usageProblem("invalid_arguments", "error.invalid_arguments",
|
|
18840
|
+
usageProblem("invalid_arguments", "error.invalid_arguments", USAGE2, { detail: args2.positionals[0] ?? "" })
|
|
18443
18841
|
)
|
|
18444
18842
|
);
|
|
18445
18843
|
if (action === "list") return Promise.resolve(list2(ctx, root));
|
|
@@ -18511,7 +18909,7 @@ var liveOutcome = (result) => {
|
|
|
18511
18909
|
|
|
18512
18910
|
// src/commands/secrets-live.ts
|
|
18513
18911
|
var SECRETS_LIVE_SCHEMA = "finitemonkey/secrets-live@1";
|
|
18514
|
-
var
|
|
18912
|
+
var USAGE3 = `finitemonkey secrets live <${LIVE_PROVIDERS.join("|")}> --env <VARIABLE> [--send]`;
|
|
18515
18913
|
var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
18516
18914
|
var target = (provider) => {
|
|
18517
18915
|
const probe = LIVE_PROBES[provider];
|
|
@@ -18542,7 +18940,7 @@ var send = (ctx, provider, variable, key) => {
|
|
|
18542
18940
|
var secretsLiveCommand = {
|
|
18543
18941
|
path: ["secrets", "live"],
|
|
18544
18942
|
summaryKey: "secrets.command.live",
|
|
18545
|
-
usage:
|
|
18943
|
+
usage: USAGE3,
|
|
18546
18944
|
status: "ready",
|
|
18547
18945
|
maxPositionals: 1,
|
|
18548
18946
|
options: { env: { type: "string" }, send: { type: "boolean" } },
|
|
@@ -18553,7 +18951,7 @@ var secretsLiveCommand = {
|
|
|
18553
18951
|
return Promise.resolve(
|
|
18554
18952
|
failureFrom(
|
|
18555
18953
|
ctx.t,
|
|
18556
|
-
usageProblem("invalid_arguments", "error.invalid_arguments",
|
|
18954
|
+
usageProblem("invalid_arguments", "error.invalid_arguments", USAGE3, {
|
|
18557
18955
|
detail: provider === void 0 ? args2.positionals[0] ?? "<provider>" : "--env <VARIABLE>"
|
|
18558
18956
|
})
|
|
18559
18957
|
)
|
|
@@ -18563,7 +18961,7 @@ var secretsLiveCommand = {
|
|
|
18563
18961
|
return Promise.resolve(
|
|
18564
18962
|
failureFrom(
|
|
18565
18963
|
ctx.t,
|
|
18566
|
-
needsPersonProblem("secrets.error.person_only", `${
|
|
18964
|
+
needsPersonProblem("secrets.error.person_only", `${USAGE3.replace(" [--send]", "")} --send`, {
|
|
18567
18965
|
action: ctx.t("secrets.action.live")
|
|
18568
18966
|
})
|
|
18569
18967
|
)
|
|
@@ -18571,7 +18969,7 @@ var secretsLiveCommand = {
|
|
|
18571
18969
|
const key = ctx.env[variable] ?? "";
|
|
18572
18970
|
if (key === "")
|
|
18573
18971
|
return Promise.resolve(
|
|
18574
|
-
failureFrom(ctx.t, usageProblem("invalid_arguments", "secrets.live.no_value",
|
|
18972
|
+
failureFrom(ctx.t, usageProblem("invalid_arguments", "secrets.live.no_value", USAGE3, { variable }))
|
|
18575
18973
|
);
|
|
18576
18974
|
return Promise.resolve(send(ctx, provider, variable, key));
|
|
18577
18975
|
}
|
|
@@ -18740,7 +19138,7 @@ var gateLight = (runtime, paths) => {
|
|
|
18740
19138
|
};
|
|
18741
19139
|
|
|
18742
19140
|
// src/statusline/input.ts
|
|
18743
|
-
var
|
|
19141
|
+
var text8 = (value) => typeof value === "string" && value !== "" ? value : void 0;
|
|
18744
19142
|
var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
18745
19143
|
var usedTokens = (usage) => {
|
|
18746
19144
|
if (!isRecord(usage)) return void 0;
|
|
@@ -18760,8 +19158,8 @@ var parseStatusInput = (raw) => {
|
|
|
18760
19158
|
const record3 = parsed.ok ? parsed.value : {};
|
|
18761
19159
|
const workspace = isRecord(record3["workspace"]) ? record3["workspace"] : {};
|
|
18762
19160
|
return {
|
|
18763
|
-
sessionId:
|
|
18764
|
-
dir:
|
|
19161
|
+
sessionId: text8(record3["session_id"]),
|
|
19162
|
+
dir: text8(workspace["project_dir"]) ?? text8(workspace["current_dir"]) ?? text8(record3["cwd"]),
|
|
18765
19163
|
contextPercent: contextPercent(record3),
|
|
18766
19164
|
raw
|
|
18767
19165
|
};
|
|
@@ -18776,7 +19174,7 @@ var segmentText = (t, segment) => [
|
|
|
18776
19174
|
...segment.contextPercent === void 0 ? [] : [t("statusline.context", { percent: segment.contextPercent })]
|
|
18777
19175
|
].join(" · ");
|
|
18778
19176
|
var ANSI3 = new RegExp(`${String.fromCharCode(27)}\\[[0-9;?]*[A-Za-z]`, "g");
|
|
18779
|
-
var graphemes = (
|
|
19177
|
+
var graphemes = (text9) => Array.from(new Intl.Segmenter(void 0, { granularity: "grapheme" }).segment(text9), (part2) => part2.segment);
|
|
18780
19178
|
var fit = (line2, columns) => {
|
|
18781
19179
|
if (columns === void 0) return line2;
|
|
18782
19180
|
const visible = graphemes(line2.replace(ANSI3, ""));
|
|
@@ -18837,16 +19235,16 @@ var cached2 = (runtime, paths) => {
|
|
|
18837
19235
|
const raw = readJsonFile(runtime, paths.cache(WRAP_CACHE))?.["sessions"];
|
|
18838
19236
|
return isRecord(raw) ? Object.fromEntries(Object.entries(raw).filter((entry2) => typeof entry2[1] === "string")) : {};
|
|
18839
19237
|
};
|
|
18840
|
-
var remember = (runtime, paths, session,
|
|
18841
|
-
const sessions = Object.entries({ ...cached2(runtime, paths), [session]:
|
|
19238
|
+
var remember = (runtime, paths, session, text9) => {
|
|
19239
|
+
const sessions = Object.entries({ ...cached2(runtime, paths), [session]: text9 }).slice(-KEEP3);
|
|
18842
19240
|
writeJsonFile(runtime, paths.cache(WRAP_CACHE), { sessions: Object.fromEntries(sessions) });
|
|
18843
19241
|
};
|
|
18844
19242
|
var runWrapped = async (runtime, paths, request, shell = nodeShell) => {
|
|
18845
19243
|
const result = await shell(request.command, request.input, request.cwd, request.timeoutMs);
|
|
18846
19244
|
if (result.text !== void 0) {
|
|
18847
|
-
const
|
|
18848
|
-
if (paths !== void 0) remember(runtime, paths, request.session,
|
|
18849
|
-
return
|
|
19245
|
+
const text9 = result.text.replace(/\s+$/, "");
|
|
19246
|
+
if (paths !== void 0) remember(runtime, paths, request.session, text9);
|
|
19247
|
+
return text9;
|
|
18850
19248
|
}
|
|
18851
19249
|
return paths === void 0 ? "" : cached2(runtime, paths)[request.session] ?? "";
|
|
18852
19250
|
};
|
|
@@ -18855,8 +19253,8 @@ var runWrapped = async (runtime, paths, request, shell = nodeShell) => {
|
|
|
18855
19253
|
var STDIN_MS = 300;
|
|
18856
19254
|
var WRAP_MS = 700;
|
|
18857
19255
|
var wrapOf = (ctx, root) => {
|
|
18858
|
-
const
|
|
18859
|
-
const lock =
|
|
19256
|
+
const text9 = ctx.runtime.readText(toAbsolutePath(`${root}/${LOCK_FILE}`));
|
|
19257
|
+
const lock = text9.ok ? parseLock(text9.value) : void 0;
|
|
18860
19258
|
if (!lock?.ok) return void 0;
|
|
18861
19259
|
return lock.value.entries.find((entry2) => entry2.added?.statusLine !== void 0)?.added?.statusLine?.wrap ?? void 0;
|
|
18862
19260
|
};
|
|
@@ -18902,7 +19300,7 @@ var statuslineCommand = {
|
|
|
18902
19300
|
};
|
|
18903
19301
|
|
|
18904
19302
|
// src/task/create.ts
|
|
18905
|
-
import { join as
|
|
19303
|
+
import { join as join50 } from "node:path";
|
|
18906
19304
|
|
|
18907
19305
|
// src/core/yaml/write.ts
|
|
18908
19306
|
var PLAIN_SAFE = /^[A-Za-z_./][A-Za-z0-9_./ ()-]*$/;
|
|
@@ -18991,10 +19389,10 @@ var explicitChecks = (draft) => [
|
|
|
18991
19389
|
var draftChecks = (runtime, paths, draft) => {
|
|
18992
19390
|
const disk = {
|
|
18993
19391
|
read: (relative11) => {
|
|
18994
|
-
const
|
|
18995
|
-
return
|
|
19392
|
+
const text9 = runtime.readText(toAbsolutePath(join50(paths.root, relative11)));
|
|
19393
|
+
return text9.ok ? text9.value : void 0;
|
|
18996
19394
|
},
|
|
18997
|
-
exists: (relative11) => runtime.stat(toAbsolutePath(
|
|
19395
|
+
exists: (relative11) => runtime.stat(toAbsolutePath(join50(paths.root, relative11))) !== void 0
|
|
18998
19396
|
};
|
|
18999
19397
|
const detected = detectChecks(disk);
|
|
19000
19398
|
const base = draft.runs.length > 0 || draft.tests !== void 0 ? explicitChecks(draft) : detected;
|
|
@@ -19009,7 +19407,7 @@ var draftChecks = (runtime, paths, draft) => {
|
|
|
19009
19407
|
{ id: "merge", kind: "merge" },
|
|
19010
19408
|
{ id: "callers", kind: "callers" },
|
|
19011
19409
|
...draft.paths.length > 0 || draft.budget !== void 0 ? [{ id: "diff", kind: "diff" }] : [],
|
|
19012
|
-
...runtime.stat(toAbsolutePath(
|
|
19410
|
+
...runtime.stat(toAbsolutePath(join50(paths.root, LOOK_CONFIG))) === void 0 ? [] : [{ id: "look", kind: "look" }],
|
|
19013
19411
|
...draft.git.length > 0 ? [{ id: "git", kind: "git", require: draft.git }] : [],
|
|
19014
19412
|
{ id: "pr", kind: "pr", requireOpen: draft.git.includes("pr_open") },
|
|
19015
19413
|
...draft.manual.map((description, index) => ({
|
|
@@ -19035,19 +19433,19 @@ var createTask = (runtime, paths, draft) => {
|
|
|
19035
19433
|
decisionsNeeded: [],
|
|
19036
19434
|
implicit: false
|
|
19037
19435
|
};
|
|
19038
|
-
const
|
|
19436
|
+
const text9 = stringifyYaml(contractYaml(shape));
|
|
19039
19437
|
const valid = validateContract(contractYaml(shape));
|
|
19040
19438
|
if (!valid.ok) return reject({ kind: "invalid", issues: valid.error });
|
|
19041
|
-
const wrote = runtime.writeText(paths.task(draft.id),
|
|
19439
|
+
const wrote = runtime.writeText(paths.task(draft.id), text9);
|
|
19042
19440
|
if (!wrote.ok) return reject({ kind: "write", detail: wrote.error });
|
|
19043
19441
|
const lock = readTrust(runtime, paths);
|
|
19044
19442
|
writeTrust(runtime, paths, {
|
|
19045
19443
|
...lock,
|
|
19046
19444
|
contracts: { ...lock.contracts, [draft.id]: contractHash(valid.value) },
|
|
19047
|
-
approved: { ...lock.approved, [draft.id]:
|
|
19445
|
+
approved: { ...lock.approved, [draft.id]: text9 }
|
|
19048
19446
|
});
|
|
19049
19447
|
writeState(runtime, paths, { ...readState(runtime, paths), active: draft.id });
|
|
19050
|
-
return accept({ contract: valid.value, text:
|
|
19448
|
+
return accept({ contract: valid.value, text: text9 });
|
|
19051
19449
|
};
|
|
19052
19450
|
|
|
19053
19451
|
// src/commands/task.ts
|
|
@@ -19198,7 +19596,7 @@ var taskDoneCommand = {
|
|
|
19198
19596
|
};
|
|
19199
19597
|
|
|
19200
19598
|
// src/install/githook-block.ts
|
|
19201
|
-
var withoutHookBlock = (
|
|
19599
|
+
var withoutHookBlock = (text9) => withoutManagedBlock(text9, { begin: BLOCK_BEGIN, end: BLOCK_END });
|
|
19202
19600
|
|
|
19203
19601
|
// src/install/uninstall.ts
|
|
19204
19602
|
var blockless = (kind, current2) => {
|
|
@@ -19207,8 +19605,12 @@ var blockless = (kind, current2) => {
|
|
|
19207
19605
|
};
|
|
19208
19606
|
var structural = (entry2, current2) => {
|
|
19209
19607
|
if (entry2.kind === "gitignore" || entry2.kind === "markdown" || entry2.kind === "githook") {
|
|
19210
|
-
const
|
|
19211
|
-
return
|
|
19608
|
+
const text9 = blockless(entry2.kind, current2);
|
|
19609
|
+
return text9.trim() === "" && !entry2.existed ? void 0 : text9;
|
|
19610
|
+
}
|
|
19611
|
+
if (entry2.kind === "sandbox") {
|
|
19612
|
+
const text9 = withoutSandbox(current2, entry2.sandbox ?? {});
|
|
19613
|
+
return text9 === void 0 && entry2.existed ? "{}\n" : text9;
|
|
19212
19614
|
}
|
|
19213
19615
|
const parsed = parseSettings(current2);
|
|
19214
19616
|
if (!parsed.ok || entry2.added === void 0) return current2;
|
|
@@ -19414,34 +19816,35 @@ var REGISTRY = [
|
|
|
19414
19816
|
lookCommand,
|
|
19415
19817
|
reviewPacketCommand,
|
|
19416
19818
|
statuslineCommand,
|
|
19819
|
+
critiqueVerifyCommand,
|
|
19417
19820
|
versionCommand,
|
|
19418
19821
|
helpCommand
|
|
19419
19822
|
];
|
|
19420
19823
|
|
|
19421
19824
|
// src/i18n/lang-settings.ts
|
|
19422
|
-
import { join as
|
|
19825
|
+
import { join as join51 } from "node:path";
|
|
19423
19826
|
var read3 = (runtime, path) => {
|
|
19424
|
-
const
|
|
19425
|
-
return
|
|
19827
|
+
const text9 = runtime.readText(toAbsolutePath(path));
|
|
19828
|
+
return text9.ok ? text9.value : void 0;
|
|
19426
19829
|
};
|
|
19427
19830
|
var configText = (tree, ...path) => {
|
|
19428
19831
|
const value = configAt(tree, ...path);
|
|
19429
19832
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
19430
19833
|
};
|
|
19431
|
-
var languageIn = (
|
|
19432
|
-
if (
|
|
19433
|
-
const parsed = parseJsonObject(
|
|
19834
|
+
var languageIn = (text9) => {
|
|
19835
|
+
if (text9 === void 0) return void 0;
|
|
19836
|
+
const parsed = parseJsonObject(text9);
|
|
19434
19837
|
const value = parsed.ok && isRecord(parsed.value) ? parsed.value["language"] : void 0;
|
|
19435
19838
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
19436
19839
|
};
|
|
19437
|
-
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ??
|
|
19840
|
+
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join51(runtime.homeDir(), ".claude");
|
|
19438
19841
|
var langSettings = (runtime, cwd, env, platform) => {
|
|
19439
19842
|
const root = findGitRoot(cwd, runtime) ?? cwd;
|
|
19440
|
-
const config = parseConfigYaml(read3(runtime,
|
|
19843
|
+
const config = parseConfigYaml(read3(runtime, join51(root, CONFIG_FILE2)) ?? "");
|
|
19441
19844
|
const claudeFiles = [
|
|
19442
|
-
|
|
19443
|
-
|
|
19444
|
-
|
|
19845
|
+
join51(root, ".claude", "settings.local.json"),
|
|
19846
|
+
join51(root, ".claude", "settings.json"),
|
|
19847
|
+
join51(claudeDir(runtime, env), "settings.json")
|
|
19445
19848
|
];
|
|
19446
19849
|
return {
|
|
19447
19850
|
config: configText(config, "lang"),
|
|
@@ -19528,8 +19931,8 @@ var resultFromThrown = (thrown, t) => {
|
|
|
19528
19931
|
};
|
|
19529
19932
|
var block3 = (items) => items.length > 0 ? `${items.join("\n")}
|
|
19530
19933
|
` : "";
|
|
19531
|
-
var write2 = (sink,
|
|
19532
|
-
if (
|
|
19934
|
+
var write2 = (sink, text9) => {
|
|
19935
|
+
if (text9 !== "") sink.write(maskText(text9));
|
|
19533
19936
|
};
|
|
19534
19937
|
var render2 = (ctx, command, result) => {
|
|
19535
19938
|
if (result.raw !== void 0) {
|
|
@@ -19598,9 +20001,9 @@ import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
|
19598
20001
|
var UNKNOWN_VERSION = "0.0.0-unknown";
|
|
19599
20002
|
var VERSION_SOURCES = ["../package.json", "../.claude-plugin/plugin.json"];
|
|
19600
20003
|
var versionAt = (readText, moduleUrl, relative11) => {
|
|
19601
|
-
const
|
|
19602
|
-
if (!
|
|
19603
|
-
const parsed = parseJson(
|
|
20004
|
+
const text9 = readText(toAbsolutePath(fileURLToPath4(new URL(relative11, moduleUrl))));
|
|
20005
|
+
if (!text9.ok) return void 0;
|
|
20006
|
+
const parsed = parseJson(text9.value);
|
|
19604
20007
|
if (!parsed.ok || !isRecord(parsed.value)) return void 0;
|
|
19605
20008
|
const version = parsed.value["version"];
|
|
19606
20009
|
return typeof version === "string" ? version : void 0;
|