finitemonkey 0.1.0-next.19 → 0.1.0-next.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/finitemonkey.mjs +737 -360
- package/package.json +1 -1
package/dist/finitemonkey.mjs
CHANGED
|
@@ -810,8 +810,8 @@ var parsePreset = (raw) => raw === void 0 ? "neutral" : PRESETS.find((preset) =>
|
|
|
810
810
|
var COMMITTED_STATE = ["config.yml", "runtime/", "tasks/", "rules.yml", "policy.yml", "findings/"];
|
|
811
811
|
|
|
812
812
|
// src/install/lock-file.ts
|
|
813
|
-
var ENTRY_KINDS = ["settings", "gitignore", "generated", "markdown"];
|
|
814
|
-
var strings = (value) => Array.isArray(value) && value.every((
|
|
813
|
+
var ENTRY_KINDS = ["settings", "gitignore", "generated", "markdown", "githook"];
|
|
814
|
+
var strings = (value) => Array.isArray(value) && value.every((item2) => typeof item2 === "string") ? value : void 0;
|
|
815
815
|
var parseAdded = (value) => {
|
|
816
816
|
if (!isRecord(value)) return void 0;
|
|
817
817
|
const deny2 = strings(value["deny"]);
|
|
@@ -828,7 +828,7 @@ var parseAdded = (value) => {
|
|
|
828
828
|
var parseEntry = (value) => {
|
|
829
829
|
if (!isRecord(value)) return void 0;
|
|
830
830
|
const { path, kind, existed, hash, backup } = value;
|
|
831
|
-
const known2 = ENTRY_KINDS.find((
|
|
831
|
+
const known2 = ENTRY_KINDS.find((item2) => item2 === kind);
|
|
832
832
|
if (typeof path !== "string" || known2 === void 0 || typeof existed !== "boolean" || typeof hash !== "string") {
|
|
833
833
|
return void 0;
|
|
834
834
|
}
|
|
@@ -851,7 +851,7 @@ var parseEntries = (value) => {
|
|
|
851
851
|
};
|
|
852
852
|
var fromRecord = (record3) => {
|
|
853
853
|
const { schema, version, preset, installedAt, updatedAt } = record3;
|
|
854
|
-
const known2 = PRESETS.find((
|
|
854
|
+
const known2 = PRESETS.find((item2) => item2 === preset);
|
|
855
855
|
const entries = parseEntries(record3["entries"]);
|
|
856
856
|
const createdDirs = strings(record3["createdDirs"]);
|
|
857
857
|
if (schema !== LOCK_SCHEMA) return reject(`schema is not ${LOCK_SCHEMA}`);
|
|
@@ -870,134 +870,6 @@ var parseLock = (text6) => {
|
|
|
870
870
|
var renderLock = (lock) => `${JSON.stringify(lock, null, 2)}
|
|
871
871
|
`;
|
|
872
872
|
|
|
873
|
-
// src/policy/policy.ts
|
|
874
|
-
import { join as join3 } from "node:path";
|
|
875
|
-
|
|
876
|
-
// src/git/project-config.ts
|
|
877
|
-
import { join as join2 } from "node:path";
|
|
878
|
-
var CONFIG_FILE2 = ".finitemonkey/config.yml";
|
|
879
|
-
var unquote = (value) => value.trim().replace(/^(["'])(.*)\1$/s, "$2");
|
|
880
|
-
var stripComment = (line2) => {
|
|
881
|
-
let quote;
|
|
882
|
-
for (let index = 0; index < line2.length; index += 1) {
|
|
883
|
-
const char = line2.charAt(index);
|
|
884
|
-
if (quote === void 0 && (char === '"' || char === "'")) quote = char;
|
|
885
|
-
else if (char === quote) quote = void 0;
|
|
886
|
-
else if (quote === void 0 && char === "#" && (index === 0 || /\s/.test(line2.charAt(index - 1)))) {
|
|
887
|
-
return line2.slice(0, index);
|
|
888
|
-
}
|
|
889
|
-
}
|
|
890
|
-
return line2;
|
|
891
|
-
};
|
|
892
|
-
var flowList = (value) => {
|
|
893
|
-
const trimmed = value.trim();
|
|
894
|
-
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
|
|
895
|
-
return trimmed.slice(1, -1).split(",").map(unquote).filter((item) => item !== "");
|
|
896
|
-
};
|
|
897
|
-
var keyValue = (body2) => {
|
|
898
|
-
const match = /^("[^"]*"|'[^']*'|[^:]+?)\s*:(?:\s+(.*))?$/.exec(body2);
|
|
899
|
-
if (match === null) return void 0;
|
|
900
|
-
return { key: unquote(match[1] ?? ""), value: match[2] ?? "" };
|
|
901
|
-
};
|
|
902
|
-
var addItem = (frame, item) => {
|
|
903
|
-
if (frame.key === void 0) return;
|
|
904
|
-
const parent = frame.tree[frame.key];
|
|
905
|
-
if (Array.isArray(parent)) parent.push(item);
|
|
906
|
-
else frame.tree[frame.key] = [item];
|
|
907
|
-
};
|
|
908
|
-
var placeLine = (stack, indent, body2) => {
|
|
909
|
-
while (stack.length > 1 && (stack[stack.length - 1]?.indent ?? 0) >= indent && !body2.startsWith("- ")) stack.pop();
|
|
910
|
-
const top = stack[stack.length - 1];
|
|
911
|
-
if (top === void 0) return;
|
|
912
|
-
if (body2.startsWith("- ")) {
|
|
913
|
-
addItem(top, unquote(body2.slice(2)));
|
|
914
|
-
return;
|
|
915
|
-
}
|
|
916
|
-
const pair = keyValue(body2);
|
|
917
|
-
if (pair === void 0) return;
|
|
918
|
-
const parentTree = top.key === void 0 ? top.tree : ensureTree(top.tree, top.key);
|
|
919
|
-
if (pair.value === "") {
|
|
920
|
-
stack.push({ indent, tree: parentTree, key: pair.key });
|
|
921
|
-
return;
|
|
922
|
-
}
|
|
923
|
-
parentTree[pair.key] = flowList(pair.value) ?? unquote(pair.value);
|
|
924
|
-
};
|
|
925
|
-
var ensureTree = (tree, key) => {
|
|
926
|
-
const existing = tree[key];
|
|
927
|
-
if (existing !== void 0 && typeof existing !== "string" && !Array.isArray(existing)) return existing;
|
|
928
|
-
const fresh3 = {};
|
|
929
|
-
tree[key] = fresh3;
|
|
930
|
-
return fresh3;
|
|
931
|
-
};
|
|
932
|
-
var parseConfigYaml = (text6) => {
|
|
933
|
-
const root = {};
|
|
934
|
-
const stack = [{ indent: -1, tree: root, key: void 0 }];
|
|
935
|
-
for (const raw of text6.split(/\r?\n/)) {
|
|
936
|
-
const line2 = stripComment(raw).trimEnd();
|
|
937
|
-
if (line2.trim() === "" || line2.trim() === "---") continue;
|
|
938
|
-
const indent = line2.length - line2.trimStart().length;
|
|
939
|
-
placeLine(stack, indent, line2.trim());
|
|
940
|
-
}
|
|
941
|
-
return root;
|
|
942
|
-
};
|
|
943
|
-
var isTree = (node) => typeof node === "object" && !Array.isArray(node);
|
|
944
|
-
var configAt = (tree, ...path) => path.reduce((node, key) => isTree(node) ? node[key] : void 0, tree);
|
|
945
|
-
var configList = (tree, ...path) => {
|
|
946
|
-
const node = configAt(tree, ...path);
|
|
947
|
-
if (typeof node === "string") return node === "" ? [] : [node];
|
|
948
|
-
return Array.isArray(node) ? node.filter((item) => typeof item === "string") : [];
|
|
949
|
-
};
|
|
950
|
-
var configMap = (tree, ...path) => {
|
|
951
|
-
const node = configAt(tree, ...path);
|
|
952
|
-
if (!isTree(node)) return {};
|
|
953
|
-
return Object.fromEntries(
|
|
954
|
-
Object.entries(node).filter((entry2) => typeof entry2[1] === "string")
|
|
955
|
-
);
|
|
956
|
-
};
|
|
957
|
-
var loadProjectConfig = (runtime, root) => {
|
|
958
|
-
const text6 = runtime.readText(toAbsolutePath(join2(root, CONFIG_FILE2)));
|
|
959
|
-
return text6.ok ? parseConfigYaml(text6.value) : {};
|
|
960
|
-
};
|
|
961
|
-
|
|
962
|
-
// src/policy/policy.ts
|
|
963
|
-
var POLICY_FILE = ".finitemonkey/policy.yml";
|
|
964
|
-
var GUARD_MODES = ["off", "shadow", "active"];
|
|
965
|
-
var LEVELS = ["light", "standard", "strict"];
|
|
966
|
-
var SEVERITIES = ["low", "medium", "high"];
|
|
967
|
-
var DEFAULT_PROMOTE_AFTER = 20;
|
|
968
|
-
var OPTIONAL_GUARDS = ["prose"];
|
|
969
|
-
var DEFAULT_POLICY = {
|
|
970
|
-
exists: false,
|
|
971
|
-
level: "standard",
|
|
972
|
-
severity: "medium",
|
|
973
|
-
promoteAfter: DEFAULT_PROMOTE_AFTER,
|
|
974
|
-
guards: {}
|
|
975
|
-
};
|
|
976
|
-
var pick = (options, value, fallback) => options.find((option) => option === value) ?? fallback;
|
|
977
|
-
var parsePolicy = (text6) => {
|
|
978
|
-
const tree = parseConfigYaml(text6);
|
|
979
|
-
const raw = configAt(tree, "promoteAfter");
|
|
980
|
-
const promoteAfter = typeof raw === "string" ? Number.parseInt(raw, 10) : Number.NaN;
|
|
981
|
-
const guards = Object.fromEntries(
|
|
982
|
-
Object.entries(configMap(tree, "guards")).flatMap(([id, mode]) => {
|
|
983
|
-
const known2 = GUARD_MODES.find((option) => option === mode);
|
|
984
|
-
return known2 === void 0 ? [] : [[id, known2]];
|
|
985
|
-
})
|
|
986
|
-
);
|
|
987
|
-
return {
|
|
988
|
-
exists: true,
|
|
989
|
-
level: pick(LEVELS, configAt(tree, "level"), DEFAULT_POLICY.level),
|
|
990
|
-
severity: pick(SEVERITIES, configAt(tree, "severity"), DEFAULT_POLICY.severity),
|
|
991
|
-
promoteAfter: Number.isInteger(promoteAfter) && promoteAfter > 0 ? promoteAfter : DEFAULT_PROMOTE_AFTER,
|
|
992
|
-
guards
|
|
993
|
-
};
|
|
994
|
-
};
|
|
995
|
-
var policyPath = (root) => toAbsolutePath(join3(root, POLICY_FILE));
|
|
996
|
-
var loadPolicy = (runtime, root) => {
|
|
997
|
-
const text6 = runtime.readText(policyPath(root));
|
|
998
|
-
return text6.ok ? parsePolicy(text6.value) : DEFAULT_POLICY;
|
|
999
|
-
};
|
|
1000
|
-
|
|
1001
873
|
// src/install/managed-block.ts
|
|
1002
874
|
var lineBreak = (text6) => text6.includes("\r\n") ? "\r\n" : "\n";
|
|
1003
875
|
var blockRange = (lines3, markers) => {
|
|
@@ -1147,7 +1019,7 @@ var hookHandler = (runner, spec2, failClosedSupported) => {
|
|
|
1147
1019
|
};
|
|
1148
1020
|
var OWN_SHELL = /(^|[\s/"'])finitemonkey(@[^\s"]+|\.mjs|\.cmd|\.exe)?"?\s+hook\s/;
|
|
1149
1021
|
var OWN_SCRIPT = /finitemonkey(\.mjs|\.cmd|\.exe)?$/;
|
|
1150
|
-
var stringsOf = (value) => Array.isArray(value) ? value.filter((
|
|
1022
|
+
var stringsOf = (value) => Array.isArray(value) ? value.filter((item2) => typeof item2 === "string") : [];
|
|
1151
1023
|
var isOwnHandler = (handler) => {
|
|
1152
1024
|
if (!isRecord(handler)) return false;
|
|
1153
1025
|
const command = handler["command"];
|
|
@@ -1187,6 +1059,218 @@ var verifySkillTemplate = (runner, lang) => {
|
|
|
1187
1059
|
};
|
|
1188
1060
|
var isOwnSkill = hasMarker;
|
|
1189
1061
|
|
|
1062
|
+
// src/githooks/install.ts
|
|
1063
|
+
var HOOK_MARKERS = { begin: BLOCK_BEGIN, end: BLOCK_END };
|
|
1064
|
+
var GIT_HOOK_NAMES = ["pre-commit", "commit-msg", "pre-push"];
|
|
1065
|
+
var LEFTHOOK_FILES = ["lefthook.yml", "lefthook.yaml", ".lefthook.yml", ".lefthook.yaml"];
|
|
1066
|
+
var PRE_COMMIT_FILE = ".pre-commit-config.yaml";
|
|
1067
|
+
var HOOK_ARGS = {
|
|
1068
|
+
"pre-commit": "secrets scan --staged",
|
|
1069
|
+
"commit-msg": 'commit-msg "$1"',
|
|
1070
|
+
"pre-push": 'push check "$1"'
|
|
1071
|
+
};
|
|
1072
|
+
var item = (path, before, after, proposal) => {
|
|
1073
|
+
const base = { path, kind: "githook", before };
|
|
1074
|
+
if (proposal !== void 0) return { ...base, action: "skip-foreign", after: before ?? "", proposal };
|
|
1075
|
+
if (before === void 0) return { ...base, action: "create", after };
|
|
1076
|
+
return { ...base, action: before === after ? "unchanged" : "merge", after };
|
|
1077
|
+
};
|
|
1078
|
+
var huskyItems = (project, runner) => GIT_HOOK_NAMES.map((hook) => {
|
|
1079
|
+
const path = `.husky/${hook}`;
|
|
1080
|
+
const before = project.read(path);
|
|
1081
|
+
return item(path, before, withManagedBlock(before, HOOK_MARKERS, [cliCommand(runner, HOOK_ARGS[hook])]));
|
|
1082
|
+
});
|
|
1083
|
+
var lefthookBody = (runner) => [
|
|
1084
|
+
"pre-commit:",
|
|
1085
|
+
" commands:",
|
|
1086
|
+
" finitemonkey-secrets:",
|
|
1087
|
+
` run: ${cliCommand(runner, "secrets scan --staged")}`,
|
|
1088
|
+
"commit-msg:",
|
|
1089
|
+
" commands:",
|
|
1090
|
+
" finitemonkey-commit-msg:",
|
|
1091
|
+
` run: ${cliCommand(runner, "commit-msg {1}")}`,
|
|
1092
|
+
"pre-push:",
|
|
1093
|
+
" commands:",
|
|
1094
|
+
" finitemonkey-push:",
|
|
1095
|
+
` run: ${cliCommand(runner, "push check {1}")}`,
|
|
1096
|
+
" use_stdin: true"
|
|
1097
|
+
];
|
|
1098
|
+
var definesHook = (text6) => GIT_HOOK_NAMES.some((hook) => new RegExp(`^${hook}:`, "m").test(withoutManagedBlock(text6, HOOK_MARKERS)));
|
|
1099
|
+
var lefthookItem = (project, runner, path) => {
|
|
1100
|
+
const before = project.read(path);
|
|
1101
|
+
const after = withManagedBlock(before, HOOK_MARKERS, lefthookBody(runner));
|
|
1102
|
+
return before !== void 0 && definesHook(before) ? item(path, before, after, after) : item(path, before, after);
|
|
1103
|
+
};
|
|
1104
|
+
var preCommitBody = (runner, indent) => {
|
|
1105
|
+
const hook = (id, args2, stage) => [
|
|
1106
|
+
`${indent} - id: ${id}`,
|
|
1107
|
+
`${indent} name: ${id}`,
|
|
1108
|
+
`${indent} entry: ${cliCommand(runner, args2)}`,
|
|
1109
|
+
`${indent} language: system`,
|
|
1110
|
+
`${indent} pass_filenames: ${stage === "commit-msg" ? "true" : "false"}`,
|
|
1111
|
+
`${indent} always_run: true`,
|
|
1112
|
+
`${indent} stages: [${stage}]`
|
|
1113
|
+
];
|
|
1114
|
+
return [
|
|
1115
|
+
`${indent}- repo: local`,
|
|
1116
|
+
`${indent} hooks:`,
|
|
1117
|
+
...hook("finitemonkey-secrets", "secrets scan --staged", "pre-commit"),
|
|
1118
|
+
...hook("finitemonkey-commit-msg", "commit-msg", "commit-msg"),
|
|
1119
|
+
...hook("finitemonkey-push", "push check", "pre-push")
|
|
1120
|
+
];
|
|
1121
|
+
};
|
|
1122
|
+
var TOP_KEY = /^[A-Za-z_][\w-]*:/;
|
|
1123
|
+
var reposIsLast = (text6) => {
|
|
1124
|
+
const keys = withoutManagedBlock(text6, HOOK_MARKERS).split(/\r?\n/).filter((line2) => TOP_KEY.test(line2));
|
|
1125
|
+
return keys.at(-1)?.startsWith("repos:") === true;
|
|
1126
|
+
};
|
|
1127
|
+
var preCommitItem = (project, runner) => {
|
|
1128
|
+
const before = project.read(PRE_COMMIT_FILE) ?? "";
|
|
1129
|
+
const indent = /^( *)- repo:/m.exec(before)?.[1] ?? " ";
|
|
1130
|
+
const after = withManagedBlock(before, HOOK_MARKERS, preCommitBody(runner, indent));
|
|
1131
|
+
return reposIsLast(before) ? item(PRE_COMMIT_FILE, before, after) : item(PRE_COMMIT_FILE, before, after, after);
|
|
1132
|
+
};
|
|
1133
|
+
var usesHusky = (project) => {
|
|
1134
|
+
if (project.isDir(".husky")) return true;
|
|
1135
|
+
return /"husky"\s*:/.test(project.read("package.json") ?? "");
|
|
1136
|
+
};
|
|
1137
|
+
var gitHookItems = (project, runner) => {
|
|
1138
|
+
const lefthook = LEFTHOOK_FILES.find((path) => project.exists(path));
|
|
1139
|
+
return [
|
|
1140
|
+
...usesHusky(project) ? huskyItems(project, runner) : [],
|
|
1141
|
+
...lefthook === void 0 ? [] : [lefthookItem(project, runner, lefthook)],
|
|
1142
|
+
...project.exists(PRE_COMMIT_FILE) ? [preCommitItem(project, runner)] : []
|
|
1143
|
+
];
|
|
1144
|
+
};
|
|
1145
|
+
|
|
1146
|
+
// src/policy/policy.ts
|
|
1147
|
+
import { join as join3 } from "node:path";
|
|
1148
|
+
|
|
1149
|
+
// src/git/project-config.ts
|
|
1150
|
+
import { join as join2 } from "node:path";
|
|
1151
|
+
var CONFIG_FILE2 = ".finitemonkey/config.yml";
|
|
1152
|
+
var unquote = (value) => value.trim().replace(/^(["'])(.*)\1$/s, "$2");
|
|
1153
|
+
var stripComment = (line2) => {
|
|
1154
|
+
let quote;
|
|
1155
|
+
for (let index = 0; index < line2.length; index += 1) {
|
|
1156
|
+
const char = line2.charAt(index);
|
|
1157
|
+
if (quote === void 0 && (char === '"' || char === "'")) quote = char;
|
|
1158
|
+
else if (char === quote) quote = void 0;
|
|
1159
|
+
else if (quote === void 0 && char === "#" && (index === 0 || /\s/.test(line2.charAt(index - 1)))) {
|
|
1160
|
+
return line2.slice(0, index);
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
return line2;
|
|
1164
|
+
};
|
|
1165
|
+
var flowList = (value) => {
|
|
1166
|
+
const trimmed = value.trim();
|
|
1167
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
|
|
1168
|
+
return trimmed.slice(1, -1).split(",").map(unquote).filter((item2) => item2 !== "");
|
|
1169
|
+
};
|
|
1170
|
+
var keyValue = (body2) => {
|
|
1171
|
+
const match = /^("[^"]*"|'[^']*'|[^:]+?)\s*:(?:\s+(.*))?$/.exec(body2);
|
|
1172
|
+
if (match === null) return void 0;
|
|
1173
|
+
return { key: unquote(match[1] ?? ""), value: match[2] ?? "" };
|
|
1174
|
+
};
|
|
1175
|
+
var addItem = (frame, item2) => {
|
|
1176
|
+
if (frame.key === void 0) return;
|
|
1177
|
+
const parent = frame.tree[frame.key];
|
|
1178
|
+
if (Array.isArray(parent)) parent.push(item2);
|
|
1179
|
+
else frame.tree[frame.key] = [item2];
|
|
1180
|
+
};
|
|
1181
|
+
var placeLine = (stack, indent, body2) => {
|
|
1182
|
+
while (stack.length > 1 && (stack[stack.length - 1]?.indent ?? 0) >= indent && !body2.startsWith("- ")) stack.pop();
|
|
1183
|
+
const top = stack[stack.length - 1];
|
|
1184
|
+
if (top === void 0) return;
|
|
1185
|
+
if (body2.startsWith("- ")) {
|
|
1186
|
+
addItem(top, unquote(body2.slice(2)));
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
const pair = keyValue(body2);
|
|
1190
|
+
if (pair === void 0) return;
|
|
1191
|
+
const parentTree = top.key === void 0 ? top.tree : ensureTree(top.tree, top.key);
|
|
1192
|
+
if (pair.value === "") {
|
|
1193
|
+
stack.push({ indent, tree: parentTree, key: pair.key });
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
parentTree[pair.key] = flowList(pair.value) ?? unquote(pair.value);
|
|
1197
|
+
};
|
|
1198
|
+
var ensureTree = (tree, key) => {
|
|
1199
|
+
const existing = tree[key];
|
|
1200
|
+
if (existing !== void 0 && typeof existing !== "string" && !Array.isArray(existing)) return existing;
|
|
1201
|
+
const fresh3 = {};
|
|
1202
|
+
tree[key] = fresh3;
|
|
1203
|
+
return fresh3;
|
|
1204
|
+
};
|
|
1205
|
+
var parseConfigYaml = (text6) => {
|
|
1206
|
+
const root = {};
|
|
1207
|
+
const stack = [{ indent: -1, tree: root, key: void 0 }];
|
|
1208
|
+
for (const raw of text6.split(/\r?\n/)) {
|
|
1209
|
+
const line2 = stripComment(raw).trimEnd();
|
|
1210
|
+
if (line2.trim() === "" || line2.trim() === "---") continue;
|
|
1211
|
+
const indent = line2.length - line2.trimStart().length;
|
|
1212
|
+
placeLine(stack, indent, line2.trim());
|
|
1213
|
+
}
|
|
1214
|
+
return root;
|
|
1215
|
+
};
|
|
1216
|
+
var isTree = (node) => typeof node === "object" && !Array.isArray(node);
|
|
1217
|
+
var configAt = (tree, ...path) => path.reduce((node, key) => isTree(node) ? node[key] : void 0, tree);
|
|
1218
|
+
var configList = (tree, ...path) => {
|
|
1219
|
+
const node = configAt(tree, ...path);
|
|
1220
|
+
if (typeof node === "string") return node === "" ? [] : [node];
|
|
1221
|
+
return Array.isArray(node) ? node.filter((item2) => typeof item2 === "string") : [];
|
|
1222
|
+
};
|
|
1223
|
+
var configMap = (tree, ...path) => {
|
|
1224
|
+
const node = configAt(tree, ...path);
|
|
1225
|
+
if (!isTree(node)) return {};
|
|
1226
|
+
return Object.fromEntries(
|
|
1227
|
+
Object.entries(node).filter((entry2) => typeof entry2[1] === "string")
|
|
1228
|
+
);
|
|
1229
|
+
};
|
|
1230
|
+
var loadProjectConfig = (runtime, root) => {
|
|
1231
|
+
const text6 = runtime.readText(toAbsolutePath(join2(root, CONFIG_FILE2)));
|
|
1232
|
+
return text6.ok ? parseConfigYaml(text6.value) : {};
|
|
1233
|
+
};
|
|
1234
|
+
|
|
1235
|
+
// src/policy/policy.ts
|
|
1236
|
+
var POLICY_FILE = ".finitemonkey/policy.yml";
|
|
1237
|
+
var GUARD_MODES = ["off", "shadow", "active"];
|
|
1238
|
+
var LEVELS = ["light", "standard", "strict"];
|
|
1239
|
+
var SEVERITIES = ["low", "medium", "high"];
|
|
1240
|
+
var DEFAULT_PROMOTE_AFTER = 20;
|
|
1241
|
+
var OPTIONAL_GUARDS = ["prose"];
|
|
1242
|
+
var DEFAULT_POLICY = {
|
|
1243
|
+
exists: false,
|
|
1244
|
+
level: "standard",
|
|
1245
|
+
severity: "medium",
|
|
1246
|
+
promoteAfter: DEFAULT_PROMOTE_AFTER,
|
|
1247
|
+
guards: {}
|
|
1248
|
+
};
|
|
1249
|
+
var pick = (options, value, fallback) => options.find((option) => option === value) ?? fallback;
|
|
1250
|
+
var parsePolicy = (text6) => {
|
|
1251
|
+
const tree = parseConfigYaml(text6);
|
|
1252
|
+
const raw = configAt(tree, "promoteAfter");
|
|
1253
|
+
const promoteAfter = typeof raw === "string" ? Number.parseInt(raw, 10) : Number.NaN;
|
|
1254
|
+
const guards = Object.fromEntries(
|
|
1255
|
+
Object.entries(configMap(tree, "guards")).flatMap(([id, mode]) => {
|
|
1256
|
+
const known2 = GUARD_MODES.find((option) => option === mode);
|
|
1257
|
+
return known2 === void 0 ? [] : [[id, known2]];
|
|
1258
|
+
})
|
|
1259
|
+
);
|
|
1260
|
+
return {
|
|
1261
|
+
exists: true,
|
|
1262
|
+
level: pick(LEVELS, configAt(tree, "level"), DEFAULT_POLICY.level),
|
|
1263
|
+
severity: pick(SEVERITIES, configAt(tree, "severity"), DEFAULT_POLICY.severity),
|
|
1264
|
+
promoteAfter: Number.isInteger(promoteAfter) && promoteAfter > 0 ? promoteAfter : DEFAULT_PROMOTE_AFTER,
|
|
1265
|
+
guards
|
|
1266
|
+
};
|
|
1267
|
+
};
|
|
1268
|
+
var policyPath = (root) => toAbsolutePath(join3(root, POLICY_FILE));
|
|
1269
|
+
var loadPolicy = (runtime, root) => {
|
|
1270
|
+
const text6 = runtime.readText(policyPath(root));
|
|
1271
|
+
return text6.ok ? parsePolicy(text6.value) : DEFAULT_POLICY;
|
|
1272
|
+
};
|
|
1273
|
+
|
|
1190
1274
|
// src/install/agents-block.ts
|
|
1191
1275
|
var MARKDOWN_MARKERS = { begin: MD_BLOCK_BEGIN, end: MD_BLOCK_END };
|
|
1192
1276
|
var IMPORT_LINE = /^@AGENTS\.md[ \t]*$/m;
|
|
@@ -1461,26 +1545,26 @@ var pendingMigrations = (migrations, from, to) => {
|
|
|
1461
1545
|
return version === void 0 ? [] : [{ migration, version }];
|
|
1462
1546
|
}).filter(({ version }) => compareSemver(version, low) > 0 && compareSemver(version, high) <= 0).sort((a, b) => compareSemver(a.version, b.version)).map(({ migration }) => migration);
|
|
1463
1547
|
};
|
|
1464
|
-
var migrateItem = (
|
|
1465
|
-
if (steps.length === 0 ||
|
|
1466
|
-
const migrated = steps.reduce((text6, step3) => step3(text6),
|
|
1467
|
-
if (migrated ===
|
|
1468
|
-
const recorded = previous.entries.find((entry2) => entry2.path ===
|
|
1469
|
-
if (recorded !== hashText(
|
|
1470
|
-
return { ...
|
|
1548
|
+
var migrateItem = (item2, previous, steps) => {
|
|
1549
|
+
if (steps.length === 0 || item2.before === void 0) return item2;
|
|
1550
|
+
const migrated = steps.reduce((text6, step3) => step3(text6), item2.after);
|
|
1551
|
+
if (migrated === item2.after) return item2;
|
|
1552
|
+
const recorded = previous.entries.find((entry2) => entry2.path === item2.path)?.hash;
|
|
1553
|
+
if (recorded !== hashText(item2.before)) {
|
|
1554
|
+
return { ...item2, action: "keep-edited", after: item2.before, proposal: migrated };
|
|
1471
1555
|
}
|
|
1472
|
-
return { ...
|
|
1556
|
+
return { ...item2, action: migrated === item2.before ? "unchanged" : "update", after: migrated };
|
|
1473
1557
|
};
|
|
1474
1558
|
var applyMigrations = (items, previous, version, migrations) => {
|
|
1475
1559
|
if (previous === void 0) return items;
|
|
1476
1560
|
const pending = pendingMigrations(migrations, previous.version, version);
|
|
1477
1561
|
if (pending.length === 0) return items;
|
|
1478
1562
|
return items.map(
|
|
1479
|
-
(
|
|
1480
|
-
|
|
1563
|
+
(item2) => migrateItem(
|
|
1564
|
+
item2,
|
|
1481
1565
|
previous,
|
|
1482
1566
|
pending.flatMap(
|
|
1483
|
-
(migration) => migration.files.filter((file) => file.path ===
|
|
1567
|
+
(migration) => migration.files.filter((file) => file.path === item2.path).map((file) => file.migrate)
|
|
1484
1568
|
)
|
|
1485
1569
|
)
|
|
1486
1570
|
);
|
|
@@ -1536,8 +1620,8 @@ var mergeHooks = (hooks, isOwn, wanted) => {
|
|
|
1536
1620
|
const entries = events.flatMap((event) => {
|
|
1537
1621
|
const groups = hooks[event];
|
|
1538
1622
|
if (groups !== void 0 && !Array.isArray(groups)) return [[event, groups]];
|
|
1539
|
-
const
|
|
1540
|
-
return
|
|
1623
|
+
const merged2 = mergeEvent(asList(groups), isOwn, wanted[event]);
|
|
1624
|
+
return merged2.length === 0 ? [] : [[event, merged2]];
|
|
1541
1625
|
});
|
|
1542
1626
|
return Object.fromEntries(entries);
|
|
1543
1627
|
};
|
|
@@ -1673,15 +1757,15 @@ var settingsItem = (project, detection, input, previous) => {
|
|
|
1673
1757
|
marketplace: input.marketplace,
|
|
1674
1758
|
language: input.claudeLanguage
|
|
1675
1759
|
};
|
|
1676
|
-
const
|
|
1677
|
-
const after = renderJson(
|
|
1760
|
+
const merged2 = mergeSettings(current2.value, wanted, previous?.handlers ?? []);
|
|
1761
|
+
const after = renderJson(merged2.settings, before === void 0 ? DEFAULT_STYLE : styleOf(before));
|
|
1678
1762
|
return accept({
|
|
1679
1763
|
path: SETTINGS_FILE,
|
|
1680
1764
|
kind: "settings",
|
|
1681
1765
|
action: actionFor(before, after, "merge"),
|
|
1682
1766
|
before,
|
|
1683
1767
|
after,
|
|
1684
|
-
added: joinAdded(previous?.added,
|
|
1768
|
+
added: joinAdded(previous?.added, merged2.added),
|
|
1685
1769
|
handlers: Object.values(hooks)
|
|
1686
1770
|
});
|
|
1687
1771
|
};
|
|
@@ -1733,7 +1817,13 @@ var buildPlan = (project, detection, input, previous, migrations = MIGRATIONS) =
|
|
|
1733
1817
|
detection,
|
|
1734
1818
|
preset: input.preset,
|
|
1735
1819
|
items: applyMigrations(
|
|
1736
|
-
[
|
|
1820
|
+
[
|
|
1821
|
+
settings.value,
|
|
1822
|
+
gitignoreItem(project),
|
|
1823
|
+
...markdownItems(project, detection.runner, input.lang),
|
|
1824
|
+
...generated,
|
|
1825
|
+
...gitHookItems(project, detection.runner)
|
|
1826
|
+
],
|
|
1737
1827
|
previous,
|
|
1738
1828
|
input.version,
|
|
1739
1829
|
migrations
|
|
@@ -1741,7 +1831,7 @@ var buildPlan = (project, detection, input, previous, migrations = MIGRATIONS) =
|
|
|
1741
1831
|
previous
|
|
1742
1832
|
});
|
|
1743
1833
|
};
|
|
1744
|
-
var hasChanges = (plan) => plan.items.some((
|
|
1834
|
+
var hasChanges = (plan) => plan.items.some((item2) => CHANGING.includes(item2.action));
|
|
1745
1835
|
var needsWrite = (plan) => hasChanges(plan) || plan.previous === void 0;
|
|
1746
1836
|
|
|
1747
1837
|
// src/install/apply.ts
|
|
@@ -1753,33 +1843,33 @@ var writeText = (project, relative9, text6) => {
|
|
|
1753
1843
|
return written.ok ? accept(void 0) : reject({ path: relative9, detail: written.error });
|
|
1754
1844
|
};
|
|
1755
1845
|
var restore = (project, items) => {
|
|
1756
|
-
for (const
|
|
1757
|
-
if (
|
|
1758
|
-
else project.runtime.writeAtomic(project.abs(
|
|
1846
|
+
for (const item2 of items) {
|
|
1847
|
+
if (item2.before === void 0) project.runtime.remove(project.abs(item2.path));
|
|
1848
|
+
else project.runtime.writeAtomic(project.abs(item2.path), item2.before);
|
|
1759
1849
|
}
|
|
1760
1850
|
};
|
|
1761
1851
|
var backupItems = (project, dir, items) => {
|
|
1762
|
-
const manifest = items.map((
|
|
1763
|
-
for (const
|
|
1764
|
-
const saved = writeText(project, `${dir}/${
|
|
1852
|
+
const manifest = items.map((item2) => ({ path: item2.path, hash: hashText(item2.before ?? "") }));
|
|
1853
|
+
for (const item2 of items) {
|
|
1854
|
+
const saved = writeText(project, `${dir}/${item2.path}`, item2.before ?? "");
|
|
1765
1855
|
if (!saved.ok) return saved;
|
|
1766
1856
|
}
|
|
1767
1857
|
return writeText(project, `${dir}/manifest.json`, `${JSON.stringify(manifest, null, 2)}
|
|
1768
1858
|
`);
|
|
1769
1859
|
};
|
|
1770
|
-
var entryOf = (plan,
|
|
1771
|
-
const previous = plan.previous?.entries.find((entry2) => entry2.path ===
|
|
1772
|
-
const existed = previous?.existed ??
|
|
1773
|
-
const backup = previous?.backup ?? (existed && backupDir !== void 0 ? `${backupDir}/${
|
|
1774
|
-
const hash =
|
|
1860
|
+
var entryOf = (plan, item2, backupDir) => {
|
|
1861
|
+
const previous = plan.previous?.entries.find((entry2) => entry2.path === item2.path);
|
|
1862
|
+
const existed = previous?.existed ?? item2.before !== void 0;
|
|
1863
|
+
const backup = previous?.backup ?? (existed && backupDir !== void 0 ? `${backupDir}/${item2.path}` : void 0);
|
|
1864
|
+
const hash = item2.action === "keep-edited" ? previous?.hash ?? hashText(item2.proposal ?? item2.after) : hashText(item2.after);
|
|
1775
1865
|
return {
|
|
1776
|
-
path:
|
|
1777
|
-
kind:
|
|
1866
|
+
path: item2.path,
|
|
1867
|
+
kind: item2.kind,
|
|
1778
1868
|
existed,
|
|
1779
1869
|
hash,
|
|
1780
1870
|
...backup === void 0 ? {} : { backup },
|
|
1781
|
-
...
|
|
1782
|
-
...
|
|
1871
|
+
...item2.added === void 0 ? {} : { added: item2.added },
|
|
1872
|
+
...item2.handlers === void 0 ? {} : { handlers: item2.handlers }
|
|
1783
1873
|
};
|
|
1784
1874
|
};
|
|
1785
1875
|
var nextLock = (plan, version, now, backupDir, createdDirs, only) => ({
|
|
@@ -1789,22 +1879,22 @@ var nextLock = (plan, version, now, backupDir, createdDirs, only) => ({
|
|
|
1789
1879
|
installedAt: plan.previous?.installedAt ?? now,
|
|
1790
1880
|
updatedAt: now,
|
|
1791
1881
|
createdDirs: [.../* @__PURE__ */ new Set([...plan.previous?.createdDirs ?? [], ...createdDirs])],
|
|
1792
|
-
entries: plan.items.filter((
|
|
1793
|
-
if (only === void 0 || only.includes(
|
|
1794
|
-
const kept = plan.previous?.entries.find((entry2) => entry2.path ===
|
|
1882
|
+
entries: plan.items.filter((item2) => item2.action !== "skip-foreign").flatMap((item2) => {
|
|
1883
|
+
if (only === void 0 || only.includes(item2.path)) return [entryOf(plan, item2, backupDir)];
|
|
1884
|
+
const kept = plan.previous?.entries.find((entry2) => entry2.path === item2.path);
|
|
1795
1885
|
return kept === void 0 ? [] : [kept];
|
|
1796
1886
|
})
|
|
1797
1887
|
});
|
|
1798
|
-
var needsBackup = (plan,
|
|
1888
|
+
var needsBackup = (plan, item2) => item2.before !== void 0 && plan.previous?.entries.find((entry2) => entry2.path === item2.path)?.backup === void 0;
|
|
1799
1889
|
var writeAll = (project, items) => {
|
|
1800
1890
|
const done = [];
|
|
1801
|
-
for (const
|
|
1802
|
-
const written = writeText(project,
|
|
1891
|
+
for (const item2 of items) {
|
|
1892
|
+
const written = writeText(project, item2.path, item2.after);
|
|
1803
1893
|
if (!written.ok) {
|
|
1804
1894
|
restore(project, done);
|
|
1805
1895
|
return written;
|
|
1806
1896
|
}
|
|
1807
|
-
done.push(
|
|
1897
|
+
done.push(item2);
|
|
1808
1898
|
}
|
|
1809
1899
|
return accept(void 0);
|
|
1810
1900
|
};
|
|
@@ -1813,9 +1903,9 @@ var createdBy = (project) => OWN_DIRS.filter((dir) => !project.exists(dir));
|
|
|
1813
1903
|
var commit = (project, plan, version, createdDirs, only) => {
|
|
1814
1904
|
const now = project.runtime.now();
|
|
1815
1905
|
const changing = plan.items.filter(
|
|
1816
|
-
(
|
|
1906
|
+
(item2) => CHANGING.includes(item2.action) && (only === void 0 || only.includes(item2.path))
|
|
1817
1907
|
);
|
|
1818
|
-
const toBackup = changing.filter((
|
|
1908
|
+
const toBackup = changing.filter((item2) => needsBackup(plan, item2));
|
|
1819
1909
|
const backupDir = toBackup.length === 0 ? void 0 : `${BACKUPS_DIR}/${stamp(now)}`;
|
|
1820
1910
|
if (backupDir !== void 0) {
|
|
1821
1911
|
const saved = backupItems(project, backupDir, toBackup);
|
|
@@ -1829,7 +1919,7 @@ var commit = (project, plan, version, createdDirs, only) => {
|
|
|
1829
1919
|
restore(project, changing);
|
|
1830
1920
|
return locked;
|
|
1831
1921
|
}
|
|
1832
|
-
return accept({ lock, written: changing.map((
|
|
1922
|
+
return accept({ lock, written: changing.map((item2) => item2.path), backup: backupDir });
|
|
1833
1923
|
};
|
|
1834
1924
|
var withInstallLock = (project, work) => {
|
|
1835
1925
|
const dir = project.runtime.mkdir(project.abs(LOCKS_DIR));
|
|
@@ -1998,9 +2088,9 @@ var applyFixes = (ctx, write3) => {
|
|
|
1998
2088
|
const input = planInputFor(ctx, previous.value.preset, void 0, false);
|
|
1999
2089
|
const plan = buildPlan(project, detect(project, gitRoot2, input), input, previous.value);
|
|
2000
2090
|
if (!plan.ok) return reject({ kind: "plan", error: plan.error });
|
|
2001
|
-
const pending = plan.value.items.filter((
|
|
2091
|
+
const pending = plan.value.items.filter((item2) => CHANGING.includes(item2.action) && fixable.includes(item2.path));
|
|
2002
2092
|
if (pending.length === 0 || !write3) {
|
|
2003
|
-
return accept({ installed: true, written: pending.map((
|
|
2093
|
+
return accept({ installed: true, written: pending.map((item2) => item2.path), backup: void 0 });
|
|
2004
2094
|
}
|
|
2005
2095
|
const applied = applyPlan(project, plan.value, ctx.version, fixable);
|
|
2006
2096
|
if (!applied.ok) return reject({ kind: "apply", error: applied.error });
|
|
@@ -2870,7 +2960,7 @@ var cleanupPeriod = (env, all) => {
|
|
|
2870
2960
|
};
|
|
2871
2961
|
var PRECEDENCE = ["user", "project", "local", "managed"];
|
|
2872
2962
|
var pluginOn = (all, id) => {
|
|
2873
|
-
const ordered = PRECEDENCE.flatMap((scope2) => all.filter((
|
|
2963
|
+
const ordered = PRECEDENCE.flatMap((scope2) => all.filter((item2) => item2.scope === scope2));
|
|
2874
2964
|
const values = ordered.map(({ settings }) => settings["enabledPlugins"]).map((plugins) => isRecord(plugins) ? plugins[id] : void 0).filter((value) => typeof value === "boolean");
|
|
2875
2965
|
return values.at(-1) === true;
|
|
2876
2966
|
};
|
|
@@ -3079,7 +3169,7 @@ var readState = (runtime, paths) => {
|
|
|
3079
3169
|
const snapshot2 = raw?.["snapshot"];
|
|
3080
3170
|
return {
|
|
3081
3171
|
...typeof active === "string" ? { active } : {},
|
|
3082
|
-
done: Array.isArray(done) ? done.filter((
|
|
3172
|
+
done: Array.isArray(done) ? done.filter((item2) => typeof item2 === "string") : [],
|
|
3083
3173
|
...blocks === void 0 ? {} : { blocks },
|
|
3084
3174
|
...isRecord(snapshot2) ? { snapshot: snapshot2 } : {}
|
|
3085
3175
|
};
|
|
@@ -3139,8 +3229,8 @@ var toApproval = (value) => {
|
|
|
3139
3229
|
if (task === void 0 || check2 === void 0 || at === void 0) return void 0;
|
|
3140
3230
|
return { task, check: check2, at, reason: text2(value, "reason") ?? "" };
|
|
3141
3231
|
};
|
|
3142
|
-
var list = (raw, map) => Array.isArray(raw) ? raw.flatMap((
|
|
3143
|
-
const value = map(
|
|
3232
|
+
var list = (raw, map) => Array.isArray(raw) ? raw.flatMap((item2) => {
|
|
3233
|
+
const value = map(item2);
|
|
3144
3234
|
return value === void 0 ? [] : [value];
|
|
3145
3235
|
}) : [];
|
|
3146
3236
|
var readGrants = (store, paths) => list(readJsonFile(store, paths.grants)?.["grants"], toGrant);
|
|
@@ -3846,10 +3936,10 @@ var normalizeCommand = (text6, dialect = "bash") => {
|
|
|
3846
3936
|
return { commands: collector.out, incomplete: collector.incomplete };
|
|
3847
3937
|
};
|
|
3848
3938
|
var dialectOf = (toolName3) => toolName3 === "PowerShell" ? "powershell" : "bash";
|
|
3849
|
-
var segmentsOf = (command) => normalizeCommand(command).commands.map((
|
|
3850
|
-
program:
|
|
3851
|
-
args:
|
|
3852
|
-
redirectedFrom:
|
|
3939
|
+
var segmentsOf = (command) => normalizeCommand(command).commands.map((item2) => ({
|
|
3940
|
+
program: item2.program,
|
|
3941
|
+
args: item2.args,
|
|
3942
|
+
redirectedFrom: item2.redirects.filter((redirect) => redirect.op === "<").map((redirect) => redirect.target)
|
|
3853
3943
|
}));
|
|
3854
3944
|
|
|
3855
3945
|
// src/guards/shell.ts
|
|
@@ -4033,7 +4123,7 @@ var targets = (input) => {
|
|
|
4033
4123
|
};
|
|
4034
4124
|
var selfGuard = (input) => {
|
|
4035
4125
|
const found = targets(input);
|
|
4036
|
-
const store = found.find((
|
|
4126
|
+
const store = found.find((item2) => item2.kind === "allow_store");
|
|
4037
4127
|
const { t } = input;
|
|
4038
4128
|
if (store !== void 0)
|
|
4039
4129
|
return deny(
|
|
@@ -4373,8 +4463,8 @@ var CONFIG_FILE3 = "config-hooks.json";
|
|
|
4373
4463
|
var GUARDED_SOURCES = /* @__PURE__ */ new Set(["user_settings", "project_settings", "local_settings"]);
|
|
4374
4464
|
var countIn = (value) => {
|
|
4375
4465
|
if (typeof value === "string") return /\bfinitemonkey\b|\bfm hook\b/.test(value) ? 1 : 0;
|
|
4376
|
-
if (Array.isArray(value)) return value.reduce((sum,
|
|
4377
|
-
return isRecord(value) ? Object.values(value).reduce((sum,
|
|
4466
|
+
if (Array.isArray(value)) return value.reduce((sum, item2) => sum + countIn(item2), 0);
|
|
4467
|
+
return isRecord(value) ? Object.values(value).reduce((sum, item2) => sum + countIn(item2), 0) : 0;
|
|
4378
4468
|
};
|
|
4379
4469
|
var censusOf = (text6) => {
|
|
4380
4470
|
if (text6 === void 0) return { hooks: 0, disabled: false };
|
|
@@ -4799,16 +4889,16 @@ var positive2 = (record3, key, at, issues) => {
|
|
|
4799
4889
|
};
|
|
4800
4890
|
var strings2 = (value, at, issues) => {
|
|
4801
4891
|
if (value === void 0 || value === null) return [];
|
|
4802
|
-
if (Array.isArray(value) && value.every((
|
|
4892
|
+
if (Array.isArray(value) && value.every((item2) => typeof item2 === "string")) return value;
|
|
4803
4893
|
issues.push(`${at}: must be a list of text`);
|
|
4804
4894
|
return [];
|
|
4805
4895
|
};
|
|
4806
4896
|
var oneOf = (options, value) => options.find((option) => option === value);
|
|
4807
4897
|
var gitRequire = (value, at, issues) => {
|
|
4808
4898
|
const items = strings2(value, at, issues);
|
|
4809
|
-
const known2 = items.map((
|
|
4810
|
-
if (known2.some((
|
|
4811
|
-
return known2.filter((
|
|
4899
|
+
const known2 = items.map((item2) => oneOf(GIT_REQUIREMENTS, item2));
|
|
4900
|
+
if (known2.some((item2) => item2 === void 0)) issues.push(`${at}: use ${GIT_REQUIREMENTS.join(", ")}`);
|
|
4901
|
+
return known2.filter((item2) => item2 !== void 0);
|
|
4812
4902
|
};
|
|
4813
4903
|
var appFields = (record3, kind, at, issues) => {
|
|
4814
4904
|
const port = positive2(record3, "port", at, issues);
|
|
@@ -4858,8 +4948,8 @@ var acceptance = (value, issues) => {
|
|
|
4858
4948
|
issues.push("acceptance: must list at least one check");
|
|
4859
4949
|
return [];
|
|
4860
4950
|
}
|
|
4861
|
-
const items = value.map((
|
|
4862
|
-
const checks = items.filter((
|
|
4951
|
+
const items = value.map((item2, index) => acceptanceItem(item2, index, issues));
|
|
4952
|
+
const checks = items.filter((item2) => item2 !== void 0);
|
|
4863
4953
|
const ids2 = checks.map((check2) => check2.id);
|
|
4864
4954
|
const duplicate = ids2.find((id, index) => ids2.indexOf(id) !== index);
|
|
4865
4955
|
if (duplicate !== void 0) issues.push(`acceptance: duplicate id "${duplicate}"`);
|
|
@@ -4979,7 +5069,7 @@ var entry = (value) => {
|
|
|
4979
5069
|
const exitCode = value["exitCode"];
|
|
4980
5070
|
const failures = value["failures"];
|
|
4981
5071
|
if (!(typeof exitCode === "number" || exitCode === null) || !Array.isArray(failures)) return void 0;
|
|
4982
|
-
return { exitCode, failures: failures.filter((
|
|
5072
|
+
return { exitCode, failures: failures.filter((item2) => typeof item2 === "string") };
|
|
4983
5073
|
};
|
|
4984
5074
|
var readBaseline = (runtime, paths) => {
|
|
4985
5075
|
const raw = readJsonFile(runtime, paths.cache(BASELINE_FILE));
|
|
@@ -4987,8 +5077,8 @@ var readBaseline = (runtime, paths) => {
|
|
|
4987
5077
|
const checks = raw?.["checks"];
|
|
4988
5078
|
if (typeof treeHash2 !== "string" || !isRecord(checks)) return void 0;
|
|
4989
5079
|
const parsed = Object.entries(checks).flatMap(([id, value]) => {
|
|
4990
|
-
const
|
|
4991
|
-
return
|
|
5080
|
+
const item2 = entry(value);
|
|
5081
|
+
return item2 === void 0 ? [] : [[id, item2]];
|
|
4992
5082
|
});
|
|
4993
5083
|
return { treeHash: treeHash2, checks: Object.fromEntries(parsed) };
|
|
4994
5084
|
};
|
|
@@ -5028,8 +5118,8 @@ var resolveBase = (git, declared) => {
|
|
|
5028
5118
|
return declared;
|
|
5029
5119
|
}
|
|
5030
5120
|
const remote = defaultRemoteBranch(git);
|
|
5031
|
-
const
|
|
5032
|
-
return nonEmpty(
|
|
5121
|
+
const merged2 = remote === void 0 ? void 0 : git.out(["merge-base", "HEAD", remote])?.trim();
|
|
5122
|
+
return nonEmpty(merged2) ?? nonEmpty(headSha(git)) ?? EMPTY_TREE;
|
|
5033
5123
|
};
|
|
5034
5124
|
var nulFields = (text6) => text6.split("\0").filter((part2) => part2 !== "");
|
|
5035
5125
|
var untracked = (git) => nulFields(git.out(["ls-files", "--others", "--exclude-standard", "-z", "--", ".", EXCLUDE_FM]) ?? "");
|
|
@@ -5113,18 +5203,18 @@ var diffShape = (runtime, git, base, changes) => ({
|
|
|
5113
5203
|
// src/gate/pr-parse.ts
|
|
5114
5204
|
var FAILED = /* @__PURE__ */ new Set(["FAILURE", "CANCELLED", "TIMED_OUT", "ACTION_REQUIRED", "STARTUP_FAILURE", "ERROR"]);
|
|
5115
5205
|
var PENDING = /* @__PURE__ */ new Set(["IN_PROGRESS", "QUEUED", "PENDING", "WAITING", "REQUESTED", "EXPECTED"]);
|
|
5116
|
-
var ciState = (
|
|
5117
|
-
const conclusion = (optionalString(
|
|
5118
|
-
const status = (optionalString(
|
|
5206
|
+
var ciState = (item2) => {
|
|
5207
|
+
const conclusion = (optionalString(item2, "conclusion") ?? "").toUpperCase();
|
|
5208
|
+
const status = (optionalString(item2, "status") ?? optionalString(item2, "state") ?? "").toUpperCase();
|
|
5119
5209
|
if (FAILED.has(conclusion) || FAILED.has(status)) return "failed";
|
|
5120
5210
|
if (conclusion === "" && (PENDING.has(status) || status === "")) return "pending";
|
|
5121
5211
|
if (PENDING.has(status)) return "pending";
|
|
5122
5212
|
return "passed";
|
|
5123
5213
|
};
|
|
5124
|
-
var ciChecks = (value) => Array.isArray(value) ? value.flatMap((
|
|
5125
|
-
if (!isRecord(
|
|
5126
|
-
const name = optionalString(
|
|
5127
|
-
return [{ name, state: ciState(
|
|
5214
|
+
var ciChecks = (value) => Array.isArray(value) ? value.flatMap((item2) => {
|
|
5215
|
+
if (!isRecord(item2)) return [];
|
|
5216
|
+
const name = optionalString(item2, "name") ?? optionalString(item2, "context") ?? "?";
|
|
5217
|
+
return [{ name, state: ciState(item2) }];
|
|
5128
5218
|
}) : [];
|
|
5129
5219
|
var parsePrView = (text6) => {
|
|
5130
5220
|
const parsed = parseJsonObject(text6);
|
|
@@ -5397,7 +5487,7 @@ var maxBlocks = (env) => {
|
|
|
5397
5487
|
var failureSignature = (report2) => sha256Hex(
|
|
5398
5488
|
JSON.stringify([
|
|
5399
5489
|
report2.treeHash,
|
|
5400
|
-
report2.items.filter((
|
|
5490
|
+
report2.items.filter((item2) => item2.status === "failed").map((item2) => [item2.id, item2.notes])
|
|
5401
5491
|
])
|
|
5402
5492
|
);
|
|
5403
5493
|
var nextBlock = (previous, report2, env) => {
|
|
@@ -5489,12 +5579,12 @@ var PRS_AUTH_TIMEOUT_MS = 2e3;
|
|
|
5489
5579
|
var parsePrList = (text6) => {
|
|
5490
5580
|
const parsed = parseJson(text6);
|
|
5491
5581
|
if (!parsed.ok || !Array.isArray(parsed.value)) return void 0;
|
|
5492
|
-
return parsed.value.flatMap((
|
|
5493
|
-
if (!isRecord(
|
|
5494
|
-
const mergeable = optionalString(
|
|
5495
|
-
const status = optionalString(
|
|
5582
|
+
return parsed.value.flatMap((item2) => {
|
|
5583
|
+
if (!isRecord(item2) || typeof item2["number"] !== "number") return [];
|
|
5584
|
+
const mergeable = optionalString(item2, "mergeable");
|
|
5585
|
+
const status = optionalString(item2, "mergeStateStatus");
|
|
5496
5586
|
const state = mergeable === "CONFLICTING" || status === "DIRTY" ? "conflict" : status === "BEHIND" ? "behind" : "ok";
|
|
5497
|
-
return [{ number:
|
|
5587
|
+
return [{ number: item2["number"], state }];
|
|
5498
5588
|
});
|
|
5499
5589
|
};
|
|
5500
5590
|
var cachedPrs = (runtime, paths) => {
|
|
@@ -5571,7 +5661,7 @@ var params = (value) => isRecord(value) ? Object.fromEntries(
|
|
|
5571
5661
|
)
|
|
5572
5662
|
) : {};
|
|
5573
5663
|
var notes = (value) => Array.isArray(value) ? value.flatMap(
|
|
5574
|
-
(
|
|
5664
|
+
(item2) => isRecord(item2) && typeof item2["key"] === "string" ? [{ key: item2["key"], params: params(item2["params"]) }] : []
|
|
5575
5665
|
) : [];
|
|
5576
5666
|
var text3 = (record3, key) => {
|
|
5577
5667
|
const value = record3[key];
|
|
@@ -5593,7 +5683,7 @@ var optional = (record3) => {
|
|
|
5593
5683
|
...typeof durationMs === "number" ? { durationMs } : {},
|
|
5594
5684
|
...reason === void 0 ? {} : { reason },
|
|
5595
5685
|
...releasedBy === void 0 ? {} : { releasedBy },
|
|
5596
|
-
...Array.isArray(failures) ? { failures: failures.filter((
|
|
5686
|
+
...Array.isArray(failures) ? { failures: failures.filter((item2) => typeof item2 === "string") } : {},
|
|
5597
5687
|
...typeof testCount === "number" ? { testCount } : {}
|
|
5598
5688
|
};
|
|
5599
5689
|
};
|
|
@@ -6206,7 +6296,14 @@ var en_default = {
|
|
|
6206
6296
|
"secrets.live.rejected": "{host} rejected the key in ${variable} (HTTP {status}): it no longer works.",
|
|
6207
6297
|
"secrets.live.unknown": "{host} answered HTTP {status}; that does not say whether the key in ${variable} works. Check it in the provider's console.",
|
|
6208
6298
|
"secrets.live.unreachable": "Could not reach {host} with curl, so the key in ${variable} was not checked.",
|
|
6209
|
-
"secrets.live.no_value": "${variable} is empty or not set in this terminal; export it first, nothing was sent"
|
|
6299
|
+
"secrets.live.no_value": "${variable} is empty or not set in this terminal; export it first, nothing was sent",
|
|
6300
|
+
"githooks.command.commit_msg": "Check a commit message: Conventional Commits header and no AI attribution (for the commit-msg git hook)",
|
|
6301
|
+
"githooks.command.push_check": "Check what a push sends: secrets, commit messages, merges that dropped lines and protected branches (for the pre-push git hook)",
|
|
6302
|
+
"githooks.command.ci_gate": "Run the same checks on a pull request's commits in CI, one line per failing check with its fix",
|
|
6303
|
+
"githooks.pass": "ok {check}",
|
|
6304
|
+
"githooks.fail": "FAIL {check}: {detail}. Fix: {fix}",
|
|
6305
|
+
"githooks.error.failed": "{count, plural, one {# check failed} other {# checks failed}}",
|
|
6306
|
+
"githooks.error.no_base": "the base {detail} does not exist here; fetch it first (actions/checkout needs fetch-depth: 0)"
|
|
6210
6307
|
};
|
|
6211
6308
|
|
|
6212
6309
|
// src/i18n/es.json
|
|
@@ -6777,7 +6874,14 @@ var es_default = {
|
|
|
6777
6874
|
"secrets.live.rejected": "{host} rechazó la clave en ${variable} (HTTP {status}): ya no funciona.",
|
|
6778
6875
|
"secrets.live.unknown": "{host} respondió HTTP {status}; eso no dice si la clave en ${variable} funciona. Revísala en la consola del proveedor.",
|
|
6779
6876
|
"secrets.live.unreachable": "No se pudo llegar a {host} con curl, así que la clave en ${variable} no se comprobó.",
|
|
6780
|
-
"secrets.live.no_value": "${variable} está vacía o no existe en esta terminal; expórtala antes, no se envió nada"
|
|
6877
|
+
"secrets.live.no_value": "${variable} está vacía o no existe en esta terminal; expórtala antes, no se envió nada",
|
|
6878
|
+
"githooks.command.commit_msg": "Revisa un mensaje de commit: encabezado Conventional Commits y ninguna atribución a IA (para el hook commit-msg de git)",
|
|
6879
|
+
"githooks.command.push_check": "Revisa lo que envía un push: secretos, mensajes de commit, merges que perdieron líneas y ramas protegidas (para el hook pre-push de git)",
|
|
6880
|
+
"githooks.command.ci_gate": "Ejecuta las mismas revisiones en los commits de un pull request en CI, una línea por revisión fallida con su arreglo",
|
|
6881
|
+
"githooks.pass": "ok {check}",
|
|
6882
|
+
"githooks.fail": "FALLÓ {check}: {detail}. Arreglo: {fix}",
|
|
6883
|
+
"githooks.error.failed": "{count, plural, one {# revisión falló} other {# revisiones fallaron}}",
|
|
6884
|
+
"githooks.error.no_base": "la base {detail} no existe aquí; tráela antes (actions/checkout necesita fetch-depth: 0)"
|
|
6781
6885
|
};
|
|
6782
6886
|
|
|
6783
6887
|
// src/i18n/pt-BR.json
|
|
@@ -7348,7 +7452,14 @@ var pt_BR_default = {
|
|
|
7348
7452
|
"secrets.live.rejected": "{host} recusou a chave em ${variable} (HTTP {status}): ela não funciona mais.",
|
|
7349
7453
|
"secrets.live.unknown": "{host} respondeu HTTP {status}; isso não diz se a chave em ${variable} funciona. Confira no painel do provedor.",
|
|
7350
7454
|
"secrets.live.unreachable": "Não deu para alcançar {host} com o curl, então a chave em ${variable} não foi verificada.",
|
|
7351
|
-
"secrets.live.no_value": "${variable} está vazia ou não existe neste terminal; exporte antes, nada foi enviado"
|
|
7455
|
+
"secrets.live.no_value": "${variable} está vazia ou não existe neste terminal; exporte antes, nada foi enviado",
|
|
7456
|
+
"githooks.command.commit_msg": "Confere uma mensagem de commit: cabeçalho Conventional Commits e nenhuma atribuição a IA (para o hook commit-msg do git)",
|
|
7457
|
+
"githooks.command.push_check": "Confere o que um push envia: segredos, mensagens de commit, merges que perderam linhas e branches protegidas (para o hook pre-push do git)",
|
|
7458
|
+
"githooks.command.ci_gate": "Roda as mesmas checagens nos commits de um pull request no CI, uma linha por checagem que falhou com o conserto",
|
|
7459
|
+
"githooks.pass": "ok {check}",
|
|
7460
|
+
"githooks.fail": "FALHOU {check}: {detail}. Conserto: {fix}",
|
|
7461
|
+
"githooks.error.failed": "{count, plural, one {# checagem falhou} other {# checagens falharam}}",
|
|
7462
|
+
"githooks.error.no_base": "a base {detail} não existe aqui; busque antes (o actions/checkout precisa de fetch-depth: 0)"
|
|
7352
7463
|
};
|
|
7353
7464
|
|
|
7354
7465
|
// src/i18n/review.json
|
|
@@ -7920,7 +8031,14 @@ var review_default = {
|
|
|
7920
8031
|
"secrets.live.rejected": "machine",
|
|
7921
8032
|
"secrets.live.unknown": "machine",
|
|
7922
8033
|
"secrets.live.unreachable": "machine",
|
|
7923
|
-
"secrets.live.no_value": "machine"
|
|
8034
|
+
"secrets.live.no_value": "machine",
|
|
8035
|
+
"githooks.command.commit_msg": "machine",
|
|
8036
|
+
"githooks.command.push_check": "machine",
|
|
8037
|
+
"githooks.command.ci_gate": "machine",
|
|
8038
|
+
"githooks.pass": "machine",
|
|
8039
|
+
"githooks.fail": "machine",
|
|
8040
|
+
"githooks.error.failed": "machine",
|
|
8041
|
+
"githooks.error.no_base": "machine"
|
|
7924
8042
|
},
|
|
7925
8043
|
es: {
|
|
7926
8044
|
"command.init.summary": "machine",
|
|
@@ -8489,7 +8607,14 @@ var review_default = {
|
|
|
8489
8607
|
"secrets.live.rejected": "machine",
|
|
8490
8608
|
"secrets.live.unknown": "machine",
|
|
8491
8609
|
"secrets.live.unreachable": "machine",
|
|
8492
|
-
"secrets.live.no_value": "machine"
|
|
8610
|
+
"secrets.live.no_value": "machine",
|
|
8611
|
+
"githooks.command.commit_msg": "machine",
|
|
8612
|
+
"githooks.command.push_check": "machine",
|
|
8613
|
+
"githooks.command.ci_gate": "machine",
|
|
8614
|
+
"githooks.pass": "machine",
|
|
8615
|
+
"githooks.fail": "machine",
|
|
8616
|
+
"githooks.error.failed": "machine",
|
|
8617
|
+
"githooks.error.no_base": "machine"
|
|
8493
8618
|
}
|
|
8494
8619
|
};
|
|
8495
8620
|
|
|
@@ -8512,13 +8637,13 @@ var STATUS_KEYS = {
|
|
|
8512
8637
|
unverified: "gate.status.unverified"
|
|
8513
8638
|
};
|
|
8514
8639
|
var isMessageKey = (key) => key in catalogs.en;
|
|
8515
|
-
var noteText = (t,
|
|
8516
|
-
var reasonText = (t,
|
|
8517
|
-
var itemDetails = (t,
|
|
8518
|
-
...
|
|
8519
|
-
...reasonText(t,
|
|
8640
|
+
var noteText = (t, item2) => maskText(isMessageKey(item2.key) ? t(item2.key, item2.params) : item2.key);
|
|
8641
|
+
var reasonText = (t, item2) => item2.status === "unverified" && item2.notes.length === 0 && item2.reason !== void 0 && isMessageKey(`gate.reason.${item2.reason}`) ? [t(`gate.reason.${item2.reason}`)] : [];
|
|
8642
|
+
var itemDetails = (t, item2) => [
|
|
8643
|
+
...item2.notes.map((entry2) => noteText(t, entry2)),
|
|
8644
|
+
...reasonText(t, item2)
|
|
8520
8645
|
];
|
|
8521
|
-
var label = (
|
|
8646
|
+
var label = (item2) => item2.command === void 0 ? item2.kind : `${item2.kind} \`${maskText(item2.command)}\``;
|
|
8522
8647
|
var reportLines = (t, report2) => {
|
|
8523
8648
|
const width = Math.max(...Object.values(STATUS_KEYS).map((key) => t(key).length)) + 2;
|
|
8524
8649
|
return [
|
|
@@ -8527,9 +8652,9 @@ var reportLines = (t, report2) => {
|
|
|
8527
8652
|
tree: report2.treeHash
|
|
8528
8653
|
}),
|
|
8529
8654
|
...report2.contractEdited ? [` ${t("gate.report.contract_edited")}`] : [],
|
|
8530
|
-
...report2.items.flatMap((
|
|
8531
|
-
` ${t(STATUS_KEYS[
|
|
8532
|
-
...itemDetails(t,
|
|
8655
|
+
...report2.items.flatMap((item2) => [
|
|
8656
|
+
` ${t(STATUS_KEYS[item2.status]).padEnd(width)}${item2.id} ${label(item2)}`,
|
|
8657
|
+
...itemDetails(t, item2).map((line2) => ` ${" ".repeat(width)} ${line2}`)
|
|
8533
8658
|
]),
|
|
8534
8659
|
t(`gate.verdict.${report2.verdict}`, report2.counts)
|
|
8535
8660
|
];
|
|
@@ -8546,24 +8671,24 @@ var REQUIRE_KEYS = {
|
|
|
8546
8671
|
worktree: "gate.require.worktree",
|
|
8547
8672
|
merge: "gate.require.merge"
|
|
8548
8673
|
};
|
|
8549
|
-
var requirement = (t,
|
|
8674
|
+
var requirement = (t, item2) => `${item2.id} ${t(REQUIRE_KEYS[item2.kind] ?? "gate.require.other", { command: maskText(item2.command ?? item2.kind), kind: item2.kind })}`;
|
|
8550
8675
|
var CLAIM_MAX = 120;
|
|
8551
8676
|
var claimOf = (message) => {
|
|
8552
8677
|
const first = maskText((message ?? "").trim().split(/\n/)[0] ?? "");
|
|
8553
8678
|
return first.length > CLAIM_MAX ? `${first.slice(0, CLAIM_MAX)}…` : first;
|
|
8554
8679
|
};
|
|
8555
8680
|
var blockReason = (t, report2, lastMessage) => {
|
|
8556
|
-
const
|
|
8681
|
+
const failing2 = report2.items.filter((item2) => item2.status === "failed");
|
|
8557
8682
|
return [
|
|
8558
8683
|
t("gate.model.title", { task: report2.task, tree: report2.treeHash }),
|
|
8559
8684
|
t("gate.model.claim", { claim: claimOf(lastMessage) || t("gate.model.no_claim") }),
|
|
8560
|
-
t("gate.model.requires", { list:
|
|
8685
|
+
t("gate.model.requires", { list: failing2.map((item2) => requirement(t, item2)).join("; ") }),
|
|
8561
8686
|
t("gate.model.not_enough", {
|
|
8562
|
-
list:
|
|
8687
|
+
list: failing2.map((item2) => `${item2.id}: ${itemDetails(t, item2).join(", ")}`).join("; ")
|
|
8563
8688
|
})
|
|
8564
8689
|
].join("\n");
|
|
8565
8690
|
};
|
|
8566
|
-
var unverifiedSummary = (t, report2) => report2.items.filter((
|
|
8691
|
+
var unverifiedSummary = (t, report2) => report2.items.filter((item2) => item2.status === "unverified").map((item2) => `${item2.id} (${itemDetails(t, item2).join(", ") || t("gate.reason.no_evidence")})`).join("; ");
|
|
8567
8692
|
|
|
8568
8693
|
// src/gate/types.ts
|
|
8569
8694
|
var note = (key, params2 = {}) => ({ key, params: params2 });
|
|
@@ -8597,22 +8722,22 @@ var fromEvidence = (id, kind, entry2, stale) => {
|
|
|
8597
8722
|
};
|
|
8598
8723
|
};
|
|
8599
8724
|
var verdictOf = (items) => {
|
|
8600
|
-
if (items.some((
|
|
8601
|
-
return items.some((
|
|
8725
|
+
if (items.some((item2) => item2.status === "failed")) return "failed";
|
|
8726
|
+
return items.some((item2) => item2.status === "unverified") ? "incomplete" : "passed";
|
|
8602
8727
|
};
|
|
8603
8728
|
var buildReport = (input) => {
|
|
8604
8729
|
const { contract, ledger, treeHash: treeHash2 } = input;
|
|
8605
8730
|
const items = contract.acceptance.map((check2) => {
|
|
8606
8731
|
const command = REUSABLE_KINDS.includes(check2.kind) ? check2.run : void 0;
|
|
8607
8732
|
const entry2 = latestFor(
|
|
8608
|
-
ledger.filter((
|
|
8733
|
+
ledger.filter((item2) => item2.task === contract.id),
|
|
8609
8734
|
check2.id,
|
|
8610
8735
|
treeHash2,
|
|
8611
8736
|
command
|
|
8612
8737
|
);
|
|
8613
8738
|
return fromEvidence(check2.id, check2.kind, entry2, hasStale(ledger, check2.id, treeHash2));
|
|
8614
8739
|
});
|
|
8615
|
-
const count4 = (status) => items.filter((
|
|
8740
|
+
const count4 = (status) => items.filter((item2) => item2.status === status).length;
|
|
8616
8741
|
return {
|
|
8617
8742
|
schema: GATE_SCHEMA,
|
|
8618
8743
|
task: contract.id,
|
|
@@ -8654,7 +8779,7 @@ var currentReport = (runtime, paths, sessionId, taskId) => {
|
|
|
8654
8779
|
fastPath: true
|
|
8655
8780
|
});
|
|
8656
8781
|
};
|
|
8657
|
-
var ids = (report2, status) => report2.items.filter((
|
|
8782
|
+
var ids = (report2, status) => report2.items.filter((item2) => item2.status === status).map((item2) => item2.id).join(", ") || "-";
|
|
8658
8783
|
var snapshotOf = (t, report2, grants, at) => ({
|
|
8659
8784
|
at,
|
|
8660
8785
|
task: report2?.task ?? "",
|
|
@@ -8662,7 +8787,7 @@ var snapshotOf = (t, report2, grants, at) => ({
|
|
|
8662
8787
|
implicit: report2?.implicit ?? true,
|
|
8663
8788
|
tree: report2?.treeHash ?? "",
|
|
8664
8789
|
passed: report2 === void 0 ? "-" : ids(report2, "passed"),
|
|
8665
|
-
failed: report2 === void 0 ? "-" : report2.items.filter((
|
|
8790
|
+
failed: report2 === void 0 ? "-" : report2.items.filter((item2) => item2.status === "failed").map((item2) => `${item2.id} (${itemDetails(t, item2).join(", ")})`).join("; ") || "-",
|
|
8666
8791
|
unverified: report2 === void 0 ? "-" : ids(report2, "unverified"),
|
|
8667
8792
|
grants: grants.join(", ") || "-"
|
|
8668
8793
|
});
|
|
@@ -8758,8 +8883,8 @@ var toReview = (value) => {
|
|
|
8758
8883
|
};
|
|
8759
8884
|
var readReviews = (runtime, paths) => {
|
|
8760
8885
|
const raw = readJsonFile(runtime, paths.cache(REVIEWS_FILE))?.["reviews"];
|
|
8761
|
-
return Array.isArray(raw) ? raw.flatMap((
|
|
8762
|
-
const review2 = toReview(
|
|
8886
|
+
return Array.isArray(raw) ? raw.flatMap((item2) => {
|
|
8887
|
+
const review2 = toReview(item2);
|
|
8763
8888
|
return review2 === void 0 ? [] : [review2];
|
|
8764
8889
|
}) : [];
|
|
8765
8890
|
};
|
|
@@ -9199,7 +9324,7 @@ var gitCheck = (check2, ctx) => {
|
|
|
9199
9324
|
// src/gate/checks/manual.ts
|
|
9200
9325
|
var manualCheck = (check2, ctx) => {
|
|
9201
9326
|
const approval = readApprovals(ctx.runtime, ctx.paths).findLast(
|
|
9202
|
-
(
|
|
9327
|
+
(item2) => item2.task === ctx.contract.id && item2.check === check2.id
|
|
9203
9328
|
);
|
|
9204
9329
|
if (approval === void 0) {
|
|
9205
9330
|
return unverified("awaiting_person", [
|
|
@@ -9250,11 +9375,11 @@ var foundOutcome = (status, ctx) => {
|
|
|
9250
9375
|
if (status.localHead !== void 0 && pr.headRefOid !== "" && status.localHead !== pr.headRefOid) {
|
|
9251
9376
|
info.push(note("gate.pr.stale_head", { number: pr.number }));
|
|
9252
9377
|
}
|
|
9253
|
-
const
|
|
9378
|
+
const failing2 = pr.checks.filter((check2) => check2.state === "failed");
|
|
9254
9379
|
const pending = pr.checks.filter((check2) => check2.state === "pending");
|
|
9255
9380
|
if (isConflicting(pr)) return failed([...info, ...conflictNotes(pr, ctx)]);
|
|
9256
|
-
if (
|
|
9257
|
-
return failed([...info, ...
|
|
9381
|
+
if (failing2.length > 0)
|
|
9382
|
+
return failed([...info, ...failing2.map((check2) => note("gate.pr.ci_failed", { name: check2.name }))]);
|
|
9258
9383
|
if (pending.length > 0)
|
|
9259
9384
|
return unverified("pending", [
|
|
9260
9385
|
...info,
|
|
@@ -9825,7 +9950,7 @@ var windowEnd = (units, from, size) => {
|
|
|
9825
9950
|
while (to + 1 < Math.min(units.length, from + size) && units[to + 1]?.section === section) to += 1;
|
|
9826
9951
|
return to;
|
|
9827
9952
|
};
|
|
9828
|
-
var windowScore = (hits, window) => hits.filter((
|
|
9953
|
+
var windowScore = (hits, window) => hits.filter((item2) => item2.unit >= window.from && item2.unit <= window.to).reduce((sum, item2) => sum + item2.weight, 0);
|
|
9829
9954
|
var hotWindows = (hits, units, size, threshold2) => {
|
|
9830
9955
|
const windows = [];
|
|
9831
9956
|
for (let from = 0; from < units.length; from += 1) {
|
|
@@ -9835,12 +9960,12 @@ var hotWindows = (hits, units, size, threshold2) => {
|
|
|
9835
9960
|
return windows;
|
|
9836
9961
|
};
|
|
9837
9962
|
var spanOf2 = (hits, from, to) => {
|
|
9838
|
-
const inside = hits.filter((
|
|
9839
|
-
const lines3 = inside.map((
|
|
9963
|
+
const inside = hits.filter((item2) => item2.unit >= from && item2.unit <= to);
|
|
9964
|
+
const lines3 = inside.map((item2) => item2.line);
|
|
9840
9965
|
return {
|
|
9841
9966
|
startLine: Math.min(...lines3),
|
|
9842
9967
|
endLine: Math.max(...lines3),
|
|
9843
|
-
score: inside.reduce((sum,
|
|
9968
|
+
score: inside.reduce((sum, item2) => sum + item2.weight, 0),
|
|
9844
9969
|
hits: inside
|
|
9845
9970
|
};
|
|
9846
9971
|
};
|
|
@@ -9900,7 +10025,7 @@ var proseGateStatus = (runtime, root) => {
|
|
|
9900
10025
|
if (!files.ok) return files;
|
|
9901
10026
|
return accept({
|
|
9902
10027
|
files: files.value,
|
|
9903
|
-
flagged: files.value.filter((
|
|
10028
|
+
flagged: files.value.filter((item2) => isFlagged(item2.report)),
|
|
9904
10029
|
blocking: projectPreset(runtime, root) === "gabriel"
|
|
9905
10030
|
});
|
|
9906
10031
|
};
|
|
@@ -9912,7 +10037,7 @@ var proseOutcome = (status) => {
|
|
|
9912
10037
|
if (!status.ok) return unverified("tool_missing", [note("gate.prose.unavailable")]);
|
|
9913
10038
|
const { files, flagged, blocking: blocking2 } = status.value;
|
|
9914
10039
|
const key = blocking2 ? "gate.prose.finding" : "gate.prose.warning";
|
|
9915
|
-
const notes2 = flagged.flatMap((
|
|
10040
|
+
const notes2 = flagged.flatMap((item2) => item2.report.spans.map((span) => ({ file: item2.file, span }))).slice(0, MAX_LISTED3).map(({ file, span }) => note(key, { file, line: span.startLine, rules: rulesOf(span) }));
|
|
9916
10041
|
if (notes2.length === 0) return passed([note("gate.prose.clean", { count: files.length })]);
|
|
9917
10042
|
return blocking2 ? failed(notes2) : passed(notes2);
|
|
9918
10043
|
};
|
|
@@ -9939,7 +10064,7 @@ var reviewCheck = (check2, ctx) => {
|
|
|
9939
10064
|
return { status: "passed", notes: [note("gate.review.done", { agent: review2.agent, at: review2.at })] };
|
|
9940
10065
|
}
|
|
9941
10066
|
const approval = readApprovals(ctx.runtime, ctx.paths).findLast(
|
|
9942
|
-
(
|
|
10067
|
+
(item2) => item2.task === ctx.contract.id && item2.check === check2.id
|
|
9943
10068
|
);
|
|
9944
10069
|
if (approval !== void 0) {
|
|
9945
10070
|
return {
|
|
@@ -9967,7 +10092,7 @@ var parseAppCheck = (stdout) => {
|
|
|
9967
10092
|
if (!envelope.ok) return void 0;
|
|
9968
10093
|
const data = envelope.value["data"];
|
|
9969
10094
|
if (!isRecord(data)) return void 0;
|
|
9970
|
-
const kind = KINDS2.find((
|
|
10095
|
+
const kind = KINDS2.find((item2) => item2 === data["kind"]);
|
|
9971
10096
|
const { output, reused, stopped, durationMs, exitCode, bodyExcerpt } = data;
|
|
9972
10097
|
if (kind === void 0 || typeof output !== "string" || typeof reused !== "boolean" || typeof stopped !== "boolean")
|
|
9973
10098
|
return void 0;
|
|
@@ -10098,8 +10223,8 @@ var parseBaseline = (raw) => {
|
|
|
10098
10223
|
const list4 = parsed.value["entries"];
|
|
10099
10224
|
if (!Array.isArray(list4)) return reject("entries must be a list");
|
|
10100
10225
|
const entries = [];
|
|
10101
|
-
for (const [index,
|
|
10102
|
-
const entry2 = parseEntry2(
|
|
10226
|
+
for (const [index, item2] of list4.entries()) {
|
|
10227
|
+
const entry2 = parseEntry2(item2);
|
|
10103
10228
|
if (entry2 === void 0) return reject(`entry ${String(index)} is incomplete`);
|
|
10104
10229
|
entries.push(entry2);
|
|
10105
10230
|
}
|
|
@@ -10828,10 +10953,10 @@ var parseDropped = (text6) => {
|
|
|
10828
10953
|
const items = [];
|
|
10829
10954
|
const invalid = [];
|
|
10830
10955
|
list4.forEach((value, index) => {
|
|
10831
|
-
const
|
|
10832
|
-
if (
|
|
10833
|
-
else if (!REASONS.has(
|
|
10834
|
-
else items.push(
|
|
10956
|
+
const item2 = itemOf(value);
|
|
10957
|
+
if (item2 === void 0) invalid.push(invalidFinding(DROPPED_FILE, `item ${String(index)}: needs file and reason`));
|
|
10958
|
+
else if (!REASONS.has(item2.reason)) invalid.push(invalidFinding(item2.file, `reason ${item2.reason}`));
|
|
10959
|
+
else items.push(item2);
|
|
10835
10960
|
});
|
|
10836
10961
|
return accept({ items, invalid });
|
|
10837
10962
|
};
|
|
@@ -10842,7 +10967,7 @@ var loadDropped = (runtime, root, file) => {
|
|
|
10842
10967
|
return parseDropped(text6.value);
|
|
10843
10968
|
};
|
|
10844
10969
|
var isDropped = (finding3, items) => items.some(
|
|
10845
|
-
(
|
|
10970
|
+
(item2) => item2.file === finding3.file && (item2.side === void 0 || finding3.side === void 0 || item2.side === finding3.side) && (item2.text === void 0 || normalizeLine(item2.text) === finding3.text)
|
|
10846
10971
|
);
|
|
10847
10972
|
|
|
10848
10973
|
// src/git/merge/generated.ts
|
|
@@ -10913,15 +11038,15 @@ var verifyGenerated = (runtime, repo, inputs, paths, generators) => {
|
|
|
10913
11038
|
if (!runtime.git(["worktree", "add", "--detach", dir, commit2.value], repo.root).ok)
|
|
10914
11039
|
return reject("could not check out the merge result");
|
|
10915
11040
|
try {
|
|
10916
|
-
for (const command of new Set(declared.map((
|
|
11041
|
+
for (const command of new Set(declared.map((item2) => item2.command))) {
|
|
10917
11042
|
const ran = runGenerator(runtime, dir, command);
|
|
10918
11043
|
if (!ran.ok) return ran;
|
|
10919
11044
|
}
|
|
10920
11045
|
return accept(
|
|
10921
11046
|
declared.flatMap(({ path }) => {
|
|
10922
11047
|
const fresh3 = runtime.readText(toAbsolutePath(join24(dir, path)));
|
|
10923
|
-
const
|
|
10924
|
-
if (fresh3.ok && fresh3.value ===
|
|
11048
|
+
const merged2 = blobText(runtime, repo, commit2.value, path);
|
|
11049
|
+
if (fresh3.ok && fresh3.value === merged2) return [];
|
|
10925
11050
|
return [{ kind: "generated-differs", file: path, side: void 0, line: void 0, text: path, weak: false }];
|
|
10926
11051
|
})
|
|
10927
11052
|
);
|
|
@@ -10973,7 +11098,7 @@ var missingAdditions = (hunks, file, elsewhere) => {
|
|
|
10973
11098
|
};
|
|
10974
11099
|
var expectations = (file, hunks, removal) => {
|
|
10975
11100
|
const base = countLines(file.base);
|
|
10976
|
-
const addsOf = (side) => tally(hunks.filter((
|
|
11101
|
+
const addsOf = (side) => tally(hunks.filter((item2) => item2.side === side).flatMap((item2) => item2.added));
|
|
10977
11102
|
const ours = addsOf("ours");
|
|
10978
11103
|
const theirs = addsOf("theirs");
|
|
10979
11104
|
const keys = /* @__PURE__ */ new Set([...base.keys(), ...ours.keys(), ...theirs.keys(), ...removal.union.keys()]);
|
|
@@ -11187,7 +11312,7 @@ var verifyMerge = (runtime, repo, options = {}) => {
|
|
|
11187
11312
|
...pairs2.filter((pair) => !generated.has(pair.key)).flatMap((pair) => verifyFile(context, pair)),
|
|
11188
11313
|
...regenerated.value
|
|
11189
11314
|
];
|
|
11190
|
-
const open = [...dropped.value.invalid, ...all.filter((
|
|
11315
|
+
const open = [...dropped.value.invalid, ...all.filter((item2) => !isDropped(item2, dropped.value.items))];
|
|
11191
11316
|
return {
|
|
11192
11317
|
schema: MERGE_VERIFY_SCHEMA,
|
|
11193
11318
|
exit: open.length === 0 ? 0 : 1,
|
|
@@ -11279,8 +11404,8 @@ var recordBaseline = (runtime, repo, owner) => {
|
|
|
11279
11404
|
var recordEdits = (runtime, repo, owner, paths) => {
|
|
11280
11405
|
if (paths.length === 0) return;
|
|
11281
11406
|
const known2 = readEdits(runtime, repo, owner);
|
|
11282
|
-
const
|
|
11283
|
-
if (
|
|
11407
|
+
const merged2 = [.../* @__PURE__ */ new Set([...known2, ...paths])].sort();
|
|
11408
|
+
if (merged2.length !== known2.size) write(runtime, fileOf(repo, "edits", owner), { paths: merged2 });
|
|
11284
11409
|
};
|
|
11285
11410
|
var recordShellChanges = (runtime, repo, owner) => {
|
|
11286
11411
|
const entries = readDirty(runtime, repo.root);
|
|
@@ -11433,23 +11558,23 @@ var mergeCheck = (_check, ctx) => {
|
|
|
11433
11558
|
const repo = repoOf(ctx);
|
|
11434
11559
|
if (repo === void 0) return unverified("tool_missing", [note("gate.merge.unavailable")]);
|
|
11435
11560
|
const items = mergeGateStatus(ctx.runtime, repo, ownerOf(ctx));
|
|
11436
|
-
const
|
|
11437
|
-
const broken = items.filter((
|
|
11561
|
+
const short3 = (commit2) => commit2 === "MERGE_HEAD" ? commit2 : commit2.slice(0, 12);
|
|
11562
|
+
const broken = items.filter((item2) => item2.report.exit === 2);
|
|
11438
11563
|
if (broken.length > 0) {
|
|
11439
11564
|
return unverified(
|
|
11440
11565
|
"tool_missing",
|
|
11441
11566
|
broken.map(
|
|
11442
|
-
(
|
|
11567
|
+
(item2) => note("gate.merge.unanalyzed", { commit: short3(item2.commit), detail: item2.report.error ?? "" })
|
|
11443
11568
|
)
|
|
11444
11569
|
);
|
|
11445
11570
|
}
|
|
11446
|
-
const lost = items.filter((
|
|
11571
|
+
const lost = items.filter((item2) => item2.report.exit === 1);
|
|
11447
11572
|
if (lost.length > 0) {
|
|
11448
11573
|
return failed(
|
|
11449
11574
|
lost.map(
|
|
11450
|
-
(
|
|
11451
|
-
commit:
|
|
11452
|
-
findings: listed3(
|
|
11575
|
+
(item2) => note("gate.merge.lost", {
|
|
11576
|
+
commit: short3(item2.commit),
|
|
11577
|
+
findings: listed3(item2.report.findings.map(findingLabel))
|
|
11453
11578
|
})
|
|
11454
11579
|
)
|
|
11455
11580
|
);
|
|
@@ -11567,9 +11692,9 @@ var failureDetail = (t, failure4) => {
|
|
|
11567
11692
|
});
|
|
11568
11693
|
}
|
|
11569
11694
|
};
|
|
11570
|
-
var failingList = (t, report2) => report2.items.filter((
|
|
11695
|
+
var failingList = (t, report2) => report2.items.filter((item2) => item2.status === "failed").map((item2) => `${item2.id} (${itemDetails(t, item2).join(", ")})`).join("; ");
|
|
11571
11696
|
var touchesTests = (report2) => report2.items.some(
|
|
11572
|
-
(
|
|
11697
|
+
(item2) => item2.status === "failed" && item2.notes.some((entry2) => entry2.key.startsWith("gate.tests."))
|
|
11573
11698
|
);
|
|
11574
11699
|
var markDone = (input, report2, root) => {
|
|
11575
11700
|
if (report2.implicit) return;
|
|
@@ -12329,8 +12454,8 @@ var writtenTexts = (tool, input) => {
|
|
|
12329
12454
|
};
|
|
12330
12455
|
var stringLeaves = (value, out = []) => {
|
|
12331
12456
|
if (typeof value === "string") out.push(value);
|
|
12332
|
-
else if (Array.isArray(value)) for (const
|
|
12333
|
-
else if (isRecord(value)) for (const
|
|
12457
|
+
else if (Array.isArray(value)) for (const item2 of value) stringLeaves(item2, out);
|
|
12458
|
+
else if (isRecord(value)) for (const item2 of Object.values(value)) stringLeaves(item2, out);
|
|
12334
12459
|
return out;
|
|
12335
12460
|
};
|
|
12336
12461
|
|
|
@@ -13284,16 +13409,16 @@ var rmOutcome = (input, command, worktrees) => {
|
|
|
13284
13409
|
};
|
|
13285
13410
|
var snapshotsOf = (input, owner, pending) => {
|
|
13286
13411
|
const done = /* @__PURE__ */ new Map();
|
|
13287
|
-
for (const
|
|
13288
|
-
if (done.has(
|
|
13289
|
-
const taken = takeSnapshot(input.runtime,
|
|
13412
|
+
for (const item2 of pending) {
|
|
13413
|
+
if (done.has(item2.repo.root)) continue;
|
|
13414
|
+
const taken = takeSnapshot(input.runtime, item2.repo, { op: item2.op, owner, command: item2.command });
|
|
13290
13415
|
if (!taken.ok) {
|
|
13291
13416
|
return {
|
|
13292
13417
|
kind: "block",
|
|
13293
|
-
reason: input.t("git.block.snapshot_failed", { command:
|
|
13418
|
+
reason: input.t("git.block.snapshot_failed", { command: item2.command, detail: taken.error })
|
|
13294
13419
|
};
|
|
13295
13420
|
}
|
|
13296
|
-
done.set(
|
|
13421
|
+
done.set(item2.repo.root, input.t("git.snapshot.context", { command: item2.command, ref: taken.value.ref }));
|
|
13297
13422
|
}
|
|
13298
13423
|
if (done.size === 0) return ALLOW;
|
|
13299
13424
|
const context = [...done.values()].join("\n");
|
|
@@ -13627,8 +13752,8 @@ var sourceOf = (value, isFile, segment, heredocs) => {
|
|
|
13627
13752
|
var collect3 = (target2, segment, args2, spec2, heredocs) => {
|
|
13628
13753
|
const values = flagValues(args2, spec2);
|
|
13629
13754
|
if (values.length === 0) return void 0;
|
|
13630
|
-
const title = values.find((
|
|
13631
|
-
const bodies = values.filter((
|
|
13755
|
+
const title = values.find((item2) => spec2.title.includes(item2.flag));
|
|
13756
|
+
const bodies = values.filter((item2) => !spec2.title.includes(item2.flag)).map((item2) => sourceOf(item2.value, spec2.file.includes(item2.flag), segment, heredocs));
|
|
13632
13757
|
return { target: target2, title: title === void 0 ? void 0 : sourceOf(title.value, false, segment, heredocs), bodies };
|
|
13633
13758
|
};
|
|
13634
13759
|
var gitSubcommand = (args2) => {
|
|
@@ -13738,8 +13863,8 @@ var proseCommandGuard = (input) => {
|
|
|
13738
13863
|
const found = proseInCommand(optionalString(toolInput2(input.payload), "command") ?? "");
|
|
13739
13864
|
if (found.length === 0) return ALLOW4;
|
|
13740
13865
|
const blocking2 = projectPreset(input.runtime, projectRoot(input)) === "gabriel";
|
|
13741
|
-
for (const
|
|
13742
|
-
const decision = decisionFor(input,
|
|
13866
|
+
for (const item2 of found) {
|
|
13867
|
+
const decision = decisionFor(input, item2, blocking2);
|
|
13743
13868
|
if (decision !== void 0) return decision;
|
|
13744
13869
|
}
|
|
13745
13870
|
return ALLOW4;
|
|
@@ -14031,24 +14156,24 @@ ${right}`;
|
|
|
14031
14156
|
var mergeSpecific = (left, right) => {
|
|
14032
14157
|
if (!isRecord(left)) return right;
|
|
14033
14158
|
if (!isRecord(right)) return left;
|
|
14034
|
-
const
|
|
14035
|
-
|
|
14159
|
+
const merged2 = { ...left, ...right };
|
|
14160
|
+
merged2["additionalContext"] = joinText(left["additionalContext"], right["additionalContext"]);
|
|
14036
14161
|
const leftRank = DECISION_RANK[String(left["permissionDecision"])] ?? -1;
|
|
14037
14162
|
const rightRank = DECISION_RANK[String(right["permissionDecision"])] ?? -1;
|
|
14038
14163
|
if (leftRank > rightRank) {
|
|
14039
|
-
|
|
14040
|
-
|
|
14164
|
+
merged2["permissionDecision"] = left["permissionDecision"];
|
|
14165
|
+
merged2["permissionDecisionReason"] = left["permissionDecisionReason"];
|
|
14041
14166
|
}
|
|
14042
|
-
return Object.fromEntries(Object.entries(
|
|
14167
|
+
return Object.fromEntries(Object.entries(merged2).filter(([, value]) => value !== void 0));
|
|
14043
14168
|
};
|
|
14044
14169
|
var mergeOutputs = (left, right) => {
|
|
14045
14170
|
if (right === void 0) return left;
|
|
14046
|
-
const
|
|
14171
|
+
const merged2 = { ...left, ...right };
|
|
14047
14172
|
const systemMessage = joinText(left["systemMessage"], right["systemMessage"]);
|
|
14048
|
-
if (systemMessage !== void 0)
|
|
14173
|
+
if (systemMessage !== void 0) merged2["systemMessage"] = systemMessage;
|
|
14049
14174
|
const specific = mergeSpecific(left["hookSpecificOutput"], right["hookSpecificOutput"]);
|
|
14050
|
-
if (specific !== void 0)
|
|
14051
|
-
return
|
|
14175
|
+
if (specific !== void 0) merged2["hookSpecificOutput"] = specific;
|
|
14176
|
+
return merged2;
|
|
14052
14177
|
};
|
|
14053
14178
|
var composeDecisions = async (input, handlers) => {
|
|
14054
14179
|
let output = {};
|
|
@@ -14060,14 +14185,14 @@ var composeDecisions = async (input, handlers) => {
|
|
|
14060
14185
|
return { kind: "allow", output };
|
|
14061
14186
|
};
|
|
14062
14187
|
var mergeHandlers = (...sets) => {
|
|
14063
|
-
const
|
|
14188
|
+
const merged2 = {};
|
|
14064
14189
|
for (const set of sets) {
|
|
14065
14190
|
for (const event of HOOK_EVENTS) {
|
|
14066
14191
|
const list4 = set[event];
|
|
14067
|
-
if (list4 !== void 0)
|
|
14192
|
+
if (list4 !== void 0) merged2[event] = [...merged2[event] ?? [], ...list4];
|
|
14068
14193
|
}
|
|
14069
14194
|
}
|
|
14070
|
-
return
|
|
14195
|
+
return merged2;
|
|
14071
14196
|
};
|
|
14072
14197
|
var SECRETS_HANDLERS = {
|
|
14073
14198
|
PreToolUse: [secretsPreToolUse],
|
|
@@ -14235,7 +14360,7 @@ var expand = (text6, vars) => text6.replace(/\$\{(\w+)\}|\$(\w+)/g, (match, brac
|
|
|
14235
14360
|
const name = braced ?? bare ?? "";
|
|
14236
14361
|
return vars[name] ?? match;
|
|
14237
14362
|
});
|
|
14238
|
-
var strings3 = (value) => Array.isArray(value) ? value.filter((
|
|
14363
|
+
var strings3 = (value) => Array.isArray(value) ? value.filter((item2) => typeof item2 === "string") : [];
|
|
14239
14364
|
var invocationOf = (entry2, vars) => {
|
|
14240
14365
|
const raw = optionalString(entry2.handler, "command");
|
|
14241
14366
|
if (raw === void 0 || raw.trim() === "") return void 0;
|
|
@@ -14536,7 +14661,7 @@ var PRECEDENCE2 = ["managed", "local", "project", "user"];
|
|
|
14536
14661
|
var pluginEnabled = (env) => {
|
|
14537
14662
|
const all = readSettings(env);
|
|
14538
14663
|
for (const scope2 of PRECEDENCE2) {
|
|
14539
|
-
const value = asRecord(all.find((
|
|
14664
|
+
const value = asRecord(all.find((item2) => item2.scope === scope2)?.settings["enabledPlugins"])[PLUGIN_ID];
|
|
14540
14665
|
if (typeof value === "boolean") return { value, scope: scope2 };
|
|
14541
14666
|
}
|
|
14542
14667
|
return void 0;
|
|
@@ -14900,7 +15025,7 @@ var toItem = (ctx, finding3) => ({
|
|
|
14900
15025
|
detail: ctx.t(finding3.messageKey, finding3.params),
|
|
14901
15026
|
...finding3.fixKey === void 0 ? {} : { fix: ctx.t(finding3.fixKey, finding3.params) }
|
|
14902
15027
|
});
|
|
14903
|
-
var count3 = (items, status) => items.filter((
|
|
15028
|
+
var count3 = (items, status) => items.filter((item2) => item2.status === status).length;
|
|
14904
15029
|
var hookItems = (ctx, findings) => findings.flatMap(
|
|
14905
15030
|
(finding3) => finding3.hook === void 0 ? [] : [
|
|
14906
15031
|
{
|
|
@@ -14922,18 +15047,18 @@ var buildReport2 = (ctx, findings) => {
|
|
|
14922
15047
|
|
|
14923
15048
|
// src/commands/doctor.ts
|
|
14924
15049
|
var MARK = { ok: "ok ", warn: "warn", fail: "FAIL" };
|
|
14925
|
-
var itemLines = (ctx,
|
|
14926
|
-
`[${MARK[
|
|
14927
|
-
...
|
|
15050
|
+
var itemLines = (ctx, item2) => [
|
|
15051
|
+
`[${MARK[item2.status]}] ${item2.check}: ${item2.detail}`,
|
|
15052
|
+
...item2.fix !== void 0 && item2.status !== "ok" ? [` ${ctx.t("output.fix", { fix: item2.fix })}`] : []
|
|
14928
15053
|
];
|
|
14929
15054
|
var doctorResult = (ctx, report2, extra = []) => {
|
|
14930
15055
|
const human2 = [
|
|
14931
15056
|
ctx.t("doctor.title", { version: ctx.version }),
|
|
14932
15057
|
...extra,
|
|
14933
|
-
...report2.checks.flatMap((
|
|
15058
|
+
...report2.checks.flatMap((item2) => itemLines(ctx, item2)),
|
|
14934
15059
|
ctx.t("doctor.summary", report2.summary)
|
|
14935
15060
|
];
|
|
14936
|
-
const failed4 = report2.checks.find((
|
|
15061
|
+
const failed4 = report2.checks.find((item2) => item2.status === "fail");
|
|
14937
15062
|
if (failed4 === void 0) return { ok: true, exitCode: ExitCode.Ok, data: report2, human: human2 };
|
|
14938
15063
|
return {
|
|
14939
15064
|
ok: false,
|
|
@@ -14957,7 +15082,7 @@ var fixLines = (ctx, fixed) => {
|
|
|
14957
15082
|
...fixed.backup === void 0 ? [] : [ctx.t("doctor.fix.backup", { path: fixed.backup })]
|
|
14958
15083
|
];
|
|
14959
15084
|
};
|
|
14960
|
-
var manualLines = (ctx, report2) => report2.checks.some((
|
|
15085
|
+
var manualLines = (ctx, report2) => report2.checks.some((item2) => item2.status !== "ok" && item2.fix !== void 0) ? [ctx.t("doctor.fix.manual")] : [];
|
|
14961
15086
|
var writeReport = (ctx, data, result) => {
|
|
14962
15087
|
const env = doctorEnvFrom(ctx);
|
|
14963
15088
|
const path = reportPath(env);
|
|
@@ -15092,6 +15217,172 @@ var gateCommand = {
|
|
|
15092
15217
|
}
|
|
15093
15218
|
};
|
|
15094
15219
|
|
|
15220
|
+
// src/githooks/commit-msg.ts
|
|
15221
|
+
var COMMIT_TYPES = [
|
|
15222
|
+
"feat",
|
|
15223
|
+
"fix",
|
|
15224
|
+
"docs",
|
|
15225
|
+
"style",
|
|
15226
|
+
"refactor",
|
|
15227
|
+
"perf",
|
|
15228
|
+
"test",
|
|
15229
|
+
"build",
|
|
15230
|
+
"ci",
|
|
15231
|
+
"chore",
|
|
15232
|
+
"revert"
|
|
15233
|
+
];
|
|
15234
|
+
var MAX_HEADER = 100;
|
|
15235
|
+
var HEADER = new RegExp(`^(?:${COMMIT_TYPES.join("|")})(?:\\([^()\\r\\n]+\\))?!?: \\S`);
|
|
15236
|
+
var EXEMPT = /^(?:Merge |Revert "|fixup! |squash! |amend! |Initial commit$)/;
|
|
15237
|
+
var AI_NAMES = /claude|anthropic|copilot|chatgpt|openai|cursor|gemini|codex|devin|aider/i;
|
|
15238
|
+
var ATTRIBUTION = [
|
|
15239
|
+
/^co-authored-by:.*$/gim,
|
|
15240
|
+
/generated (?:with|by) \[?(?:claude|copilot|chatgpt|cursor|gemini|codex)/i,
|
|
15241
|
+
/🤖 generated/i,
|
|
15242
|
+
/claude\.ai\/code\//i
|
|
15243
|
+
];
|
|
15244
|
+
var meaningful2 = (message) => message.replace(/\r\n/g, "\n").split("\n").filter((line2) => !line2.startsWith("#"));
|
|
15245
|
+
var hasAttribution = (body2) => {
|
|
15246
|
+
const [coAuthor, ...rest] = ATTRIBUTION;
|
|
15247
|
+
const coAuthors = coAuthor === void 0 ? [] : body2.match(coAuthor) ?? [];
|
|
15248
|
+
return coAuthors.some((line2) => AI_NAMES.test(line2)) || rest.some((pattern) => pattern.test(body2));
|
|
15249
|
+
};
|
|
15250
|
+
var commitMessageProblems = (message) => {
|
|
15251
|
+
const lines3 = meaningful2(message);
|
|
15252
|
+
const header = lines3.find((line2) => line2.trim() !== "") ?? "";
|
|
15253
|
+
const problems = [];
|
|
15254
|
+
if (!EXEMPT.test(header) && !HEADER.test(header)) problems.push("header");
|
|
15255
|
+
if (header.length > MAX_HEADER) problems.push("header_length");
|
|
15256
|
+
if (hasAttribution(lines3.join("\n"))) problems.push("attribution");
|
|
15257
|
+
return problems;
|
|
15258
|
+
};
|
|
15259
|
+
|
|
15260
|
+
// src/githooks/checks.ts
|
|
15261
|
+
var DIFF_FLAGS2 = ["-U0", "--no-color", "--no-ext-diff", "--no-renames"];
|
|
15262
|
+
var short = (sha) => sha.slice(0, 12);
|
|
15263
|
+
var secretsInRange = (runtime, root, range) => {
|
|
15264
|
+
const log = runtime.git(["log", "-p", "--format=commit %H", ...DIFF_FLAGS2, ...range, "--"], root);
|
|
15265
|
+
const baseline = loadBaseline(runtime, root);
|
|
15266
|
+
const accepted2 = acceptedFingerprints(baseline.ok ? baseline.value : EMPTY_BASELINE, runtime.now());
|
|
15267
|
+
const found = log.ok ? blocking(diffFindings(log.stdout)).filter((item2) => !accepted2.has(item2.fingerprint)) : [];
|
|
15268
|
+
const failures = log.ok ? found.map(
|
|
15269
|
+
(item2) => `${item2.ruleId} at ${item2.file}:${String(item2.line)}${item2.commit === void 0 ? "" : ` (${short(item2.commit)})`}`
|
|
15270
|
+
) : [`git log ${range.join(" ")} failed`];
|
|
15271
|
+
return { id: "secrets", failures, fix: "finitemonkey secrets scan --history" };
|
|
15272
|
+
};
|
|
15273
|
+
var RECORD = "";
|
|
15274
|
+
var commitMessagesInRange = (runtime, root, range) => {
|
|
15275
|
+
const log = runtime.git(["log", "--no-merges", `--format=%H%x00%B${RECORD}`, ...range, "--"], root);
|
|
15276
|
+
const failures = log.ok ? log.stdout.split(RECORD).map((record3) => record3.replace(/^\n/, "")).filter((record3) => record3.includes("\0")).flatMap((record3) => {
|
|
15277
|
+
const [sha = "", body2 = ""] = record3.split("\0");
|
|
15278
|
+
return commitMessageProblems(body2).map((problem4) => `${short(sha)} ${problem4}`);
|
|
15279
|
+
}) : [`git log ${range.join(" ")} failed`];
|
|
15280
|
+
return { id: "commit-messages", failures, fix: "git rebase -i <base> and reword the listed commits" };
|
|
15281
|
+
};
|
|
15282
|
+
var mergesInRange = (runtime, repo, range) => {
|
|
15283
|
+
const listed4 = runtime.git(["rev-list", "--merges", ...range, "--"], repo.root);
|
|
15284
|
+
const merges = listed4.ok ? listed4.stdout.split("\n").filter((line2) => line2 !== "") : [];
|
|
15285
|
+
const failures = merges.flatMap((sha) => {
|
|
15286
|
+
const report2 = verifyMerge(runtime, repo, { rev: sha });
|
|
15287
|
+
if (report2.exit === 0) return [];
|
|
15288
|
+
return [
|
|
15289
|
+
report2.exit === 2 ? `${short(sha)} could not be analyzed` : `${short(sha)} dropped ${String(report2.findings.length)} line(s)`
|
|
15290
|
+
];
|
|
15291
|
+
});
|
|
15292
|
+
return { id: "merges", failures, fix: "finitemonkey merge verify <merge-commit>" };
|
|
15293
|
+
};
|
|
15294
|
+
var failing = (checks) => checks.filter((check2) => check2.failures.length > 0);
|
|
15295
|
+
|
|
15296
|
+
// src/githooks/ci.ts
|
|
15297
|
+
var ciBase = (runtime, repo, env, base) => {
|
|
15298
|
+
if (base !== void 0 && base !== "") return base;
|
|
15299
|
+
const target2 = env["GITHUB_BASE_REF"];
|
|
15300
|
+
if (target2 !== void 0 && target2 !== "") return `origin/${target2}`;
|
|
15301
|
+
return `origin/${repoFacts(runtime, repo).defaultBranch()}`;
|
|
15302
|
+
};
|
|
15303
|
+
var baseExists = (runtime, repo, base) => runtime.git(["rev-parse", "--verify", "--quiet", `${base}^{commit}`], repo.root).ok;
|
|
15304
|
+
var ciChecks2 = (runtime, repo, base) => {
|
|
15305
|
+
const range = [`${base}..HEAD`];
|
|
15306
|
+
return [
|
|
15307
|
+
secretsInRange(runtime, repo.root, range),
|
|
15308
|
+
commitMessagesInRange(runtime, repo.root, range),
|
|
15309
|
+
mergesInRange(runtime, repo, range)
|
|
15310
|
+
];
|
|
15311
|
+
};
|
|
15312
|
+
|
|
15313
|
+
// src/githooks/pre-push.ts
|
|
15314
|
+
var ZERO2 = /^0+$/;
|
|
15315
|
+
var parsePushUpdates = (stdin) => stdin.split(/\r?\n/).map((line2) => line2.trim().split(/\s+/)).flatMap(
|
|
15316
|
+
([localRef, localSha, remoteRef, remoteSha]) => localRef === void 0 || localSha === void 0 || remoteRef === void 0 || remoteSha === void 0 ? [] : [{ localRef, localSha, remoteRef, remoteSha }]
|
|
15317
|
+
);
|
|
15318
|
+
var updatesFromEnv = (env) => {
|
|
15319
|
+
const from = env["PRE_COMMIT_FROM_REF"];
|
|
15320
|
+
const to = env["PRE_COMMIT_TO_REF"];
|
|
15321
|
+
if (from === void 0 || to === void 0 || from === "" || to === "") return [];
|
|
15322
|
+
const branch = env["PRE_COMMIT_REMOTE_BRANCH"] ?? "";
|
|
15323
|
+
return [{ localRef: to, localSha: to, remoteRef: branch, remoteSha: from }];
|
|
15324
|
+
};
|
|
15325
|
+
var branchOf = (ref) => ref.replace(/^refs\/heads\//, "");
|
|
15326
|
+
var isDeletion = (update) => ZERO2.test(update.localSha);
|
|
15327
|
+
var isNewRef = (update) => ZERO2.test(update.remoteSha);
|
|
15328
|
+
var rangeOf = (update, remote) => isNewRef(update) ? [update.localSha, "--not", `--remotes=${remote}`] : [`${update.remoteSha}..${update.localSha}`];
|
|
15329
|
+
var rewrites = (runtime, repo, update) => !isNewRef(update) && !isDeletion(update) && !runtime.git(["merge-base", "--is-ancestor", update.remoteSha, update.localSha], repo.root).ok;
|
|
15330
|
+
var protectedCheck = (runtime, repo, updates) => {
|
|
15331
|
+
const facts = repoFacts(runtime, repo);
|
|
15332
|
+
const failures = updates.flatMap((update) => {
|
|
15333
|
+
const branch = branchOf(update.remoteRef);
|
|
15334
|
+
if (!update.remoteRef.startsWith("refs/heads/") || !facts.isProtected(branch)) return [];
|
|
15335
|
+
if (isDeletion(update)) return [`deletes protected branch ${branch}`];
|
|
15336
|
+
return rewrites(runtime, repo, update) ? [`rewrites protected branch ${branch} (force push)`] : [];
|
|
15337
|
+
});
|
|
15338
|
+
return { id: "protected", failures, fix: "push to a new branch and open a pull request" };
|
|
15339
|
+
};
|
|
15340
|
+
var merged = (id, fix, checks) => ({
|
|
15341
|
+
id,
|
|
15342
|
+
fix,
|
|
15343
|
+
failures: [...new Set(checks.flatMap((check2) => check2.failures))]
|
|
15344
|
+
});
|
|
15345
|
+
var prePushChecks = (runtime, repo, remote, updates) => {
|
|
15346
|
+
const pushed = updates.filter((update) => !isDeletion(update));
|
|
15347
|
+
const ranges = pushed.map((update) => rangeOf(update, remote));
|
|
15348
|
+
const secrets = ranges.map((range) => secretsInRange(runtime, repo.root, range));
|
|
15349
|
+
const messages = ranges.map((range) => commitMessagesInRange(runtime, repo.root, range));
|
|
15350
|
+
const merges = ranges.map((range) => mergesInRange(runtime, repo, range));
|
|
15351
|
+
return [
|
|
15352
|
+
protectedCheck(runtime, repo, updates),
|
|
15353
|
+
merged("secrets", "finitemonkey secrets scan --history", secrets),
|
|
15354
|
+
merged("commit-messages", "git rebase -i <base> and reword the listed commits", messages),
|
|
15355
|
+
merged("merges", "finitemonkey merge verify <merge-commit>", merges)
|
|
15356
|
+
];
|
|
15357
|
+
};
|
|
15358
|
+
|
|
15359
|
+
// src/githooks/render.ts
|
|
15360
|
+
var MAX_LISTED8 = 5;
|
|
15361
|
+
var detailOf = (check2) => {
|
|
15362
|
+
const listed4 = check2.failures.slice(0, MAX_LISTED8).join("; ");
|
|
15363
|
+
const rest = check2.failures.length - MAX_LISTED8;
|
|
15364
|
+
return rest > 0 ? `${listed4}; +${String(rest)}` : listed4;
|
|
15365
|
+
};
|
|
15366
|
+
var checkLine = (t, check2) => check2.failures.length === 0 ? t("githooks.pass", { check: check2.id }) : t("githooks.fail", { check: check2.id, detail: detailOf(check2), fix: check2.fix });
|
|
15367
|
+
var checksResult = (t, schema, code, checks) => {
|
|
15368
|
+
const data = { schema, checks };
|
|
15369
|
+
const bad = failing(checks);
|
|
15370
|
+
const human2 = bad.length === 0 ? checks.map((check2) => checkLine(t, check2)) : bad.map((check2) => checkLine(t, check2));
|
|
15371
|
+
if (bad.length === 0) return success(data, human2);
|
|
15372
|
+
const [first] = bad;
|
|
15373
|
+
return {
|
|
15374
|
+
ok: false,
|
|
15375
|
+
exitCode: ExitCode.CheckFailed,
|
|
15376
|
+
data,
|
|
15377
|
+
human: human2,
|
|
15378
|
+
error: {
|
|
15379
|
+
code,
|
|
15380
|
+
message: t("githooks.error.failed", { count: bad.length }),
|
|
15381
|
+
fix: first?.fix ?? ""
|
|
15382
|
+
}
|
|
15383
|
+
};
|
|
15384
|
+
};
|
|
15385
|
+
|
|
15095
15386
|
// src/commands/git-problems.ts
|
|
15096
15387
|
var notRepository = (dir) => ({
|
|
15097
15388
|
code: "not_a_git_repo",
|
|
@@ -15103,14 +15394,90 @@ var notRepository = (dir) => ({
|
|
|
15103
15394
|
var gitProblem = (code, messageKey, detail, fix, exitCode = ExitCode.CheckFailed) => ({ code, messageKey, params: { detail }, fix, exitCode });
|
|
15104
15395
|
var repoOf2 = (ctx) => openRepo(ctx.runtime, ctx.cwd);
|
|
15105
15396
|
|
|
15397
|
+
// src/commands/git-checks.ts
|
|
15398
|
+
var COMMIT_MSG_SCHEMA = "finitemonkey/commit-msg@1";
|
|
15399
|
+
var PUSH_CHECK_SCHEMA = "finitemonkey/push-check@1";
|
|
15400
|
+
var CI_GATE_SCHEMA = "finitemonkey/ci-gate@1";
|
|
15401
|
+
var COMMIT_MSG_USAGE = "finitemonkey commit-msg <message-file>";
|
|
15402
|
+
var commitMsgCommand = {
|
|
15403
|
+
path: ["commit-msg"],
|
|
15404
|
+
summaryKey: "githooks.command.commit_msg",
|
|
15405
|
+
usage: COMMIT_MSG_USAGE,
|
|
15406
|
+
status: "ready",
|
|
15407
|
+
maxPositionals: 1,
|
|
15408
|
+
run: (ctx, args2) => {
|
|
15409
|
+
const file = args2.positionals[0];
|
|
15410
|
+
if (file === void 0)
|
|
15411
|
+
return Promise.resolve(
|
|
15412
|
+
failureFrom(
|
|
15413
|
+
ctx.t,
|
|
15414
|
+
usageProblem("invalid_arguments", "error.invalid_arguments", COMMIT_MSG_USAGE, { detail: "<message-file>" })
|
|
15415
|
+
)
|
|
15416
|
+
);
|
|
15417
|
+
const text6 = ctx.runtime.readText(toAbsolutePath(file, ctx.cwd));
|
|
15418
|
+
if (!text6.ok)
|
|
15419
|
+
return Promise.resolve(
|
|
15420
|
+
failureFrom(
|
|
15421
|
+
ctx.t,
|
|
15422
|
+
usageProblem("invalid_arguments", "error.invalid_arguments", COMMIT_MSG_USAGE, { detail: file })
|
|
15423
|
+
)
|
|
15424
|
+
);
|
|
15425
|
+
const failures = commitMessageProblems(text6.value);
|
|
15426
|
+
const fix = "git commit --amend, using type(scope): subject and no AI attribution lines";
|
|
15427
|
+
return Promise.resolve(
|
|
15428
|
+
checksResult(ctx.t, COMMIT_MSG_SCHEMA, "githook_failed", [{ id: "commit-messages", failures, fix }])
|
|
15429
|
+
);
|
|
15430
|
+
}
|
|
15431
|
+
};
|
|
15432
|
+
var pushCheckCommand = {
|
|
15433
|
+
path: ["push", "check"],
|
|
15434
|
+
summaryKey: "githooks.command.push_check",
|
|
15435
|
+
usage: "finitemonkey push check [<remote>] < pre-push stdin",
|
|
15436
|
+
status: "ready",
|
|
15437
|
+
maxPositionals: 2,
|
|
15438
|
+
run: async (ctx, args2) => {
|
|
15439
|
+
const repo = repoOf2(ctx);
|
|
15440
|
+
if (repo === void 0) return failureFrom(ctx.t, notRepository(ctx.cwd));
|
|
15441
|
+
const piped = parsePushUpdates(ctx.io.stdin.isTTY === true ? "" : await readAll(ctx.io.stdin));
|
|
15442
|
+
const updates = piped.length > 0 ? piped : updatesFromEnv(ctx.env);
|
|
15443
|
+
const checks = prePushChecks(ctx.runtime, repo, args2.positionals[0] ?? "origin", updates);
|
|
15444
|
+
return checksResult(ctx.t, PUSH_CHECK_SCHEMA, "githook_failed", checks);
|
|
15445
|
+
}
|
|
15446
|
+
};
|
|
15447
|
+
var ciGateCommand = {
|
|
15448
|
+
path: ["ci", "gate"],
|
|
15449
|
+
summaryKey: "githooks.command.ci_gate",
|
|
15450
|
+
usage: "finitemonkey ci gate [--base <ref>]",
|
|
15451
|
+
status: "ready",
|
|
15452
|
+
options: { base: { type: "string" } },
|
|
15453
|
+
run: (ctx, args2) => {
|
|
15454
|
+
const repo = repoOf2(ctx);
|
|
15455
|
+
if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
|
|
15456
|
+
const base = ciBase(ctx.runtime, repo, ctx.env, stringOption(args2.values, "base"));
|
|
15457
|
+
if (!baseExists(ctx.runtime, repo, base))
|
|
15458
|
+
return Promise.resolve(
|
|
15459
|
+
failureFrom(
|
|
15460
|
+
ctx.t,
|
|
15461
|
+
gitProblem(
|
|
15462
|
+
"githook_failed",
|
|
15463
|
+
"githooks.error.no_base",
|
|
15464
|
+
base,
|
|
15465
|
+
`git fetch origin && finitemonkey ci gate --base ${base}`
|
|
15466
|
+
)
|
|
15467
|
+
)
|
|
15468
|
+
);
|
|
15469
|
+
return Promise.resolve(checksResult(ctx.t, CI_GATE_SCHEMA, "ci_gate_failed", ciChecks2(ctx.runtime, repo, base)));
|
|
15470
|
+
}
|
|
15471
|
+
};
|
|
15472
|
+
|
|
15106
15473
|
// src/commands/git-merge-verify.ts
|
|
15107
15474
|
var MERGE_VERIFY_RUN_SCHEMA = "finitemonkey/merge-verify-run@1";
|
|
15108
|
-
var
|
|
15109
|
-
var humanOf = (ctx,
|
|
15110
|
-
const { report: report2 } =
|
|
15475
|
+
var short2 = (sha) => (sha ?? "?").slice(0, 12);
|
|
15476
|
+
var humanOf = (ctx, item2) => {
|
|
15477
|
+
const { report: report2 } = item2;
|
|
15111
15478
|
const range = {
|
|
15112
|
-
base:
|
|
15113
|
-
result:
|
|
15479
|
+
base: short2(report2.base),
|
|
15480
|
+
result: item2.commit === "MERGE_HEAD" ? "working tree" : short2(item2.commit)
|
|
15114
15481
|
};
|
|
15115
15482
|
if (report2.exit === 2) return [ctx.t("git.error.merge_unanalyzable", { detail: report2.error ?? "" })];
|
|
15116
15483
|
if (report2.exit === 0) {
|
|
@@ -15139,15 +15506,15 @@ var targetsOf2 = (ctx, repo, rev, since) => {
|
|
|
15139
15506
|
return headIsMerge(ctx, repo) ? ["HEAD"] : [];
|
|
15140
15507
|
};
|
|
15141
15508
|
var resultOf = (ctx, checked) => {
|
|
15142
|
-
const human2 = [...checked.flatMap((
|
|
15509
|
+
const human2 = [...checked.flatMap((item2) => humanOf(ctx, item2)), ctx.t("git.merge.limits")];
|
|
15143
15510
|
const data = { schema: MERGE_VERIFY_RUN_SCHEMA, merges: checked };
|
|
15144
|
-
const broken = checked.find((
|
|
15511
|
+
const broken = checked.find((item2) => item2.report.exit === 2);
|
|
15145
15512
|
if (broken !== void 0) {
|
|
15146
15513
|
const detail = broken.report.error ?? "";
|
|
15147
15514
|
const problem4 = gitProblem("merge_unanalyzable", "git.error.merge_unanalyzable", detail, "git status");
|
|
15148
15515
|
return failureFrom(ctx.t, { ...problem4, exitCode: ExitCode.Usage, data }, human2);
|
|
15149
15516
|
}
|
|
15150
|
-
const lost = checked.reduce((count4,
|
|
15517
|
+
const lost = checked.reduce((count4, item2) => count4 + item2.report.findings.length, 0);
|
|
15151
15518
|
if (lost === 0) return success(data, human2);
|
|
15152
15519
|
return failureFrom(
|
|
15153
15520
|
ctx.t,
|
|
@@ -15330,7 +15697,7 @@ var parentsOf2 = (runtime, repo, commit2) => {
|
|
|
15330
15697
|
}, {});
|
|
15331
15698
|
};
|
|
15332
15699
|
var viewOf = (runtime, repo, ref, journal) => {
|
|
15333
|
-
const entry2 = journal.find((
|
|
15700
|
+
const entry2 = journal.find((item2) => item2.ref === ref);
|
|
15334
15701
|
if (entry2 !== void 0) {
|
|
15335
15702
|
return { ref, commit: entry2.commit, at: entry2.at, op: entry2.op, head: entry2.head, index: entry2.index };
|
|
15336
15703
|
}
|
|
@@ -15745,7 +16112,7 @@ var valueProblems = (lang, catalog, reference) => Object.keys(catalog).sort().fl
|
|
|
15745
16112
|
var pluralProblems = (lang, key, value, source) => {
|
|
15746
16113
|
const own = choicesOf(value);
|
|
15747
16114
|
return choicesOf(source).flatMap((choice) => {
|
|
15748
|
-
const found = own.find((
|
|
16115
|
+
const found = own.find((item2) => item2.name === choice.name && item2.kind === choice.kind);
|
|
15749
16116
|
const expected = choice.kind === "plural" ? requiredPluralBranches(lang) : choice.branches;
|
|
15750
16117
|
const actual = found?.branches ?? [];
|
|
15751
16118
|
const missingBranch = expected.some((branch) => !actual.includes(branch));
|
|
@@ -16112,11 +16479,11 @@ var authorityWarning = (t, authority) => ({
|
|
|
16112
16479
|
source: authority.source
|
|
16113
16480
|
})
|
|
16114
16481
|
});
|
|
16115
|
-
var itemWarning = (t,
|
|
16116
|
-
if (
|
|
16117
|
-
return [{ code: "foreign_file", message: t("install.warn.foreign", { path:
|
|
16118
|
-
if (
|
|
16119
|
-
return [{ code: "edited_file", message: t("install.warn.edited", { path:
|
|
16482
|
+
var itemWarning = (t, item2) => {
|
|
16483
|
+
if (item2.action === "skip-foreign")
|
|
16484
|
+
return [{ code: "foreign_file", message: t("install.warn.foreign", { path: item2.path }) }];
|
|
16485
|
+
if (item2.action === "keep-edited")
|
|
16486
|
+
return [{ code: "edited_file", message: t("install.warn.edited", { path: item2.path }) }];
|
|
16120
16487
|
return [];
|
|
16121
16488
|
};
|
|
16122
16489
|
var planWarnings = (t, plan) => [
|
|
@@ -16125,10 +16492,10 @@ var planWarnings = (t, plan) => [
|
|
|
16125
16492
|
...plan.detection.location.wslMount ? [{ code: "wsl_mount", message: t("install.warn.wsl_mount", { path: plan.detection.root }) }] : [],
|
|
16126
16493
|
...plan.detection.location.synced ? [{ code: "synced_folder", message: t("install.warn.synced_folder", { path: plan.detection.root }) }] : [],
|
|
16127
16494
|
...plan.detection.authorities.map((authority) => authorityWarning(t, authority)),
|
|
16128
|
-
...plan.items.flatMap((
|
|
16495
|
+
...plan.items.flatMap((item2) => itemWarning(t, item2))
|
|
16129
16496
|
];
|
|
16130
16497
|
var planDiff = (plan) => plan.items.flatMap(
|
|
16131
|
-
(
|
|
16498
|
+
(item2) => item2.action === "keep-edited" || item2.action === "skip-foreign" ? lineDiff(item2.path, item2.before ?? "", item2.proposal ?? item2.after) : lineDiff(item2.path, item2.before ?? "", item2.after)
|
|
16132
16499
|
);
|
|
16133
16500
|
var planData = (t, plan, mode, applied, backup) => ({
|
|
16134
16501
|
schema: PLAN_SCHEMA,
|
|
@@ -16489,7 +16856,7 @@ var acceptEntry = (ctx, args2) => {
|
|
|
16489
16856
|
return failure3(ctx, baselineInvalid(baseline.error));
|
|
16490
16857
|
}
|
|
16491
16858
|
const scanned = scanPath(ctx.runtime, root, ".");
|
|
16492
|
-
const finding3 = scanned.ok ? scanned.value.find((
|
|
16859
|
+
const finding3 = scanned.ok ? scanned.value.find((item2) => item2.id === args2.id) : void 0;
|
|
16493
16860
|
if (finding3 === void 0) {
|
|
16494
16861
|
return failure3(
|
|
16495
16862
|
ctx,
|
|
@@ -16499,7 +16866,7 @@ var acceptEntry = (ctx, args2) => {
|
|
|
16499
16866
|
);
|
|
16500
16867
|
}
|
|
16501
16868
|
const entry2 = entryOf3(ctx, finding3, args2);
|
|
16502
|
-
const entries = [...baseline.value.entries.filter((
|
|
16869
|
+
const entries = [...baseline.value.entries.filter((item2) => item2.id !== entry2.id), entry2];
|
|
16503
16870
|
const saved = saveBaseline(ctx.runtime, root, { schema: baseline.value.schema, entries });
|
|
16504
16871
|
if (!saved.ok) {
|
|
16505
16872
|
return failure3(ctx, writeFailed(BASELINE_FILE2, saved.error));
|
|
@@ -16603,7 +16970,7 @@ var list2 = (ctx, root) => {
|
|
|
16603
16970
|
}));
|
|
16604
16971
|
const human2 = incidents.length === 0 ? [ctx.t("secrets.incident.none")] : [
|
|
16605
16972
|
ctx.t("secrets.incident.title", { count: incidents.length }),
|
|
16606
|
-
...incidents.flatMap((
|
|
16973
|
+
...incidents.flatMap((item2) => itemLines2(ctx, item2))
|
|
16607
16974
|
];
|
|
16608
16975
|
return success({ schema: "finitemonkey/secrets-incidents@1", incidents }, human2);
|
|
16609
16976
|
};
|
|
@@ -16866,26 +17233,26 @@ var dataOf = (mode, outcome2, incidents) => {
|
|
|
16866
17233
|
var humanOf2 = (ctx, data) => [
|
|
16867
17234
|
ctx.t("secrets.scan.title", { mode: data.mode, ...data.summary }),
|
|
16868
17235
|
...data.findings.map(
|
|
16869
|
-
(
|
|
16870
|
-
status:
|
|
16871
|
-
rule:
|
|
16872
|
-
where: `${
|
|
16873
|
-
id:
|
|
16874
|
-
mask:
|
|
17236
|
+
(item2) => ctx.t("secrets.scan.item", {
|
|
17237
|
+
status: item2.status,
|
|
17238
|
+
rule: item2.rule,
|
|
17239
|
+
where: `${item2.file}:${String(item2.line)}`,
|
|
17240
|
+
id: item2.id,
|
|
17241
|
+
mask: item2.mask
|
|
16875
17242
|
})
|
|
16876
17243
|
),
|
|
16877
17244
|
...data.summary.total === 0 ? [ctx.t("secrets.scan.clean")] : []
|
|
16878
17245
|
];
|
|
16879
17246
|
var resultOf3 = (ctx, data) => {
|
|
16880
17247
|
const human2 = humanOf2(ctx, data);
|
|
16881
|
-
const
|
|
16882
|
-
if (
|
|
17248
|
+
const failing2 = data.findings.filter((item2) => item2.status !== "known" && item2.severity !== "warning").length;
|
|
17249
|
+
if (failing2 === 0) return success(data, human2);
|
|
16883
17250
|
return failureFrom(
|
|
16884
17251
|
ctx.t,
|
|
16885
17252
|
{
|
|
16886
17253
|
code: "secrets_found",
|
|
16887
17254
|
messageKey: "secrets.error.found",
|
|
16888
|
-
params: { count:
|
|
17255
|
+
params: { count: failing2 },
|
|
16889
17256
|
fix: "finitemonkey secrets accept <id> --reason <why>",
|
|
16890
17257
|
exitCode: ExitCode.CheckFailed,
|
|
16891
17258
|
data
|
|
@@ -16946,18 +17313,18 @@ var inline2 = (value) => {
|
|
|
16946
17313
|
};
|
|
16947
17314
|
var lines2 = (value, indent) => {
|
|
16948
17315
|
if (Array.isArray(value)) {
|
|
16949
|
-
return value.flatMap((
|
|
16950
|
-
const flat = inline2(
|
|
17316
|
+
return value.flatMap((item2) => {
|
|
17317
|
+
const flat = inline2(item2);
|
|
16951
17318
|
if (flat !== void 0) return [`${indent}- ${flat}`];
|
|
16952
|
-
const [head = "", ...rest] = lines2(
|
|
17319
|
+
const [head = "", ...rest] = lines2(item2, `${indent} `);
|
|
16953
17320
|
return [`${indent}- ${head.trimStart()}`, ...rest];
|
|
16954
17321
|
});
|
|
16955
17322
|
}
|
|
16956
17323
|
if (isScalar(value)) return [`${indent}${scalarText(value)}`];
|
|
16957
|
-
return Object.entries(value).flatMap(([key,
|
|
16958
|
-
const flat = inline2(
|
|
17324
|
+
return Object.entries(value).flatMap(([key, item2]) => {
|
|
17325
|
+
const flat = inline2(item2);
|
|
16959
17326
|
const name = scalarText(key);
|
|
16960
|
-
return flat === void 0 ? [`${indent}${name}:`, ...lines2(
|
|
17327
|
+
return flat === void 0 ? [`${indent}${name}:`, ...lines2(item2, `${indent} `)] : [`${indent}${name}: ${flat}`];
|
|
16961
17328
|
});
|
|
16962
17329
|
};
|
|
16963
17330
|
var stringifyYaml = (value) => `${lines2(value, "").join("\n")}
|
|
@@ -17088,9 +17455,9 @@ var noRepo = (ctx) => failureFrom(ctx.t, problem3("not_a_git_repo", "error.not_a
|
|
|
17088
17455
|
var list3 = (values, name) => {
|
|
17089
17456
|
const value = values[name];
|
|
17090
17457
|
const raw = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
17091
|
-
return raw.filter((
|
|
17458
|
+
return raw.filter((item2) => typeof item2 === "string" && item2.trim() !== "");
|
|
17092
17459
|
};
|
|
17093
|
-
var csv = (values, name) => list3(values, name).flatMap((
|
|
17460
|
+
var csv = (values, name) => list3(values, name).flatMap((item2) => item2.split(",").map((part2) => part2.trim())).filter((part2) => part2 !== "");
|
|
17094
17461
|
var positive3 = (values, name) => {
|
|
17095
17462
|
const parsed = Number.parseInt(stringOption(values, name) ?? "", 10);
|
|
17096
17463
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : void 0;
|
|
@@ -17098,7 +17465,7 @@ var positive3 = (values, name) => {
|
|
|
17098
17465
|
var draftFrom = (ctx, paths, id, values) => {
|
|
17099
17466
|
const files = positive3(values, "budget-files");
|
|
17100
17467
|
const lines3 = positive3(values, "budget-lines");
|
|
17101
|
-
const size = SIZES.find((
|
|
17468
|
+
const size = SIZES.find((item2) => item2 === stringOption(values, "size")) ?? "S";
|
|
17102
17469
|
return {
|
|
17103
17470
|
id,
|
|
17104
17471
|
goal: stringOption(values, "goal") ?? id,
|
|
@@ -17107,7 +17474,7 @@ var draftFrom = (ctx, paths, id, values) => {
|
|
|
17107
17474
|
paths: csv(values, "paths"),
|
|
17108
17475
|
runs: list3(values, "run"),
|
|
17109
17476
|
tests: stringOption(values, "tests"),
|
|
17110
|
-
git: csv(values, "git").filter((
|
|
17477
|
+
git: csv(values, "git").filter((item2) => GIT_REQUIREMENTS.some((known2) => known2 === item2)),
|
|
17111
17478
|
manual: list3(values, "manual"),
|
|
17112
17479
|
budget: files === void 0 && lines3 === void 0 ? void 0 : { ...files === void 0 ? {} : { files }, ...lines3 === void 0 ? {} : { lines: lines3 } }
|
|
17113
17480
|
};
|
|
@@ -17219,11 +17586,18 @@ var taskDoneCommand = {
|
|
|
17219
17586
|
run: (ctx, args2) => Promise.resolve(doneTask(ctx, args2.positionals[0]))
|
|
17220
17587
|
};
|
|
17221
17588
|
|
|
17589
|
+
// src/install/githook-block.ts
|
|
17590
|
+
var withoutHookBlock = (text6) => withoutManagedBlock(text6, { begin: BLOCK_BEGIN, end: BLOCK_END });
|
|
17591
|
+
|
|
17222
17592
|
// src/install/uninstall.ts
|
|
17593
|
+
var blockless = (kind, current2) => {
|
|
17594
|
+
if (kind === "gitignore") return withoutBlock(current2);
|
|
17595
|
+
return kind === "markdown" ? withoutMarkdownBlock(current2) : withoutHookBlock(current2);
|
|
17596
|
+
};
|
|
17223
17597
|
var structural = (entry2, current2) => {
|
|
17224
|
-
if (entry2.kind === "gitignore" || entry2.kind === "markdown") {
|
|
17225
|
-
const text6 = entry2.kind
|
|
17226
|
-
return text6 === "" && !entry2.existed ? void 0 : text6;
|
|
17598
|
+
if (entry2.kind === "gitignore" || entry2.kind === "markdown" || entry2.kind === "githook") {
|
|
17599
|
+
const text6 = blockless(entry2.kind, current2);
|
|
17600
|
+
return text6.trim() === "" && !entry2.existed ? void 0 : text6;
|
|
17227
17601
|
}
|
|
17228
17602
|
const parsed = parseSettings(current2);
|
|
17229
17603
|
if (!parsed.ok || entry2.added === void 0) return current2;
|
|
@@ -17254,13 +17628,13 @@ var planRemoval = (project, lock, options) => ({
|
|
|
17254
17628
|
purge: options.purge,
|
|
17255
17629
|
removeDirs: lock.createdDirs.filter((dir) => dir !== STATE_DIR).toSorted((a, b) => b.split("/").length - a.split("/").length)
|
|
17256
17630
|
});
|
|
17257
|
-
var applyItem = (project,
|
|
17258
|
-
if (
|
|
17259
|
-
const removed = project.runtime.remove(project.abs(
|
|
17260
|
-
return removed.ok ? removed : reject({ path:
|
|
17631
|
+
var applyItem = (project, item2) => {
|
|
17632
|
+
if (item2.action === "delete") {
|
|
17633
|
+
const removed = project.runtime.remove(project.abs(item2.path));
|
|
17634
|
+
return removed.ok ? removed : reject({ path: item2.path, detail: removed.error });
|
|
17261
17635
|
}
|
|
17262
|
-
if (
|
|
17263
|
-
return writeText(project,
|
|
17636
|
+
if (item2.after === void 0 || item2.action === "gone" || item2.action.startsWith("keep")) return accept(void 0);
|
|
17637
|
+
return writeText(project, item2.path, item2.after);
|
|
17264
17638
|
};
|
|
17265
17639
|
var isEmptyDir = (project, relative9) => {
|
|
17266
17640
|
const listed4 = project.runtime.listDir(project.abs(relative9));
|
|
@@ -17275,8 +17649,8 @@ var finish3 = (project, plan) => {
|
|
|
17275
17649
|
};
|
|
17276
17650
|
var applyRemoval = (project, plan) => {
|
|
17277
17651
|
const changed = withInstallLock(project, () => {
|
|
17278
|
-
for (const
|
|
17279
|
-
const done = applyItem(project,
|
|
17652
|
+
for (const item2 of plan.items) {
|
|
17653
|
+
const done = applyItem(project, item2);
|
|
17280
17654
|
if (!done.ok) return done;
|
|
17281
17655
|
}
|
|
17282
17656
|
return accept(void 0);
|
|
@@ -17301,7 +17675,7 @@ var dataOf2 = (root, plan, applied) => ({
|
|
|
17301
17675
|
purge: plan.purge,
|
|
17302
17676
|
root,
|
|
17303
17677
|
actions: plan.items.map(({ path, action }) => ({ path, action })),
|
|
17304
|
-
kept: plan.items.filter((
|
|
17678
|
+
kept: plan.items.filter((item2) => item2.action.startsWith("keep")).map(({ path }) => path),
|
|
17305
17679
|
outside: OUTSIDE.map(({ command }) => command)
|
|
17306
17680
|
});
|
|
17307
17681
|
var linesOf2 = (ctx, data) => [
|
|
@@ -17422,6 +17796,9 @@ var REGISTRY = [
|
|
|
17422
17796
|
rescueCommand,
|
|
17423
17797
|
mergeVerifyCommand,
|
|
17424
17798
|
appCheckCommand,
|
|
17799
|
+
commitMsgCommand,
|
|
17800
|
+
pushCheckCommand,
|
|
17801
|
+
ciGateCommand,
|
|
17425
17802
|
versionCommand,
|
|
17426
17803
|
helpCommand
|
|
17427
17804
|
];
|