finitemonkey 0.1.0-next.21 → 0.1.0-next.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.es.md +1 -0
- package/README.md +1 -0
- package/README.pt-BR.md +1 -0
- package/dist/finitemonkey.mjs +974 -412
- package/package.json +1 -1
package/dist/finitemonkey.mjs
CHANGED
|
@@ -870,6 +870,92 @@ var parseLock = (text7) => {
|
|
|
870
870
|
var renderLock = (lock) => `${JSON.stringify(lock, null, 2)}
|
|
871
871
|
`;
|
|
872
872
|
|
|
873
|
+
// src/git/project-config.ts
|
|
874
|
+
import { join as join2 } from "node:path";
|
|
875
|
+
var CONFIG_FILE2 = ".finitemonkey/config.yml";
|
|
876
|
+
var unquote = (value) => value.trim().replace(/^(["'])(.*)\1$/s, "$2");
|
|
877
|
+
var stripComment = (line2) => {
|
|
878
|
+
let quote;
|
|
879
|
+
for (let index = 0; index < line2.length; index += 1) {
|
|
880
|
+
const char = line2.charAt(index);
|
|
881
|
+
if (quote === void 0 && (char === '"' || char === "'")) quote = char;
|
|
882
|
+
else if (char === quote) quote = void 0;
|
|
883
|
+
else if (quote === void 0 && char === "#" && (index === 0 || /\s/.test(line2.charAt(index - 1)))) {
|
|
884
|
+
return line2.slice(0, index);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
return line2;
|
|
888
|
+
};
|
|
889
|
+
var flowList = (value) => {
|
|
890
|
+
const trimmed = value.trim();
|
|
891
|
+
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
|
|
892
|
+
return trimmed.slice(1, -1).split(",").map(unquote).filter((item2) => item2 !== "");
|
|
893
|
+
};
|
|
894
|
+
var keyValue = (body2) => {
|
|
895
|
+
const match = /^("[^"]*"|'[^']*'|[^:]+?)\s*:(?:\s+(.*))?$/.exec(body2);
|
|
896
|
+
if (match === null) return void 0;
|
|
897
|
+
return { key: unquote(match[1] ?? ""), value: match[2] ?? "" };
|
|
898
|
+
};
|
|
899
|
+
var addItem = (frame, item2) => {
|
|
900
|
+
if (frame.key === void 0) return;
|
|
901
|
+
const parent = frame.tree[frame.key];
|
|
902
|
+
if (Array.isArray(parent)) parent.push(item2);
|
|
903
|
+
else frame.tree[frame.key] = [item2];
|
|
904
|
+
};
|
|
905
|
+
var placeLine = (stack, indent, body2) => {
|
|
906
|
+
while (stack.length > 1 && (stack[stack.length - 1]?.indent ?? 0) >= indent && !body2.startsWith("- ")) stack.pop();
|
|
907
|
+
const top = stack[stack.length - 1];
|
|
908
|
+
if (top === void 0) return;
|
|
909
|
+
if (body2.startsWith("- ")) {
|
|
910
|
+
addItem(top, unquote(body2.slice(2)));
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
const pair = keyValue(body2);
|
|
914
|
+
if (pair === void 0) return;
|
|
915
|
+
const parentTree = top.key === void 0 ? top.tree : ensureTree(top.tree, top.key);
|
|
916
|
+
if (pair.value === "") {
|
|
917
|
+
stack.push({ indent, tree: parentTree, key: pair.key });
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
parentTree[pair.key] = flowList(pair.value) ?? unquote(pair.value);
|
|
921
|
+
};
|
|
922
|
+
var ensureTree = (tree, key) => {
|
|
923
|
+
const existing = tree[key];
|
|
924
|
+
if (existing !== void 0 && typeof existing !== "string" && !Array.isArray(existing)) return existing;
|
|
925
|
+
const fresh3 = {};
|
|
926
|
+
tree[key] = fresh3;
|
|
927
|
+
return fresh3;
|
|
928
|
+
};
|
|
929
|
+
var parseConfigYaml = (text7) => {
|
|
930
|
+
const root = {};
|
|
931
|
+
const stack = [{ indent: -1, tree: root, key: void 0 }];
|
|
932
|
+
for (const raw of text7.split(/\r?\n/)) {
|
|
933
|
+
const line2 = stripComment(raw).trimEnd();
|
|
934
|
+
if (line2.trim() === "" || line2.trim() === "---") continue;
|
|
935
|
+
const indent = line2.length - line2.trimStart().length;
|
|
936
|
+
placeLine(stack, indent, line2.trim());
|
|
937
|
+
}
|
|
938
|
+
return root;
|
|
939
|
+
};
|
|
940
|
+
var isTree = (node) => typeof node === "object" && !Array.isArray(node);
|
|
941
|
+
var configAt = (tree, ...path) => path.reduce((node, key) => isTree(node) ? node[key] : void 0, tree);
|
|
942
|
+
var configList = (tree, ...path) => {
|
|
943
|
+
const node = configAt(tree, ...path);
|
|
944
|
+
if (typeof node === "string") return node === "" ? [] : [node];
|
|
945
|
+
return Array.isArray(node) ? node.filter((item2) => typeof item2 === "string") : [];
|
|
946
|
+
};
|
|
947
|
+
var configMap = (tree, ...path) => {
|
|
948
|
+
const node = configAt(tree, ...path);
|
|
949
|
+
if (!isTree(node)) return {};
|
|
950
|
+
return Object.fromEntries(
|
|
951
|
+
Object.entries(node).filter((entry2) => typeof entry2[1] === "string")
|
|
952
|
+
);
|
|
953
|
+
};
|
|
954
|
+
var loadProjectConfig = (runtime, root) => {
|
|
955
|
+
const text7 = runtime.readText(toAbsolutePath(join2(root, CONFIG_FILE2)));
|
|
956
|
+
return text7.ok ? parseConfigYaml(text7.value) : {};
|
|
957
|
+
};
|
|
958
|
+
|
|
873
959
|
// src/install/managed-block.ts
|
|
874
960
|
var lineBreak = (text7) => text7.includes("\r\n") ? "\r\n" : "\n";
|
|
875
961
|
var blockRange = (lines3, markers) => {
|
|
@@ -1145,94 +1231,6 @@ var gitHookItems = (project, runner) => {
|
|
|
1145
1231
|
|
|
1146
1232
|
// src/policy/policy.ts
|
|
1147
1233
|
import { join as join3 } from "node:path";
|
|
1148
|
-
|
|
1149
|
-
// src/git/project-config.ts
|
|
1150
|
-
import { join as join2 } from "node:path";
|
|
1151
|
-
var CONFIG_FILE2 = ".finitemonkey/config.yml";
|
|
1152
|
-
var unquote = (value) => value.trim().replace(/^(["'])(.*)\1$/s, "$2");
|
|
1153
|
-
var stripComment = (line2) => {
|
|
1154
|
-
let quote;
|
|
1155
|
-
for (let index = 0; index < line2.length; index += 1) {
|
|
1156
|
-
const char = line2.charAt(index);
|
|
1157
|
-
if (quote === void 0 && (char === '"' || char === "'")) quote = char;
|
|
1158
|
-
else if (char === quote) quote = void 0;
|
|
1159
|
-
else if (quote === void 0 && char === "#" && (index === 0 || /\s/.test(line2.charAt(index - 1)))) {
|
|
1160
|
-
return line2.slice(0, index);
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
return line2;
|
|
1164
|
-
};
|
|
1165
|
-
var flowList = (value) => {
|
|
1166
|
-
const trimmed = value.trim();
|
|
1167
|
-
if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
|
|
1168
|
-
return trimmed.slice(1, -1).split(",").map(unquote).filter((item2) => item2 !== "");
|
|
1169
|
-
};
|
|
1170
|
-
var keyValue = (body2) => {
|
|
1171
|
-
const match = /^("[^"]*"|'[^']*'|[^:]+?)\s*:(?:\s+(.*))?$/.exec(body2);
|
|
1172
|
-
if (match === null) return void 0;
|
|
1173
|
-
return { key: unquote(match[1] ?? ""), value: match[2] ?? "" };
|
|
1174
|
-
};
|
|
1175
|
-
var addItem = (frame, item2) => {
|
|
1176
|
-
if (frame.key === void 0) return;
|
|
1177
|
-
const parent = frame.tree[frame.key];
|
|
1178
|
-
if (Array.isArray(parent)) parent.push(item2);
|
|
1179
|
-
else frame.tree[frame.key] = [item2];
|
|
1180
|
-
};
|
|
1181
|
-
var placeLine = (stack, indent, body2) => {
|
|
1182
|
-
while (stack.length > 1 && (stack[stack.length - 1]?.indent ?? 0) >= indent && !body2.startsWith("- ")) stack.pop();
|
|
1183
|
-
const top = stack[stack.length - 1];
|
|
1184
|
-
if (top === void 0) return;
|
|
1185
|
-
if (body2.startsWith("- ")) {
|
|
1186
|
-
addItem(top, unquote(body2.slice(2)));
|
|
1187
|
-
return;
|
|
1188
|
-
}
|
|
1189
|
-
const pair = keyValue(body2);
|
|
1190
|
-
if (pair === void 0) return;
|
|
1191
|
-
const parentTree = top.key === void 0 ? top.tree : ensureTree(top.tree, top.key);
|
|
1192
|
-
if (pair.value === "") {
|
|
1193
|
-
stack.push({ indent, tree: parentTree, key: pair.key });
|
|
1194
|
-
return;
|
|
1195
|
-
}
|
|
1196
|
-
parentTree[pair.key] = flowList(pair.value) ?? unquote(pair.value);
|
|
1197
|
-
};
|
|
1198
|
-
var ensureTree = (tree, key) => {
|
|
1199
|
-
const existing = tree[key];
|
|
1200
|
-
if (existing !== void 0 && typeof existing !== "string" && !Array.isArray(existing)) return existing;
|
|
1201
|
-
const fresh3 = {};
|
|
1202
|
-
tree[key] = fresh3;
|
|
1203
|
-
return fresh3;
|
|
1204
|
-
};
|
|
1205
|
-
var parseConfigYaml = (text7) => {
|
|
1206
|
-
const root = {};
|
|
1207
|
-
const stack = [{ indent: -1, tree: root, key: void 0 }];
|
|
1208
|
-
for (const raw of text7.split(/\r?\n/)) {
|
|
1209
|
-
const line2 = stripComment(raw).trimEnd();
|
|
1210
|
-
if (line2.trim() === "" || line2.trim() === "---") continue;
|
|
1211
|
-
const indent = line2.length - line2.trimStart().length;
|
|
1212
|
-
placeLine(stack, indent, line2.trim());
|
|
1213
|
-
}
|
|
1214
|
-
return root;
|
|
1215
|
-
};
|
|
1216
|
-
var isTree = (node) => typeof node === "object" && !Array.isArray(node);
|
|
1217
|
-
var configAt = (tree, ...path) => path.reduce((node, key) => isTree(node) ? node[key] : void 0, tree);
|
|
1218
|
-
var configList = (tree, ...path) => {
|
|
1219
|
-
const node = configAt(tree, ...path);
|
|
1220
|
-
if (typeof node === "string") return node === "" ? [] : [node];
|
|
1221
|
-
return Array.isArray(node) ? node.filter((item2) => typeof item2 === "string") : [];
|
|
1222
|
-
};
|
|
1223
|
-
var configMap = (tree, ...path) => {
|
|
1224
|
-
const node = configAt(tree, ...path);
|
|
1225
|
-
if (!isTree(node)) return {};
|
|
1226
|
-
return Object.fromEntries(
|
|
1227
|
-
Object.entries(node).filter((entry2) => typeof entry2[1] === "string")
|
|
1228
|
-
);
|
|
1229
|
-
};
|
|
1230
|
-
var loadProjectConfig = (runtime, root) => {
|
|
1231
|
-
const text7 = runtime.readText(toAbsolutePath(join2(root, CONFIG_FILE2)));
|
|
1232
|
-
return text7.ok ? parseConfigYaml(text7.value) : {};
|
|
1233
|
-
};
|
|
1234
|
-
|
|
1235
|
-
// src/policy/policy.ts
|
|
1236
1234
|
var POLICY_FILE = ".finitemonkey/policy.yml";
|
|
1237
1235
|
var GUARD_MODES = ["off", "shadow", "active"];
|
|
1238
1236
|
var LEVELS = ["light", "standard", "strict"];
|
|
@@ -1271,6 +1269,65 @@ var loadPolicy = (runtime, root) => {
|
|
|
1271
1269
|
return text7.ok ? parsePolicy(text7.value) : DEFAULT_POLICY;
|
|
1272
1270
|
};
|
|
1273
1271
|
|
|
1272
|
+
// src/review/agents.ts
|
|
1273
|
+
var REVIEW_AGENTS = ["reviewer", "verifier"];
|
|
1274
|
+
var FORBIDDEN_TOOLS = ["Edit", "Write", "MultiEdit", "NotebookEdit", "Agent", "Task"];
|
|
1275
|
+
var reviewAgentOf = (agentType) => REVIEW_AGENTS.find((name) => agentType === name || agentType.endsWith(`:${name}`));
|
|
1276
|
+
var REVIEWER_BODY = [
|
|
1277
|
+
"# Reviewer",
|
|
1278
|
+
"",
|
|
1279
|
+
"You review a change you did not write. Your input is the review packet in your prompt: the diff, the task contract, the evidence ledger and, when present, the code owners' REVIEW.md. You never see the conversation that produced the change, and you must not ask for it.",
|
|
1280
|
+
"",
|
|
1281
|
+
"1. Read the diff first, then open the files it touches when you need context.",
|
|
1282
|
+
"2. Judge the change against the contract: does each acceptance check have evidence for the current tree?",
|
|
1283
|
+
"3. Write numbered findings in three groups: must fix, should fix, nice to have. For each: what it is, the problem, the fix, and what happens if it is not fixed.",
|
|
1284
|
+
"4. Every must-fix finding carries a reproduction command and the expected result. A finding without one goes under should fix.",
|
|
1285
|
+
"5. End with a verdict: approve, approve with changes, or request changes.",
|
|
1286
|
+
"",
|
|
1287
|
+
"You cannot edit files or start other agents. Report; do not repair."
|
|
1288
|
+
].join("\n");
|
|
1289
|
+
var VERIFIER_BODY = [
|
|
1290
|
+
"# Verifier",
|
|
1291
|
+
"",
|
|
1292
|
+
"You check claims with commands, not with opinions. Your input is the review packet and, when there is one, the reviewer's must-fix findings with their reproduction commands. You never see the conversation that produced the change.",
|
|
1293
|
+
"",
|
|
1294
|
+
"1. Run each reproduction command in a throwaway worktree on the base commit and on the head commit (`git worktree add <tmp> <rev>`), never in the person's working tree.",
|
|
1295
|
+
"2. Record both exit codes. Fails only on head: the finding stands. Fails on both: it was already there. Fails on neither: not reproduced.",
|
|
1296
|
+
"3. Report one line per finding with the command and both results, then remove the worktrees.",
|
|
1297
|
+
"",
|
|
1298
|
+
"You cannot edit files or start other agents. Report; do not repair."
|
|
1299
|
+
].join("\n");
|
|
1300
|
+
var VERIFIER = {
|
|
1301
|
+
name: "verifier",
|
|
1302
|
+
description: "Runs reproduction commands from a review on the base and head commits in throwaway worktrees and reports which findings are real. Read-only.",
|
|
1303
|
+
tools: ["Read", "Grep", "Glob", "Bash"],
|
|
1304
|
+
model: "sonnet",
|
|
1305
|
+
maxTurns: 60,
|
|
1306
|
+
body: VERIFIER_BODY
|
|
1307
|
+
};
|
|
1308
|
+
var PLAYWRIGHT_SERVER = "playwright";
|
|
1309
|
+
var withPlaywright = (spec2) => ({
|
|
1310
|
+
...spec2,
|
|
1311
|
+
tools: [...spec2.tools, `mcp__${PLAYWRIGHT_SERVER}`],
|
|
1312
|
+
mcpServers: [PLAYWRIGHT_SERVER]
|
|
1313
|
+
});
|
|
1314
|
+
var agentMarkdown = (spec2, marker) => [
|
|
1315
|
+
"---",
|
|
1316
|
+
`name: ${spec2.name}`,
|
|
1317
|
+
`description: ${spec2.description}`,
|
|
1318
|
+
`tools: ${spec2.tools.join(", ")}`,
|
|
1319
|
+
`disallowedTools: ${FORBIDDEN_TOOLS.join(", ")}`,
|
|
1320
|
+
`model: ${spec2.model}`,
|
|
1321
|
+
"effort: high",
|
|
1322
|
+
`maxTurns: ${String(spec2.maxTurns)}`,
|
|
1323
|
+
...spec2.mcpServers === void 0 ? [] : ["mcpServers:", ...spec2.mcpServers.map((name) => ` - ${name}`)],
|
|
1324
|
+
"---",
|
|
1325
|
+
"",
|
|
1326
|
+
...marker === void 0 ? [] : [`<!-- ${marker} -->`, ""],
|
|
1327
|
+
spec2.body,
|
|
1328
|
+
""
|
|
1329
|
+
].join("\n");
|
|
1330
|
+
|
|
1274
1331
|
// src/install/agents-block.ts
|
|
1275
1332
|
var MARKDOWN_MARKERS = { begin: MD_BLOCK_BEGIN, end: MD_BLOCK_END };
|
|
1276
1333
|
var IMPORT_LINE = /^@AGENTS\.md[ \t]*$/m;
|
|
@@ -1795,11 +1852,19 @@ var generatedItem = (project, file, previous) => {
|
|
|
1795
1852
|
}
|
|
1796
1853
|
return { ...base, action: actionFor(before, template, "update"), after: template };
|
|
1797
1854
|
};
|
|
1798
|
-
var
|
|
1855
|
+
var PLAYWRIGHT_VERIFIER = ".claude/agents/verifier.md";
|
|
1856
|
+
var playwrightVerifier = (project) => {
|
|
1857
|
+
const wanted = configAt(parseConfigYaml(project.read(CONFIG_FILE) ?? ""), "review", "playwright") === "true";
|
|
1858
|
+
if (!wanted) return [];
|
|
1859
|
+
const template = agentMarkdown(withPlaywright(VERIFIER), GENERATED_MARKER);
|
|
1860
|
+
return [{ path: PLAYWRIGHT_VERIFIER, template, isOurs: (text7) => text7.includes(GENERATED_MARKER) }];
|
|
1861
|
+
};
|
|
1862
|
+
var generatedFiles = (project, input, runner, vendor) => {
|
|
1799
1863
|
const own = [
|
|
1800
1864
|
{ path: CONFIG_FILE, template: configTemplate(input.preset, input.lang), isOurs: hasMarker },
|
|
1801
1865
|
{ path: VERIFY_SKILL, template: verifySkillTemplate(runner, input.lang), isOurs: isOwnSkill },
|
|
1802
|
-
{ path: POLICY_FILE, template: policyTemplate(input.lang), isOurs: hasMarker }
|
|
1866
|
+
{ path: POLICY_FILE, template: policyTemplate(input.lang), isOurs: hasMarker },
|
|
1867
|
+
...playwrightVerifier(project)
|
|
1803
1868
|
];
|
|
1804
1869
|
if (!vendor) return accept(own);
|
|
1805
1870
|
if (input.bundle === void 0) return reject({ kind: "bundle", detail: "the running finitemonkey is not a bundle" });
|
|
@@ -1810,7 +1875,7 @@ var buildPlan = (project, detection, input, previous, migrations = MIGRATIONS) =
|
|
|
1810
1875
|
const settings = settingsItem(project, detection, input, entryFor(previous, SETTINGS_FILE));
|
|
1811
1876
|
if (!settings.ok) return settings;
|
|
1812
1877
|
const vendor = detection.runner.kind === "vendored" || entryFor(previous, VENDOR_BIN) !== void 0;
|
|
1813
|
-
const files = generatedFiles(input, detection.runner, vendor);
|
|
1878
|
+
const files = generatedFiles(project, input, detection.runner, vendor);
|
|
1814
1879
|
if (!files.ok) return files;
|
|
1815
1880
|
const generated = files.value.map((file) => generatedItem(project, file, entryFor(previous, file.path)));
|
|
1816
1881
|
return accept({
|
|
@@ -3141,6 +3206,36 @@ var applyEdits = (before, edits) => edits.reduce(
|
|
|
3141
3206
|
before
|
|
3142
3207
|
);
|
|
3143
3208
|
|
|
3209
|
+
// src/hooks/reason.ts
|
|
3210
|
+
var REASON_LIMIT = 1e4;
|
|
3211
|
+
var familyOf = (base) => {
|
|
3212
|
+
const families = [
|
|
3213
|
+
"gate.guard.bypass",
|
|
3214
|
+
"gate.guard.self",
|
|
3215
|
+
"gate.guard.tests",
|
|
3216
|
+
"secrets.block",
|
|
3217
|
+
"git.block",
|
|
3218
|
+
"prod.block",
|
|
3219
|
+
"prose.block"
|
|
3220
|
+
];
|
|
3221
|
+
const found = families.find((family) => base.startsWith(`${family}.`) || base === family);
|
|
3222
|
+
if (found === void 0) throw new Error(`no reason family for ${base}`);
|
|
3223
|
+
return found;
|
|
3224
|
+
};
|
|
3225
|
+
var capReason = (text7) => text7.length <= REASON_LIMIT ? text7 : `${text7.slice(0, REASON_LIMIT - 1)}…`;
|
|
3226
|
+
var modelReason = (t, base, params2 = {}) => {
|
|
3227
|
+
const claim = `${base}.claim`;
|
|
3228
|
+
const requires = `${base}.requires`;
|
|
3229
|
+
const not = `${familyOf(base)}.not`;
|
|
3230
|
+
return capReason(
|
|
3231
|
+
[
|
|
3232
|
+
`${t("reason.label.blocked")}: ${t(claim, params2)}.`,
|
|
3233
|
+
`${t("reason.label.requires")}: ${t(requires, params2)}.`,
|
|
3234
|
+
`${t("reason.label.not")}: ${t(not, params2)}.`
|
|
3235
|
+
].join("\n")
|
|
3236
|
+
);
|
|
3237
|
+
};
|
|
3238
|
+
|
|
3144
3239
|
// src/core/clock.ts
|
|
3145
3240
|
var nowMs = (runtime) => Date.parse(runtime.now());
|
|
3146
3241
|
|
|
@@ -3254,7 +3349,8 @@ var grantFor = (input, guard) => {
|
|
|
3254
3349
|
const { runtime } = input;
|
|
3255
3350
|
return activeGrant(runtime, fmPaths(guardRoot(input)), guard, nowMs(runtime), input.payload.sessionId);
|
|
3256
3351
|
};
|
|
3257
|
-
var deny = (
|
|
3352
|
+
var deny = (fullReason, humanText) => {
|
|
3353
|
+
const reason = capReason(fullReason);
|
|
3258
3354
|
const output = {
|
|
3259
3355
|
hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: reason },
|
|
3260
3356
|
systemMessage: `finitemonkey: ${humanText}`
|
|
@@ -4044,11 +4140,20 @@ var bypassGuard = (input) => {
|
|
|
4044
4140
|
if (first === void 0 || grantFor(input, "bypass") !== void 0) return { kind: "allow" };
|
|
4045
4141
|
const { t } = input;
|
|
4046
4142
|
return deny(
|
|
4047
|
-
t
|
|
4143
|
+
modelReason(t, `gate.guard.bypass.${first.kind}`, { detail: first.detail }),
|
|
4048
4144
|
t("gate.guard.bypass.human", { detail: first.detail })
|
|
4049
4145
|
);
|
|
4050
4146
|
};
|
|
4051
4147
|
|
|
4148
|
+
// src/guards/review/hook.ts
|
|
4149
|
+
var FORBIDDEN = new Set(FORBIDDEN_TOOLS);
|
|
4150
|
+
var reviewAgentGuard = (input) => {
|
|
4151
|
+
const agent = reviewAgentOf(textField(input.payload.raw, "agent_type") ?? "");
|
|
4152
|
+
const tool = textField(input.payload.raw, "tool_name") ?? "";
|
|
4153
|
+
if (agent === void 0 || !FORBIDDEN.has(tool)) return { kind: "allow" };
|
|
4154
|
+
return { kind: "block", reason: input.t("review.guard.read_only", { agent, tool }) };
|
|
4155
|
+
};
|
|
4156
|
+
|
|
4052
4157
|
// src/guards/self/protected.ts
|
|
4053
4158
|
import { relative as relative2 } from "node:path";
|
|
4054
4159
|
var normalize = (path) => toPosixPath(path).replace(/^\.\//, "");
|
|
@@ -4127,12 +4232,15 @@ var selfGuard = (input) => {
|
|
|
4127
4232
|
const { t } = input;
|
|
4128
4233
|
if (store !== void 0)
|
|
4129
4234
|
return deny(
|
|
4130
|
-
t
|
|
4235
|
+
modelReason(t, "gate.guard.self.allow_store", { file: store.path }),
|
|
4131
4236
|
t("gate.guard.self.human", { file: store.path })
|
|
4132
4237
|
);
|
|
4133
4238
|
const first = found[0];
|
|
4134
4239
|
if (first === void 0 || grantFor(input, "self") !== void 0) return { kind: "allow" };
|
|
4135
|
-
return deny(
|
|
4240
|
+
return deny(
|
|
4241
|
+
modelReason(t, "gate.guard.self.policy", { file: first.path }),
|
|
4242
|
+
t("gate.guard.self.human", { file: first.path })
|
|
4243
|
+
);
|
|
4136
4244
|
};
|
|
4137
4245
|
|
|
4138
4246
|
// src/guards/tests/hook.ts
|
|
@@ -4342,7 +4450,7 @@ var testsGuard = (input) => {
|
|
|
4342
4450
|
const { t } = input;
|
|
4343
4451
|
const params2 = { file: first.path, before: first.before ?? 0, after: first.after ?? 0 };
|
|
4344
4452
|
const reason = SHELL_TOOLS.has(tool) ? t("gate.guard.tests.shell", params2) : t(`gate.tests.${first.kind}`, params2);
|
|
4345
|
-
return deny(t
|
|
4453
|
+
return deny(modelReason(t, "gate.guard.tests", { detail: reason }), t("gate.guard.tests.human", { detail: reason }));
|
|
4346
4454
|
};
|
|
4347
4455
|
|
|
4348
4456
|
// src/look/reads.ts
|
|
@@ -5130,27 +5238,27 @@ var gitAt = (runtime, cwd) => {
|
|
|
5130
5238
|
}
|
|
5131
5239
|
};
|
|
5132
5240
|
};
|
|
5133
|
-
var gitRoot = (
|
|
5134
|
-
const top =
|
|
5241
|
+
var gitRoot = (git2) => {
|
|
5242
|
+
const top = git2.out(["rev-parse", "--show-toplevel"])?.trim();
|
|
5135
5243
|
return top === void 0 || top === "" ? void 0 : toAbsolutePath(top);
|
|
5136
5244
|
};
|
|
5137
5245
|
var nonEmpty = (value) => value === "" ? void 0 : value;
|
|
5138
|
-
var headSha = (
|
|
5139
|
-
var currentBranch = (
|
|
5140
|
-
const name =
|
|
5246
|
+
var headSha = (git2) => git2.out(["rev-parse", "--verify", "-q", "HEAD"])?.trim();
|
|
5247
|
+
var currentBranch = (git2) => {
|
|
5248
|
+
const name = git2.out(["rev-parse", "--abbrev-ref", "HEAD"])?.trim();
|
|
5141
5249
|
return name === void 0 || name === "HEAD" || name === "" ? void 0 : name;
|
|
5142
5250
|
};
|
|
5143
|
-
var defaultRemoteBranch = (
|
|
5144
|
-
var resolveBase = (
|
|
5145
|
-
if (declared !== void 0 &&
|
|
5251
|
+
var defaultRemoteBranch = (git2) => nonEmpty(git2.out(["rev-parse", "--abbrev-ref", "origin/HEAD"])?.trim());
|
|
5252
|
+
var resolveBase = (git2, declared) => {
|
|
5253
|
+
if (declared !== void 0 && git2.out(["rev-parse", "--verify", "-q", `${declared}^{commit}`]) !== void 0) {
|
|
5146
5254
|
return declared;
|
|
5147
5255
|
}
|
|
5148
|
-
const remote = defaultRemoteBranch(
|
|
5149
|
-
const merged2 = remote === void 0 ? void 0 :
|
|
5150
|
-
return nonEmpty(merged2) ?? nonEmpty(headSha(
|
|
5256
|
+
const remote = defaultRemoteBranch(git2);
|
|
5257
|
+
const merged2 = remote === void 0 ? void 0 : git2.out(["merge-base", "HEAD", remote])?.trim();
|
|
5258
|
+
return nonEmpty(merged2) ?? nonEmpty(headSha(git2)) ?? EMPTY_TREE;
|
|
5151
5259
|
};
|
|
5152
5260
|
var nulFields = (text7) => text7.split("\0").filter((part2) => part2 !== "");
|
|
5153
|
-
var untracked = (
|
|
5261
|
+
var untracked = (git2) => nulFields(git2.out(["ls-files", "--others", "--exclude-standard", "-z", "--", ".", EXCLUDE_FM]) ?? "");
|
|
5154
5262
|
var parseNameStatus = (text7) => {
|
|
5155
5263
|
const fields = nulFields(text7);
|
|
5156
5264
|
const changes = [];
|
|
@@ -5170,21 +5278,21 @@ var parseNameStatus = (text7) => {
|
|
|
5170
5278
|
}
|
|
5171
5279
|
return changes;
|
|
5172
5280
|
};
|
|
5173
|
-
var changedFiles = (
|
|
5281
|
+
var changedFiles = (git2, base) => {
|
|
5174
5282
|
const tracked = parseNameStatus(
|
|
5175
|
-
|
|
5283
|
+
git2.out(["diff", "--name-status", "-M", "-z", "--no-color", base, "--", ".", EXCLUDE_FM]) ?? ""
|
|
5176
5284
|
);
|
|
5177
5285
|
const known2 = new Set(tracked.map((change) => change.path));
|
|
5178
|
-
const fresh3 = untracked(
|
|
5286
|
+
const fresh3 = untracked(git2).filter((path) => !known2.has(path)).map((path) => ({ status: "A", path }));
|
|
5179
5287
|
return [...tracked, ...fresh3];
|
|
5180
5288
|
};
|
|
5181
|
-
var fileAt = (
|
|
5182
|
-
var treeHash = (
|
|
5183
|
-
const head = headSha(
|
|
5184
|
-
const diff =
|
|
5289
|
+
var fileAt = (git2, rev, path) => rev === EMPTY_TREE ? void 0 : git2.out(["show", `${rev}:${path}`]);
|
|
5290
|
+
var treeHash = (git2) => {
|
|
5291
|
+
const head = headSha(git2) ?? EMPTY_TREE;
|
|
5292
|
+
const diff = git2.run(["diff", "HEAD", "--binary", "--no-ext-diff", "--no-color", "--", ".", EXCLUDE_FM]);
|
|
5185
5293
|
if (!diff.found || diff.exitCode !== 0 && head !== EMPTY_TREE) return void 0;
|
|
5186
|
-
const files = untracked(
|
|
5187
|
-
const hashes = files.length === 0 ? "" :
|
|
5294
|
+
const files = untracked(git2);
|
|
5295
|
+
const hashes = files.length === 0 ? "" : git2.out(["hash-object", "--", ...files]) ?? "";
|
|
5188
5296
|
const digest = sha256Hex([head, diff.stdout, files.join("\0"), hashes].join("\n--fm--\n"));
|
|
5189
5297
|
return `w${digest.slice(0, 16)}`;
|
|
5190
5298
|
};
|
|
@@ -5203,18 +5311,18 @@ var parseAddedLines = (diff) => {
|
|
|
5203
5311
|
}
|
|
5204
5312
|
return added;
|
|
5205
5313
|
};
|
|
5206
|
-
var addedLines = (
|
|
5207
|
-
var numstat = (
|
|
5208
|
-
const rows = (
|
|
5314
|
+
var addedLines = (git2, base) => parseAddedLines(git2.out(["diff", "-U0", "--no-color", "--no-ext-diff", base, "--", ".", EXCLUDE_FM]) ?? "");
|
|
5315
|
+
var numstat = (git2, base) => {
|
|
5316
|
+
const rows = (git2.out(["diff", "--numstat", "--no-color", base, "--", ".", EXCLUDE_FM]) ?? "").split("\n").filter((row) => row.trim() !== "");
|
|
5209
5317
|
const lines3 = rows.reduce((sum, row) => {
|
|
5210
5318
|
const [plus = "0", minus = "0"] = row.split(" ");
|
|
5211
5319
|
return sum + (Number(plus) || 0) + (Number(minus) || 0);
|
|
5212
5320
|
}, 0);
|
|
5213
5321
|
return { files: rows.length, lines: lines3 };
|
|
5214
5322
|
};
|
|
5215
|
-
var dirtyPaths = (
|
|
5216
|
-
var aheadOfUpstream = (
|
|
5217
|
-
const count4 =
|
|
5323
|
+
var dirtyPaths = (git2) => nulFields(git2.out(["status", "--porcelain", "-z", "--", ".", EXCLUDE_FM]) ?? "").map((entry2) => entry2.slice(3));
|
|
5324
|
+
var aheadOfUpstream = (git2) => {
|
|
5325
|
+
const count4 = git2.out(["rev-list", "--count", "@{u}..HEAD"])?.trim();
|
|
5218
5326
|
return count4 === void 0 ? void 0 : Number(count4);
|
|
5219
5327
|
};
|
|
5220
5328
|
|
|
@@ -5223,9 +5331,9 @@ var untrackedLines = (runtime, root, changes) => changes.filter((change) => chan
|
|
|
5223
5331
|
const text7 = runtime.readText(toAbsolutePath(join14(root, change.path)));
|
|
5224
5332
|
return sum + (text7.ok ? text7.value.split("\n").length : 0);
|
|
5225
5333
|
}, 0);
|
|
5226
|
-
var diffShape = (runtime,
|
|
5334
|
+
var diffShape = (runtime, git2, base, changes) => ({
|
|
5227
5335
|
files: changes.map((change) => change.path),
|
|
5228
|
-
lines: numstat(
|
|
5336
|
+
lines: numstat(git2, base).lines + untrackedLines(runtime, git2.cwd, changes)
|
|
5229
5337
|
});
|
|
5230
5338
|
|
|
5231
5339
|
// src/gate/pr-parse.ts
|
|
@@ -5285,11 +5393,11 @@ var cached = (runtime, paths, branch, ttlMs) => {
|
|
|
5285
5393
|
const view2 = raw["view"];
|
|
5286
5394
|
return typeof view2 === "string" ? parsePrView(view2) : void 0;
|
|
5287
5395
|
};
|
|
5288
|
-
var fetchView = (runtime,
|
|
5289
|
-
const auth = ghAuthenticated(runtime,
|
|
5396
|
+
var fetchView = (runtime, git2, branch) => {
|
|
5397
|
+
const auth = ghAuthenticated(runtime, git2.cwd);
|
|
5290
5398
|
if (auth === "timeout") return { kind: "network", detail: "gh auth status timed out" };
|
|
5291
5399
|
if (auth === "no") return { kind: "no_gh" };
|
|
5292
|
-
const view2 = gh(runtime,
|
|
5400
|
+
const view2 = gh(runtime, git2.cwd, ["pr", "view", branch, "--json", PR_FIELDS]);
|
|
5293
5401
|
if (view2.timedOut) return { kind: "network", detail: "gh pr view timed out" };
|
|
5294
5402
|
if (view2.exitCode !== 0) {
|
|
5295
5403
|
if (/no (open )?pull requests? found|no git remotes/i.test(view2.stderr)) return { kind: "none", branch };
|
|
@@ -5298,20 +5406,20 @@ var fetchView = (runtime, git, branch) => {
|
|
|
5298
5406
|
const pr = parsePrView(view2.stdout);
|
|
5299
5407
|
return pr === void 0 ? { kind: "network", detail: "gh returned unexpected JSON" } : { kind: "fetched", branch, pr, view: view2.stdout };
|
|
5300
5408
|
};
|
|
5301
|
-
var prStatus = (runtime,
|
|
5302
|
-
const branch = currentBranch(
|
|
5409
|
+
var prStatus = (runtime, git2, paths, ttlMs = PR_CACHE_TTL_MS) => {
|
|
5410
|
+
const branch = currentBranch(git2);
|
|
5303
5411
|
if (branch === void 0) return { kind: "no_branch" };
|
|
5304
5412
|
const hit3 = cached(runtime, paths, branch, ttlMs);
|
|
5305
|
-
if (hit3 !== void 0) return { kind: "found", branch, pr: hit3, localHead: headSha(
|
|
5306
|
-
const status = fetchView(runtime,
|
|
5413
|
+
if (hit3 !== void 0) return { kind: "found", branch, pr: hit3, localHead: headSha(git2) };
|
|
5414
|
+
const status = fetchView(runtime, git2, branch);
|
|
5307
5415
|
if (status.kind !== "fetched") return status;
|
|
5308
5416
|
writeJsonFile(runtime, paths.cache("pr.json"), { fetchedAt: nowMs(runtime), branch, view: status.view });
|
|
5309
|
-
return { kind: "found", branch, pr: status.pr, localHead: headSha(
|
|
5417
|
+
return { kind: "found", branch, pr: status.pr, localHead: headSha(git2) };
|
|
5310
5418
|
};
|
|
5311
|
-
var conflictFiles = (
|
|
5419
|
+
var conflictFiles = (git2, base) => {
|
|
5312
5420
|
for (const target2 of [`origin/${base}`, base]) {
|
|
5313
|
-
if (
|
|
5314
|
-
const result =
|
|
5421
|
+
if (git2.out(["rev-parse", "--verify", "-q", `${target2}^{commit}`]) === void 0) continue;
|
|
5422
|
+
const result = git2.run(["merge-tree", "--write-tree", "--name-only", "--no-messages", target2, "HEAD"]);
|
|
5315
5423
|
if (result.found && result.exitCode === 1) return parseMergeTreeConflicts(result.stdout);
|
|
5316
5424
|
if (result.found && result.exitCode === 0) return [];
|
|
5317
5425
|
}
|
|
@@ -5431,15 +5539,15 @@ var diskFiles = (runtime, root) => ({
|
|
|
5431
5539
|
},
|
|
5432
5540
|
exists: (relative11) => runtime.stat(toAbsolutePath(join15(root, relative11))) !== void 0
|
|
5433
5541
|
});
|
|
5434
|
-
var baseFiles = (
|
|
5435
|
-
read: (relative11) => base === EMPTY_TREE ? disk.read(relative11) : fileAt(
|
|
5542
|
+
var baseFiles = (git2, base, disk) => ({
|
|
5543
|
+
read: (relative11) => base === EMPTY_TREE ? disk.read(relative11) : fileAt(git2, base, relative11),
|
|
5436
5544
|
exists: disk.exists
|
|
5437
5545
|
});
|
|
5438
|
-
var loadContract = (options, paths,
|
|
5546
|
+
var loadContract = (options, paths, git2, base) => {
|
|
5439
5547
|
const disk = diskFiles(options.runtime, paths.root);
|
|
5440
5548
|
const id = activeTaskId(options.runtime, paths, options.taskId);
|
|
5441
|
-
if (id === void 0) return accept(loadImplicit(options.runtime, paths, baseFiles(
|
|
5442
|
-
return loadTask(options.runtime, paths, { disk: disk.read, base: (relative11) => fileAt(
|
|
5549
|
+
if (id === void 0) return accept(loadImplicit(options.runtime, paths, baseFiles(git2, base, disk)));
|
|
5550
|
+
return loadTask(options.runtime, paths, { disk: disk.read, base: (relative11) => fileAt(git2, base, relative11) }, id);
|
|
5443
5551
|
};
|
|
5444
5552
|
var memo = (make) => {
|
|
5445
5553
|
let value;
|
|
@@ -5461,30 +5569,30 @@ var skipReason = (runtime, paths, loaded, changes, tree, sessionId) => {
|
|
|
5461
5569
|
const session = readSession(runtime, paths);
|
|
5462
5570
|
return session !== void 0 && session.sessionId === sessionId && session.startTree === tree ? "no_session_edits" : void 0;
|
|
5463
5571
|
};
|
|
5464
|
-
var measureSize = (runtime,
|
|
5572
|
+
var measureSize = (runtime, git2, base, changes, contract) => sizeVerdict(contract.size, diffShape(runtime, git2, base, changes), sizeConfig(loadProjectConfig(runtime, git2.cwd)));
|
|
5465
5573
|
var prepareGate = (options) => {
|
|
5466
5574
|
const probe = gitAt(options.runtime, options.cwd);
|
|
5467
5575
|
const root = gitRoot(probe);
|
|
5468
5576
|
if (root === void 0) {
|
|
5469
5577
|
return options.taskId === void 0 ? { kind: "skipped", reason: "no_repo" } : { kind: "failed", failure: { kind: "not_a_repo" } };
|
|
5470
5578
|
}
|
|
5471
|
-
const
|
|
5579
|
+
const git2 = gitAt(options.runtime, root);
|
|
5472
5580
|
const paths = fmPaths(root);
|
|
5473
5581
|
const trust2 = readTrust(options.runtime, paths);
|
|
5474
|
-
const firstBase = resolveBase(
|
|
5475
|
-
const loaded = loadContract(options, paths,
|
|
5582
|
+
const firstBase = resolveBase(git2, void 0);
|
|
5583
|
+
const loaded = loadContract(options, paths, git2, firstBase);
|
|
5476
5584
|
if (!loaded.ok) return { kind: "failed", failure: { kind: "contract", problem: loaded.error } };
|
|
5477
|
-
const base = resolveBase(
|
|
5478
|
-
const changes = changedFiles(
|
|
5479
|
-
const tree = treeHash(
|
|
5585
|
+
const base = resolveBase(git2, loaded.value.contract.base);
|
|
5586
|
+
const changes = changedFiles(git2, base);
|
|
5587
|
+
const tree = treeHash(git2);
|
|
5480
5588
|
if (tree === void 0) return { kind: "failed", failure: { kind: "no_tree_hash" } };
|
|
5481
5589
|
const skip = options.noSkip === true ? void 0 : skipReason(options.runtime, paths, loaded.value, changes, tree, options.sessionId);
|
|
5482
5590
|
if (skip !== void 0) return { kind: "skipped", reason: skip };
|
|
5483
|
-
const pr = memo(() => prStatus(options.runtime,
|
|
5484
|
-
const size = measureSize(options.runtime,
|
|
5591
|
+
const pr = memo(() => prStatus(options.runtime, git2, paths));
|
|
5592
|
+
const size = measureSize(options.runtime, git2, base, changes, loaded.value.contract);
|
|
5485
5593
|
const ctx = {
|
|
5486
5594
|
runtime: options.runtime,
|
|
5487
|
-
git,
|
|
5595
|
+
git: git2,
|
|
5488
5596
|
paths,
|
|
5489
5597
|
contract: applySize(loaded.value.contract, size),
|
|
5490
5598
|
size,
|
|
@@ -5802,6 +5910,7 @@ var en_default = {
|
|
|
5802
5910
|
"i18n.problem.placeholder": "placeholders differ (expected: {expected}; found: {actual})",
|
|
5803
5911
|
"i18n.problem.plural": "plural branches missing (needs: {expected}; found: {actual})",
|
|
5804
5912
|
"i18n.problem.status": "no review status (use: {expected})",
|
|
5913
|
+
"i18n.problem.release_hint": "release instructions in text the model reads ({actual}); move them to a key the person reads",
|
|
5805
5914
|
"i18n.problem.empty": "empty text",
|
|
5806
5915
|
"i18n.problem.not_string": "value is not text",
|
|
5807
5916
|
"doctor.title": "finitemonkey doctor ({version})",
|
|
@@ -5928,12 +6037,20 @@ var en_default = {
|
|
|
5928
6037
|
"secrets.where.line": "line {line}, column {column}",
|
|
5929
6038
|
"secrets.where.file": "{file}:{line}:{column}",
|
|
5930
6039
|
"secrets.where.new_text": "{file} (line {line} of the new text)",
|
|
5931
|
-
"secrets.block.write": "
|
|
5932
|
-
"secrets.block.
|
|
5933
|
-
"secrets.block.
|
|
5934
|
-
"secrets.block.
|
|
5935
|
-
"secrets.block.
|
|
5936
|
-
"secrets.block.
|
|
6040
|
+
"secrets.block.write.claim": "the new text for {file} has a credential ({found})",
|
|
6041
|
+
"secrets.block.write.requires": "Nothing was written. Keep the value in .env (outside git) and read it by its variable name. If the key is real, treat it as exposed and rotate it",
|
|
6042
|
+
"secrets.block.not": "an encoded, split, commented or renamed copy of the same value",
|
|
6043
|
+
"secrets.block.human": "Kept a credential out of a file, command or commit.",
|
|
6044
|
+
"secrets.block.bash.claim": "this command has a credential ({found}) and did not run",
|
|
6045
|
+
"secrets.block.bash.requires": "Read the value from an environment variable instead of writing it in the command. If the key is real, treat it as exposed and rotate it",
|
|
6046
|
+
"secrets.block.commit.claim": "the staged changes have a credential ({found}), so the commit did not run",
|
|
6047
|
+
"secrets.block.commit.requires": "Remove it from the staged files, keep it in .env (outside git) and rotate it if it is real",
|
|
6048
|
+
"secrets.block.read.claim": "{target} holds secrets, and reading it would put their values in the conversation",
|
|
6049
|
+
"secrets.block.read.requires": "Ask the person for the variable names, or read .env.example",
|
|
6050
|
+
"secrets.block.env_dump.claim": "`{command}` prints every environment variable, secrets included",
|
|
6051
|
+
"secrets.block.env_dump.requires": 'Print only the variable you need by name, or check that it exists with `test -n "$NAME"`',
|
|
6052
|
+
"secrets.block.person_only.claim": "`{command}` is a decision for the person, not the model",
|
|
6053
|
+
"secrets.block.person_only.requires": "Ask the person to run it in their own terminal",
|
|
5937
6054
|
"secrets.block.prompt": "your message was not sent to the model: it has a credential ({found}). If this key was pasted anywhere else, treat it as exposed and rotate it. To use it, put it in .env and tell me only the variable name. Never paste keys into the chat. Incident: {incident}.",
|
|
5938
6055
|
"secrets.post.context": "Finite Monkey: the {tool} output contained a credential ({found}). Incident {incident} is open; rotating the key is the person's decision. Finite Monkey output never repeats the value.",
|
|
5939
6056
|
"secrets.incident.unsaved": "not recorded ({detail})",
|
|
@@ -6126,16 +6243,27 @@ var en_default = {
|
|
|
6126
6243
|
"gate.config.disabled": "Finite Monkey: this change sets disableAllHooks in {file}; it was not applied.",
|
|
6127
6244
|
"gate.config.human": "Kept the Finite Monkey hooks in {file}. If you meant it, type on its own line: finitemonkey: allow config <reason>",
|
|
6128
6245
|
"gate.config.released": "Config change to {file} accepted under your release.",
|
|
6129
|
-
"gate.guard.bypass.no_verify": "
|
|
6130
|
-
"gate.guard.bypass.
|
|
6131
|
-
"gate.guard.bypass.
|
|
6132
|
-
"gate.guard.bypass.
|
|
6133
|
-
"gate.guard.bypass.
|
|
6246
|
+
"gate.guard.bypass.no_verify.claim": "{detail} skips the git hooks",
|
|
6247
|
+
"gate.guard.bypass.no_verify.requires": "the hooks run on every commit and push",
|
|
6248
|
+
"gate.guard.bypass.not": "the same switch through another variable, flag, alias or script",
|
|
6249
|
+
"gate.guard.bypass.hooks_path.claim": "{detail} points git away from the project hooks",
|
|
6250
|
+
"gate.guard.bypass.hooks_path.requires": "core.hooksPath stays as configured",
|
|
6251
|
+
"gate.guard.bypass.hook_env.claim": "{detail} turns hooks or guards off",
|
|
6252
|
+
"gate.guard.bypass.hook_env.requires": "hooks and guards stay on",
|
|
6253
|
+
"gate.guard.bypass.fm_allow.claim": "releases are made by the person, not by a tool call",
|
|
6254
|
+
"gate.guard.bypass.fm_allow.requires": "a release comes from the person typing it in the prompt",
|
|
6255
|
+
"gate.guard.bypass.fm_pause.claim": "only the person pauses or resumes the gate",
|
|
6256
|
+
"gate.guard.bypass.fm_pause.requires": "the gate stays on unless the person pauses it",
|
|
6134
6257
|
"gate.guard.bypass.human": "Blocked {detail}: it would switch the safety net off. If you really want it, type on its own line: finitemonkey: allow bypass <reason>",
|
|
6135
|
-
"gate.guard.self.policy": "
|
|
6136
|
-
"gate.guard.self.
|
|
6258
|
+
"gate.guard.self.policy.claim": "{file} is written only by the finitemonkey executable (evidence, policy and state)",
|
|
6259
|
+
"gate.guard.self.policy.requires": "change it through finitemonkey commands",
|
|
6260
|
+
"gate.guard.self.not": "a copy, move, redirect or script that writes the same file",
|
|
6261
|
+
"gate.guard.self.allow_store.claim": "{file} records what the person released; tools never write it",
|
|
6262
|
+
"gate.guard.self.allow_store.requires": "leave this file to the person",
|
|
6137
6263
|
"gate.guard.self.human": "Kept {file} out of reach: the monkey does not grade its own homework.",
|
|
6138
|
-
"gate.guard.tests.
|
|
6264
|
+
"gate.guard.tests.claim": "{detail}",
|
|
6265
|
+
"gate.guard.tests.requires": "make the code pass the tests as they are",
|
|
6266
|
+
"gate.guard.tests.not": "a test that still runs but no longer checks the behavior",
|
|
6139
6267
|
"gate.guard.tests.shell": "a shell command writes or deletes {file}",
|
|
6140
6268
|
"gate.guard.tests.human": "Blocked: {detail}. Tests are the only witness we have. Fix the code, not the witness. If the change is intended, type on its own line: finitemonkey: allow tests <reason>",
|
|
6141
6269
|
"gate.release.allowed": "Released {guard} until {until}, by your request.",
|
|
@@ -6171,9 +6299,14 @@ var en_default = {
|
|
|
6171
6299
|
"prose.span": "lines {start}-{end}: {rules}",
|
|
6172
6300
|
"prose.fix_item": "{rule}: {fix}",
|
|
6173
6301
|
"prose.warn.command": "{target} reads as machine-written ({spans}). Rewrite it before sending. {fixes}. If it is brand text written on purpose, show the person two alternatives.",
|
|
6174
|
-
"prose.block.command": "
|
|
6175
|
-
"prose.block.
|
|
6176
|
-
"prose.block.
|
|
6302
|
+
"prose.block.command.claim": "{target} reads as machine-written ({spans}), and this project's preset does not send it. {fixes}",
|
|
6303
|
+
"prose.block.command.requires": "Rewrite it and run the command again",
|
|
6304
|
+
"prose.block.not": "the same text with a few words swapped",
|
|
6305
|
+
"prose.block.human": "Held back text that reads as machine-written.",
|
|
6306
|
+
"prose.block.unreadable.claim": "could not read {file} to check its text ({detail})",
|
|
6307
|
+
"prose.block.unreadable.requires": "Check the path and run the command again",
|
|
6308
|
+
"prose.block.marker.claim": "this would add the marker {marker} to {file}, and only the person can add it",
|
|
6309
|
+
"prose.block.marker.requires": "Rewrite the passage instead, or ask the person to add the marker in their own editor",
|
|
6177
6310
|
"prose.check.file": "{file}: {count, plural, one {# passage that reads} other {# passages that read}} as machine-written",
|
|
6178
6311
|
"prose.check.span": " {file}:{start}-{end} score {score}: {rules}",
|
|
6179
6312
|
"prose.check.fix": " do instead: {fix}",
|
|
@@ -6182,7 +6315,8 @@ var en_default = {
|
|
|
6182
6315
|
"prose.error.found": "{count, plural, one {# passage reads} other {# passages read}} as machine-written",
|
|
6183
6316
|
"prose.error.no_input": "nothing to check: pass files, --stdin, or run it inside a git repository to check the changed Markdown files",
|
|
6184
6317
|
"prose.error.unreadable": "could not read {file} ({detail})",
|
|
6185
|
-
"prose.block.marker_shell": "
|
|
6318
|
+
"prose.block.marker_shell.claim": "this command writes the marker {marker}, and only the person can add it",
|
|
6319
|
+
"prose.block.marker_shell.requires": "Rewrite the passage instead, or ask the person to add the marker in their own editor",
|
|
6186
6320
|
"prose.rule.requester-reference": "state the technical reason for the change; leave out who asked for it",
|
|
6187
6321
|
"prose.rule.person-as-reason": "name the property of the change instead of a person",
|
|
6188
6322
|
"prose.rule.origin-project": "describe the technical property you copied instead of naming where it came from",
|
|
@@ -6197,30 +6331,55 @@ var en_default = {
|
|
|
6197
6331
|
"git.command.undo": "List the snapshots or restore one, into a new worktree by default",
|
|
6198
6332
|
"git.command.rescue": "List lost commits from the reflog and fsck, and restore one into a new branch",
|
|
6199
6333
|
"git.command.merge_verify": "Check that a merge kept every change from both sides",
|
|
6200
|
-
"git.block.force_push": "
|
|
6201
|
-
"git.block.
|
|
6202
|
-
"git.block.
|
|
6203
|
-
"git.block.
|
|
6204
|
-
"git.block.
|
|
6205
|
-
"git.block.
|
|
6206
|
-
"git.block.
|
|
6207
|
-
"git.block.
|
|
6208
|
-
"git.block.
|
|
6209
|
-
"git.block.
|
|
6210
|
-
"git.block.
|
|
6211
|
-
"git.block.
|
|
6212
|
-
"git.block.
|
|
6213
|
-
"git.block.
|
|
6214
|
-
"git.block.
|
|
6215
|
-
"git.block.
|
|
6216
|
-
"git.block.
|
|
6334
|
+
"git.block.force_push.claim": "`{command}` rewrites the remote branch and can erase commits someone else pushed",
|
|
6335
|
+
"git.block.force_push.requires": "Fetch, integrate the remote commits and push normally. On your own branch after a rebase, use `git push --force-with-lease=<branch>:<expected sha> --force-if-includes`",
|
|
6336
|
+
"git.block.not": "the same effect through another git command, an alias, a script or a nested shell",
|
|
6337
|
+
"git.block.human": "Stopped a git command that could lose work or rewrite shared history.",
|
|
6338
|
+
"git.block.lease_not_own.claim": "`{command}` forces a push onto {subject}, which is not the branch you are working on or is protected",
|
|
6339
|
+
"git.block.lease_not_own.requires": "A forced push is only for your own branch, with an explicit lease",
|
|
6340
|
+
"git.block.push_delete.claim": "`{command}` deletes {subject} on the remote",
|
|
6341
|
+
"git.block.push_delete.requires": "Deleting a remote branch is for the person to do",
|
|
6342
|
+
"git.block.push_mirror.claim": "`{command}` overwrites every remote ref and would publish the backup refs",
|
|
6343
|
+
"git.block.push_mirror.requires": "Push the branch you need by name",
|
|
6344
|
+
"git.block.clean.claim": "`{command}` deletes untracked files for good; they are in no commit",
|
|
6345
|
+
"git.block.clean.requires": "Run `git clean -n` to see the list, then move what should go into .finitemonkey/trash/ or ask the person",
|
|
6346
|
+
"git.block.stash_drop.claim": "`{command}` throws away stashed work that exists nowhere else",
|
|
6347
|
+
"git.block.stash_drop.requires": "Apply it with `git stash apply` or leave it; dropping a stash is for the person to do",
|
|
6348
|
+
"git.block.branch_unmerged.claim": "`{command}` deletes {subject}, whose commits are neither in its upstream nor in the default branch",
|
|
6349
|
+
"git.block.branch_unmerged.requires": "Push or merge it first; `git branch -d` works once it is merged",
|
|
6350
|
+
"git.block.worktree_force.claim": "`{command}` removes a worktree even with uncommitted changes",
|
|
6351
|
+
"git.block.worktree_force.requires": "Commit or archive its work first; plain `git worktree remove` refuses a dirty tree on purpose",
|
|
6352
|
+
"git.block.rm_worktree.claim": "`{command}` deletes {subject}, which is a git worktree or repository; uncommitted work in it would be lost",
|
|
6353
|
+
"git.block.rm_worktree.requires": "Commit or archive it, then use `git worktree remove` without --force",
|
|
6354
|
+
"git.block.rm_root.claim": "`{command}` recursively deletes {subject}, a root or home directory",
|
|
6355
|
+
"git.block.rm_root.requires": "Name the exact folder to remove",
|
|
6356
|
+
"git.block.rebase_protected.claim": "`{command}` rewrites {subject}, a protected branch",
|
|
6357
|
+
"git.block.rebase_protected.requires": "Change it through a pull request",
|
|
6358
|
+
"git.block.rebase_shared.claim": "`{command}` rewrites {subject}, which already has someone else's commits on the remote",
|
|
6359
|
+
"git.block.rebase_shared.requires": "Merge the base into it instead of rebasing",
|
|
6360
|
+
"git.block.merge_side.claim": "`{command}` resolves conflicts by taking one side whole ({subject}), which silently drops the other side's changes",
|
|
6361
|
+
"git.block.merge_side.requires": "Resolve each conflict by hand and run `finitemonkey merge verify` before committing",
|
|
6362
|
+
"git.block.history.claim": "`{command}` deletes history or reflog entries that undo and rescue depend on",
|
|
6363
|
+
"git.block.history.requires": "Rewriting history is for the person to do",
|
|
6364
|
+
"git.block.protected_commit.claim": "`{command}` would commit straight onto {subject}, a protected branch",
|
|
6365
|
+
"git.block.protected_commit.requires": "Create a branch with `git switch -c <type>/<name>` and open a pull request",
|
|
6366
|
+
"git.block.protected_push.claim": "`{command}` pushes to {subject}, a protected branch",
|
|
6367
|
+
"git.block.protected_push.requires": "Push your own branch and open a pull request",
|
|
6368
|
+
"git.block.foreign.claim": "`{command}` would discard changes this session did not make: {files}",
|
|
6369
|
+
"git.block.foreign.requires": "Undo only your own lines with Edit",
|
|
6217
6370
|
"git.foreign.before": "{path} (had changes before you started)",
|
|
6218
6371
|
"git.foreign.other": "{path} (changed outside this session)",
|
|
6219
|
-
"git.block.unknown": "
|
|
6220
|
-
"git.block.
|
|
6372
|
+
"git.block.unknown.claim": "`{command}` discards changes, and `git status` failed, so there is no telling whose they are",
|
|
6373
|
+
"git.block.unknown.requires": "Fix the repository and try again",
|
|
6374
|
+
"git.block.snapshot_failed.claim": "`{command}` is risky and the safety snapshot before it failed ({detail}), so it did not run",
|
|
6375
|
+
"git.block.snapshot_failed.requires": "Fix the cause and try again",
|
|
6221
6376
|
"git.snapshot.context": "finitemonkey saved a snapshot before `{command}`: {ref}. `finitemonkey undo {ref}` brings the previous state back.",
|
|
6222
|
-
"prod.block.effect": "
|
|
6223
|
-
"prod.block.
|
|
6377
|
+
"prod.block.effect.claim": "`{command}` changes production ({target}) by hand, and a server edited by hand drifts from the repository",
|
|
6378
|
+
"prod.block.effect.requires": "Change it through the deploy path ({deploy}): commit, open a pull request and let the deploy run",
|
|
6379
|
+
"prod.block.not": "the same change through another tool, a script or a nested shell",
|
|
6380
|
+
"prod.block.human": "Stopped a hand edit to production; changes go through the deploy.",
|
|
6381
|
+
"prod.block.interactive.claim": "`{command}` opens an interactive session on production ({target}), where nothing typed can be checked first",
|
|
6382
|
+
"prod.block.interactive.requires": "Run one read-only command at a time, such as `ssh {target} docker ps`",
|
|
6224
6383
|
"prod.deploy.unknown": "the project's declared deploy",
|
|
6225
6384
|
"git.snapshot.done": "snapshot saved: {ref} ({commit}). `finitemonkey undo {ref}` restores it.",
|
|
6226
6385
|
"git.undo.list_title": "{count, plural, one {# snapshot:} other {# snapshots, newest first:}}",
|
|
@@ -6347,7 +6506,14 @@ var en_default = {
|
|
|
6347
6506
|
"gate.look.allow_ignored": "exception ignored, its decision is not recorded: {entry}",
|
|
6348
6507
|
"gate.look.unopened": "{count, plural, one {# capture} other {# captures}} not opened after the last edit to a screen file",
|
|
6349
6508
|
"gate.look.open": "open with Read: {file}",
|
|
6350
|
-
"gate.look.ok": "{count, plural, one {# capture} other {# captures}}, no failure, all opened"
|
|
6509
|
+
"gate.look.ok": "{count, plural, one {# capture} other {# captures}}, no failure, all opened",
|
|
6510
|
+
"reason.label.blocked": "blocked",
|
|
6511
|
+
"reason.label.requires": "requires",
|
|
6512
|
+
"reason.label.not": "does not count",
|
|
6513
|
+
"reason.human.generic": "Blocked a tool call; the reason went to Claude.",
|
|
6514
|
+
"review.command.packet": "Print the review packet for the reviewer and verifier agents: diff, task contract, evidence ledger and REVIEW.md, never the conversation",
|
|
6515
|
+
"review.guard.read_only": "blocked: the {agent} agent is read-only and cannot use {tool}. Report the finding; the main session makes the change.",
|
|
6516
|
+
"review.error.no_base": "the base {detail} is not a commit here"
|
|
6351
6517
|
};
|
|
6352
6518
|
|
|
6353
6519
|
// src/i18n/es.json
|
|
@@ -6396,6 +6562,7 @@ var es_default = {
|
|
|
6396
6562
|
"i18n.problem.placeholder": "marcadores distintos (esperado: {expected}; encontrado: {actual})",
|
|
6397
6563
|
"i18n.problem.plural": "faltan ramas de plural (necesita: {expected}; encontradas: {actual})",
|
|
6398
6564
|
"i18n.problem.status": "sin estado de revisión (use: {expected})",
|
|
6565
|
+
"i18n.problem.release_hint": "instrucción de liberación en texto que lee el modelo ({actual}); muévela a una clave que lee la persona",
|
|
6399
6566
|
"i18n.problem.empty": "texto vacío",
|
|
6400
6567
|
"i18n.problem.not_string": "el valor no es texto",
|
|
6401
6568
|
"doctor.title": "finitemonkey doctor ({version})",
|
|
@@ -6522,12 +6689,20 @@ var es_default = {
|
|
|
6522
6689
|
"secrets.where.line": "línea {line}, columna {column}",
|
|
6523
6690
|
"secrets.where.file": "{file}:{line}:{column}",
|
|
6524
6691
|
"secrets.where.new_text": "{file} (línea {line} del texto nuevo)",
|
|
6525
|
-
"secrets.block.write": "
|
|
6526
|
-
"secrets.block.
|
|
6527
|
-
"secrets.block.
|
|
6528
|
-
"secrets.block.
|
|
6529
|
-
"secrets.block.
|
|
6530
|
-
"secrets.block.
|
|
6692
|
+
"secrets.block.write.claim": "el texto nuevo de {file} tiene una credencial ({found})",
|
|
6693
|
+
"secrets.block.write.requires": "No se escribió nada. Deja el valor en .env (fuera de git) y léelo por el nombre de la variable. Si la clave es real, trátala como expuesta y rótala",
|
|
6694
|
+
"secrets.block.not": "una copia codificada, partida, comentada o renombrada del mismo valor",
|
|
6695
|
+
"secrets.block.human": "Mantuve una credencial fuera de un archivo, comando o commit.",
|
|
6696
|
+
"secrets.block.bash.claim": "este comando tiene una credencial ({found}) y no se ejecutó",
|
|
6697
|
+
"secrets.block.bash.requires": "Lee el valor de una variable de entorno en lugar de escribirlo en el comando. Si la clave es real, trátala como expuesta y rótala",
|
|
6698
|
+
"secrets.block.commit.claim": "los cambios preparados tienen una credencial ({found}), así que el commit no se ejecutó",
|
|
6699
|
+
"secrets.block.commit.requires": "Quítala de los archivos preparados, déjala en .env (fuera de git) y rótala si es real",
|
|
6700
|
+
"secrets.block.read.claim": "{target} guarda secretos, y leerlo pondría sus valores en la conversación",
|
|
6701
|
+
"secrets.block.read.requires": "Pide a la persona los nombres de las variables, o lee .env.example",
|
|
6702
|
+
"secrets.block.env_dump.claim": "`{command}` imprime todas las variables de entorno, secretos incluidos",
|
|
6703
|
+
"secrets.block.env_dump.requires": 'Imprime solo la variable que necesitas por su nombre, o comprueba que existe con `test -n "$NAME"`',
|
|
6704
|
+
"secrets.block.person_only.claim": "`{command}` es una decisión de la persona, no del modelo",
|
|
6705
|
+
"secrets.block.person_only.requires": "Pide a la persona que lo ejecute en su propia terminal",
|
|
6531
6706
|
"secrets.block.prompt": "tu mensaje no se envió al modelo: tiene una credencial ({found}). Si esta clave se pegó en otro lugar, trátala como expuesta y rótala. Para usarla, ponla en .env y dime solo el nombre de la variable. Nunca pegues claves en el chat. Incidente: {incident}.",
|
|
6532
6707
|
"secrets.post.context": "Finite Monkey: la salida de {tool} contenía una credencial ({found}). El incidente {incident} está abierto; rotar la clave es decisión de la persona. La salida de Finite Monkey nunca repite el valor.",
|
|
6533
6708
|
"secrets.incident.unsaved": "no registrado ({detail})",
|
|
@@ -6720,16 +6895,27 @@ var es_default = {
|
|
|
6720
6895
|
"gate.config.disabled": "Finite Monkey: este cambio activa disableAllHooks en {file}; no se aplicó.",
|
|
6721
6896
|
"gate.config.human": "Mantuve los hooks de Finite Monkey en {file}. Si fue a propósito, escriba en una línea: finitemonkey: allow config <motivo>",
|
|
6722
6897
|
"gate.config.released": "Cambio de config en {file} aceptado por su liberación.",
|
|
6723
|
-
"gate.guard.bypass.no_verify": "
|
|
6724
|
-
"gate.guard.bypass.
|
|
6725
|
-
"gate.guard.bypass.
|
|
6726
|
-
"gate.guard.bypass.
|
|
6727
|
-
"gate.guard.bypass.
|
|
6898
|
+
"gate.guard.bypass.no_verify.claim": "{detail} se salta los git hooks",
|
|
6899
|
+
"gate.guard.bypass.no_verify.requires": "los hooks corren en cada commit y push",
|
|
6900
|
+
"gate.guard.bypass.not": "el mismo interruptor con otra variable, flag, alias o script",
|
|
6901
|
+
"gate.guard.bypass.hooks_path.claim": "{detail} desvía git de los hooks del proyecto",
|
|
6902
|
+
"gate.guard.bypass.hooks_path.requires": "core.hooksPath queda como está configurado",
|
|
6903
|
+
"gate.guard.bypass.hook_env.claim": "{detail} apaga hooks o guardas",
|
|
6904
|
+
"gate.guard.bypass.hook_env.requires": "hooks y guardas quedan encendidos",
|
|
6905
|
+
"gate.guard.bypass.fm_allow.claim": "las liberaciones las hace la persona, no una llamada de herramienta",
|
|
6906
|
+
"gate.guard.bypass.fm_allow.requires": "la liberación viene de la persona escribiéndola en el prompt",
|
|
6907
|
+
"gate.guard.bypass.fm_pause.claim": "solo la persona pausa o reanuda la compuerta",
|
|
6908
|
+
"gate.guard.bypass.fm_pause.requires": "la compuerta sigue activa salvo que la persona la pause",
|
|
6728
6909
|
"gate.guard.bypass.human": "Bloqueé {detail}: apagaría la red de seguridad. Si de verdad lo quiere, escriba en una línea: finitemonkey: allow bypass <motivo>",
|
|
6729
|
-
"gate.guard.self.policy": "
|
|
6730
|
-
"gate.guard.self.
|
|
6910
|
+
"gate.guard.self.policy.claim": "{file} solo lo escribe el ejecutable finitemonkey (evidencia, política y estado)",
|
|
6911
|
+
"gate.guard.self.policy.requires": "cambiarlo con los comandos de finitemonkey",
|
|
6912
|
+
"gate.guard.self.not": "una copia, movimiento, redirección o script que escribe el mismo archivo",
|
|
6913
|
+
"gate.guard.self.allow_store.claim": "{file} registra lo que la persona liberó; ninguna herramienta lo escribe",
|
|
6914
|
+
"gate.guard.self.allow_store.requires": "dejar este archivo a la persona",
|
|
6731
6915
|
"gate.guard.self.human": "Dejé {file} fuera de alcance: el mono no corrige su propio examen.",
|
|
6732
|
-
"gate.guard.tests.
|
|
6916
|
+
"gate.guard.tests.claim": "{detail}",
|
|
6917
|
+
"gate.guard.tests.requires": "hacer que el código pase las pruebas como están",
|
|
6918
|
+
"gate.guard.tests.not": "una prueba que sigue corriendo pero ya no comprueba el comportamiento",
|
|
6733
6919
|
"gate.guard.tests.shell": "un comando de shell escribe o borra {file}",
|
|
6734
6920
|
"gate.guard.tests.human": "Bloqueado: {detail}. La prueba es el único testigo que tenemos. Corrija el código, no al testigo. Si el cambio es a propósito, escriba en una línea: finitemonkey: allow tests <motivo>",
|
|
6735
6921
|
"gate.release.allowed": "Liberé {guard} hasta {until}, a su pedido.",
|
|
@@ -6765,9 +6951,14 @@ var es_default = {
|
|
|
6765
6951
|
"prose.span": "líneas {start}-{end}: {rules}",
|
|
6766
6952
|
"prose.fix_item": "{rule}: {fix}",
|
|
6767
6953
|
"prose.warn.command": "{target} parece escrito por IA ({spans}). Reescríbelo antes de enviarlo. {fixes}. Si es texto de marca escrito a propósito, muéstrale dos alternativas a la persona.",
|
|
6768
|
-
"prose.block.command": "
|
|
6769
|
-
"prose.block.
|
|
6770
|
-
"prose.block.
|
|
6954
|
+
"prose.block.command.claim": "{target} parece escrito por IA ({spans}), y el preset de este proyecto no deja enviarlo. {fixes}",
|
|
6955
|
+
"prose.block.command.requires": "Reescríbelo y vuelve a ejecutar el comando",
|
|
6956
|
+
"prose.block.not": "el mismo texto con algunas palabras cambiadas",
|
|
6957
|
+
"prose.block.human": "Retuve un texto que parece escrito por una máquina.",
|
|
6958
|
+
"prose.block.unreadable.claim": "no se pudo leer {file} para revisar el texto ({detail})",
|
|
6959
|
+
"prose.block.unreadable.requires": "Revisa la ruta y vuelve a ejecutar el comando",
|
|
6960
|
+
"prose.block.marker.claim": "esto añadiría el marcador {marker} en {file}, y solo la persona puede ponerlo",
|
|
6961
|
+
"prose.block.marker.requires": "Reescribe el pasaje, o pide a la persona que ponga el marcador en su propio editor",
|
|
6771
6962
|
"prose.check.file": "{file}: {count, plural, one {# pasaje que parece escrito} other {# pasajes que parecen escritos}} por IA",
|
|
6772
6963
|
"prose.check.span": " {file}:{start}-{end} puntos {score}: {rules}",
|
|
6773
6964
|
"prose.check.fix": " haz en su lugar: {fix}",
|
|
@@ -6776,7 +6967,8 @@ var es_default = {
|
|
|
6776
6967
|
"prose.error.found": "{count, plural, one {# pasaje parece escrito} other {# pasajes parecen escritos}} por IA",
|
|
6777
6968
|
"prose.error.no_input": "nada que revisar: pasa archivos, --stdin, o ejecútalo dentro de un repositorio git para revisar los Markdown modificados",
|
|
6778
6969
|
"prose.error.unreadable": "no se pudo leer {file} ({detail})",
|
|
6779
|
-
"prose.block.marker_shell": "
|
|
6970
|
+
"prose.block.marker_shell.claim": "este comando escribe el marcador {marker}, y solo la persona puede ponerlo",
|
|
6971
|
+
"prose.block.marker_shell.requires": "Reescribe el pasaje, o pide a la persona que ponga el marcador en su propio editor",
|
|
6780
6972
|
"prose.rule.requester-reference": "di el motivo técnico del cambio, sin decir quién lo pidió",
|
|
6781
6973
|
"prose.rule.person-as-reason": "nombra la propiedad del cambio en lugar de la persona",
|
|
6782
6974
|
"prose.rule.origin-project": "describe la propiedad técnica, sin citar de dónde salió",
|
|
@@ -6791,30 +6983,55 @@ var es_default = {
|
|
|
6791
6983
|
"git.command.undo": "Lista las instantáneas o restaura una, por defecto en un worktree nuevo",
|
|
6792
6984
|
"git.command.rescue": "Lista commits perdidos por el reflog y el fsck, y restaura uno en una rama nueva",
|
|
6793
6985
|
"git.command.merge_verify": "Comprueba que una fusión conservó cada cambio de los dos lados",
|
|
6794
|
-
"git.block.force_push": "
|
|
6795
|
-
"git.block.
|
|
6796
|
-
"git.block.
|
|
6797
|
-
"git.block.
|
|
6798
|
-
"git.block.
|
|
6799
|
-
"git.block.
|
|
6800
|
-
"git.block.
|
|
6801
|
-
"git.block.
|
|
6802
|
-
"git.block.
|
|
6803
|
-
"git.block.
|
|
6804
|
-
"git.block.
|
|
6805
|
-
"git.block.
|
|
6806
|
-
"git.block.
|
|
6807
|
-
"git.block.
|
|
6808
|
-
"git.block.
|
|
6809
|
-
"git.block.
|
|
6810
|
-
"git.block.
|
|
6986
|
+
"git.block.force_push.claim": "`{command}` reescribe la rama remota y puede borrar commits que otra persona subió",
|
|
6987
|
+
"git.block.force_push.requires": "Trae e integra los commits remotos y sube normalmente. En tu propia rama después de un rebase, usa `git push --force-with-lease=<rama>:<sha esperado> --force-if-includes`",
|
|
6988
|
+
"git.block.not": "el mismo efecto con otro comando git, un alias, un script o un shell anidado",
|
|
6989
|
+
"git.block.human": "Detuve un comando git que podía perder trabajo o reescribir historia compartida.",
|
|
6990
|
+
"git.block.lease_not_own.claim": "`{command}` fuerza un push sobre {subject}, que no es la rama en la que trabajas o está protegida",
|
|
6991
|
+
"git.block.lease_not_own.requires": "El push forzado solo vale en tu propia rama, con lease explícito",
|
|
6992
|
+
"git.block.push_delete.claim": "`{command}` borra {subject} en el remoto",
|
|
6993
|
+
"git.block.push_delete.requires": "Borrar una rama remota le toca a la persona",
|
|
6994
|
+
"git.block.push_mirror.claim": "`{command}` sobrescribe todas las refs del remoto y publicaría las refs de respaldo",
|
|
6995
|
+
"git.block.push_mirror.requires": "Sube por nombre la rama que necesitas",
|
|
6996
|
+
"git.block.clean.claim": "`{command}` borra para siempre archivos sin seguimiento, que no están en ningún commit",
|
|
6997
|
+
"git.block.clean.requires": "Corre `git clean -n` para ver la lista y mueve lo que deba irse a .finitemonkey/trash/, o pregunta a la persona",
|
|
6998
|
+
"git.block.stash_drop.claim": "`{command}` tira trabajo guardado en el stash que no existe en otro lugar",
|
|
6999
|
+
"git.block.stash_drop.requires": "Aplícalo con `git stash apply` o déjalo; descartar un stash le toca a la persona",
|
|
7000
|
+
"git.block.branch_unmerged.claim": "`{command}` borra {subject}, cuyos commits no están en su upstream ni en la rama por defecto",
|
|
7001
|
+
"git.block.branch_unmerged.requires": "Súbela o fusiónala antes; `git branch -d` funciona cuando ya está fusionada",
|
|
7002
|
+
"git.block.worktree_force.claim": "`{command}` elimina un worktree aunque tenga cambios sin commit",
|
|
7003
|
+
"git.block.worktree_force.requires": "Haz commit o archiva su trabajo antes; `git worktree remove` sin forzar rechaza un árbol sucio a propósito",
|
|
7004
|
+
"git.block.rm_worktree.claim": "`{command}` borra {subject}, que es un worktree o repositorio git; el trabajo sin commit se perdería",
|
|
7005
|
+
"git.block.rm_worktree.requires": "Haz commit o archívalo y luego usa `git worktree remove` sin --force",
|
|
7006
|
+
"git.block.rm_root.claim": "`{command}` borra de forma recursiva {subject}, una carpeta raíz o la carpeta personal",
|
|
7007
|
+
"git.block.rm_root.requires": "Indica la carpeta exacta a eliminar",
|
|
7008
|
+
"git.block.rebase_protected.claim": "`{command}` reescribe {subject}, una rama protegida",
|
|
7009
|
+
"git.block.rebase_protected.requires": "Cámbiala con un pull request",
|
|
7010
|
+
"git.block.rebase_shared.claim": "`{command}` reescribe {subject}, que ya tiene commits de otra persona en el remoto",
|
|
7011
|
+
"git.block.rebase_shared.requires": "Fusiona la base en ella en lugar de hacer rebase",
|
|
7012
|
+
"git.block.merge_side.claim": "`{command}` resuelve conflictos tomando un lado entero ({subject}), lo que descarta en silencio los cambios del otro lado",
|
|
7013
|
+
"git.block.merge_side.requires": "Resuelve cada conflicto a mano y corre `finitemonkey merge verify` antes del commit",
|
|
7014
|
+
"git.block.history.claim": "`{command}` borra historial o entradas del reflog de las que dependen undo y rescue",
|
|
7015
|
+
"git.block.history.requires": "Reescribir el historial le toca a la persona",
|
|
7016
|
+
"git.block.protected_commit.claim": "`{command}` haría commit directo en {subject}, una rama protegida",
|
|
7017
|
+
"git.block.protected_commit.requires": "Crea una rama con `git switch -c <tipo>/<nombre>` y abre un pull request",
|
|
7018
|
+
"git.block.protected_push.claim": "`{command}` sube a {subject}, una rama protegida",
|
|
7019
|
+
"git.block.protected_push.requires": "Sube tu propia rama y abre un pull request",
|
|
7020
|
+
"git.block.foreign.claim": "`{command}` descartaría cambios que esta sesión no hizo: {files}",
|
|
7021
|
+
"git.block.foreign.requires": "Deshaz solo tus líneas con Edit",
|
|
6811
7022
|
"git.foreign.before": "{path} (tenía cambios antes de que empezaras)",
|
|
6812
7023
|
"git.foreign.other": "{path} (cambiado fuera de esta sesión)",
|
|
6813
|
-
"git.block.unknown": "
|
|
6814
|
-
"git.block.
|
|
7024
|
+
"git.block.unknown.claim": "`{command}` descarta cambios y `git status` falló, así que no hay forma de saber de quién son",
|
|
7025
|
+
"git.block.unknown.requires": "Arregla el repositorio e inténtalo de nuevo",
|
|
7026
|
+
"git.block.snapshot_failed.claim": "`{command}` es arriesgado y la instantánea de seguridad previa falló ({detail}), así que no se ejecutó",
|
|
7027
|
+
"git.block.snapshot_failed.requires": "Corrige la causa e inténtalo de nuevo",
|
|
6815
7028
|
"git.snapshot.context": "finitemonkey guardó una instantánea antes de `{command}`: {ref}. `finitemonkey undo {ref}` devuelve el estado anterior.",
|
|
6816
|
-
"prod.block.effect": "
|
|
6817
|
-
"prod.block.
|
|
7029
|
+
"prod.block.effect.claim": "`{command}` cambia producción ({target}) a mano, y un servidor editado a mano se aparta del repositorio",
|
|
7030
|
+
"prod.block.effect.requires": "Cámbialo por el camino de deploy ({deploy}): commit, pull request y que corra el deploy",
|
|
7031
|
+
"prod.block.not": "el mismo cambio con otra herramienta, un script o un shell anidado",
|
|
7032
|
+
"prod.block.human": "Detuve una edición a mano en producción; los cambios van por el despliegue.",
|
|
7033
|
+
"prod.block.interactive.claim": "`{command}` abre una sesión interactiva en producción ({target}), donde nada de lo que se escriba se revisa antes",
|
|
7034
|
+
"prod.block.interactive.requires": "Corre un comando de solo lectura a la vez, como `ssh {target} docker ps`",
|
|
6818
7035
|
"prod.deploy.unknown": "el deploy declarado del proyecto",
|
|
6819
7036
|
"git.snapshot.done": "instantánea guardada: {ref} ({commit}). `finitemonkey undo {ref}` la restaura.",
|
|
6820
7037
|
"git.undo.list_title": "{count, plural, one {# instantánea:} other {# instantáneas, de la más nueva a la más vieja:}}",
|
|
@@ -6941,7 +7158,14 @@ var es_default = {
|
|
|
6941
7158
|
"gate.look.allow_ignored": "excepción ignorada, su decisión no está registrada: {entry}",
|
|
6942
7159
|
"gate.look.unopened": "{count, plural, one {# captura sin abrir} other {# capturas sin abrir}} después de la última edición de un archivo de pantalla",
|
|
6943
7160
|
"gate.look.open": "abre con Read: {file}",
|
|
6944
|
-
"gate.look.ok": "{count, plural, one {# captura} other {# capturas}}, ningún fallo, todas abiertas"
|
|
7161
|
+
"gate.look.ok": "{count, plural, one {# captura} other {# capturas}}, ningún fallo, todas abiertas",
|
|
7162
|
+
"reason.label.blocked": "bloqueado",
|
|
7163
|
+
"reason.label.requires": "exige",
|
|
7164
|
+
"reason.label.not": "no cuenta",
|
|
7165
|
+
"reason.human.generic": "Bloqueé una llamada de herramienta; el motivo fue a Claude.",
|
|
7166
|
+
"review.command.packet": "Muestra el paquete de revisión para los agentes revisor y verificador: diff, contrato de la tarea, registro de evidencias y REVIEW.md, nunca la conversación",
|
|
7167
|
+
"review.guard.read_only": "bloqueado: el agente {agent} es de solo lectura y no puede usar {tool}. Informa el hallazgo; la sesión principal hace el cambio.",
|
|
7168
|
+
"review.error.no_base": "la base {detail} no es un commit aquí"
|
|
6945
7169
|
};
|
|
6946
7170
|
|
|
6947
7171
|
// src/i18n/pt-BR.json
|
|
@@ -6990,6 +7214,7 @@ var pt_BR_default = {
|
|
|
6990
7214
|
"i18n.problem.placeholder": "marcadores diferentes (esperado: {expected}; encontrado: {actual})",
|
|
6991
7215
|
"i18n.problem.plural": "faltam ramos de plural (precisa: {expected}; achados: {actual})",
|
|
6992
7216
|
"i18n.problem.status": "sem status de revisão (use: {expected})",
|
|
7217
|
+
"i18n.problem.release_hint": "instrução de liberação em texto que o modelo lê ({actual}); leve para uma chave que a pessoa lê",
|
|
6993
7218
|
"i18n.problem.empty": "texto vazio",
|
|
6994
7219
|
"i18n.problem.not_string": "valor não é texto",
|
|
6995
7220
|
"doctor.title": "finitemonkey doctor ({version})",
|
|
@@ -7116,12 +7341,20 @@ var pt_BR_default = {
|
|
|
7116
7341
|
"secrets.where.line": "linha {line}, coluna {column}",
|
|
7117
7342
|
"secrets.where.file": "{file}:{line}:{column}",
|
|
7118
7343
|
"secrets.where.new_text": "{file} (linha {line} do texto novo)",
|
|
7119
|
-
"secrets.block.write": "
|
|
7120
|
-
"secrets.block.
|
|
7121
|
-
"secrets.block.
|
|
7122
|
-
"secrets.block.
|
|
7123
|
-
"secrets.block.
|
|
7124
|
-
"secrets.block.
|
|
7344
|
+
"secrets.block.write.claim": "o texto novo de {file} tem uma credencial ({found})",
|
|
7345
|
+
"secrets.block.write.requires": "Nada foi gravado. Deixe o valor no .env (fora do git) e leia pelo nome da variável. Se a chave é real, trate como exposta e troque",
|
|
7346
|
+
"secrets.block.not": "uma cópia codificada, partida, comentada ou renomeada do mesmo valor",
|
|
7347
|
+
"secrets.block.human": "Mantive uma credencial fora de um arquivo, comando ou commit.",
|
|
7348
|
+
"secrets.block.bash.claim": "este comando tem uma credencial ({found}) e não rodou",
|
|
7349
|
+
"secrets.block.bash.requires": "Leia o valor de uma variável de ambiente em vez de escrevê-lo no comando. Se a chave é real, trate como exposta e troque",
|
|
7350
|
+
"secrets.block.commit.claim": "as mudanças preparadas têm uma credencial ({found}), então o commit não rodou",
|
|
7351
|
+
"secrets.block.commit.requires": "Tire-a dos arquivos preparados, deixe no .env (fora do git) e troque a chave se for real",
|
|
7352
|
+
"secrets.block.read.claim": "{target} guarda segredos, e lê-lo colocaria os valores na conversa",
|
|
7353
|
+
"secrets.block.read.requires": "Peça à pessoa os nomes das variáveis, ou leia o .env.example",
|
|
7354
|
+
"secrets.block.env_dump.claim": "`{command}` imprime todas as variáveis de ambiente, segredos inclusive",
|
|
7355
|
+
"secrets.block.env_dump.requires": 'Imprima só a variável que precisa, pelo nome, ou confira que ela existe com `test -n "$NAME"`',
|
|
7356
|
+
"secrets.block.person_only.claim": "`{command}` é decisão da pessoa, não do modelo",
|
|
7357
|
+
"secrets.block.person_only.requires": "Peça à pessoa que rode no terminal dela",
|
|
7125
7358
|
"secrets.block.prompt": "sua mensagem não foi enviada ao modelo: ela tem uma credencial ({found}). Se essa chave foi colada em outro lugar, trate como exposta e troque. Para usá-la, ponha no .env e me diga só o nome da variável. Nunca cole chaves na conversa. Incidente: {incident}.",
|
|
7126
7359
|
"secrets.post.context": "Finite Monkey: a saída de {tool} continha uma credencial ({found}). O incidente {incident} está aberto; trocar a chave é decisão da pessoa. A saída do Finite Monkey nunca repete o valor.",
|
|
7127
7360
|
"secrets.incident.unsaved": "não registrado ({detail})",
|
|
@@ -7314,16 +7547,27 @@ var pt_BR_default = {
|
|
|
7314
7547
|
"gate.config.disabled": "Finite Monkey: esta mudança liga disableAllHooks em {file}; ela não foi aplicada.",
|
|
7315
7548
|
"gate.config.human": "Mantive os hooks do Finite Monkey em {file}. Se foi de propósito, digite numa linha: finitemonkey: allow config <motivo>",
|
|
7316
7549
|
"gate.config.released": "Mudança de config em {file} aceita pela sua liberação.",
|
|
7317
|
-
"gate.guard.bypass.no_verify": "
|
|
7318
|
-
"gate.guard.bypass.
|
|
7319
|
-
"gate.guard.bypass.
|
|
7320
|
-
"gate.guard.bypass.
|
|
7321
|
-
"gate.guard.bypass.
|
|
7550
|
+
"gate.guard.bypass.no_verify.claim": "{detail} pula os git hooks",
|
|
7551
|
+
"gate.guard.bypass.no_verify.requires": "os hooks rodam em todo commit e push",
|
|
7552
|
+
"gate.guard.bypass.not": "a mesma chave por outra variável, flag, alias ou script",
|
|
7553
|
+
"gate.guard.bypass.hooks_path.claim": "{detail} desvia o git dos hooks do projeto",
|
|
7554
|
+
"gate.guard.bypass.hooks_path.requires": "core.hooksPath fica como está configurado",
|
|
7555
|
+
"gate.guard.bypass.hook_env.claim": "{detail} desliga hooks ou travas",
|
|
7556
|
+
"gate.guard.bypass.hook_env.requires": "hooks e travas ficam ligados",
|
|
7557
|
+
"gate.guard.bypass.fm_allow.claim": "liberação é feita pela pessoa, não por chamada de ferramenta",
|
|
7558
|
+
"gate.guard.bypass.fm_allow.requires": "a liberação vem da pessoa digitando no prompt",
|
|
7559
|
+
"gate.guard.bypass.fm_pause.claim": "só a pessoa pausa ou retoma o portão",
|
|
7560
|
+
"gate.guard.bypass.fm_pause.requires": "o portão fica ligado a menos que a pessoa o pause",
|
|
7322
7561
|
"gate.guard.bypass.human": "Barrei {detail}: desligaria a rede de proteção. Se quiser mesmo, digite numa linha: finitemonkey: allow bypass <motivo>",
|
|
7323
|
-
"gate.guard.self.policy": "
|
|
7324
|
-
"gate.guard.self.
|
|
7562
|
+
"gate.guard.self.policy.claim": "{file} só é escrito pelo executável finitemonkey (evidência, política e estado)",
|
|
7563
|
+
"gate.guard.self.policy.requires": "mudar pelos comandos do finitemonkey",
|
|
7564
|
+
"gate.guard.self.not": "uma cópia, move, redirecionamento ou script que grava o mesmo arquivo",
|
|
7565
|
+
"gate.guard.self.allow_store.claim": "{file} registra o que a pessoa liberou; ferramenta nunca escreve nele",
|
|
7566
|
+
"gate.guard.self.allow_store.requires": "deixar este arquivo para a pessoa",
|
|
7325
7567
|
"gate.guard.self.human": "Deixei {file} fora do alcance: o macaco não corrige a própria prova.",
|
|
7326
|
-
"gate.guard.tests.
|
|
7568
|
+
"gate.guard.tests.claim": "{detail}",
|
|
7569
|
+
"gate.guard.tests.requires": "fazer o código passar nos testes como estão",
|
|
7570
|
+
"gate.guard.tests.not": "um teste que ainda roda mas não confere mais o comportamento",
|
|
7327
7571
|
"gate.guard.tests.shell": "um comando de shell escreve ou apaga {file}",
|
|
7328
7572
|
"gate.guard.tests.human": "Barrado: {detail}. O teste é a única testemunha que temos. Corrija o código, não a testemunha. Se a mudança é de propósito, digite numa linha: finitemonkey: allow tests <motivo>",
|
|
7329
7573
|
"gate.release.allowed": "Liberei {guard} até {until}, a seu pedido.",
|
|
@@ -7359,9 +7603,14 @@ var pt_BR_default = {
|
|
|
7359
7603
|
"prose.span": "linhas {start}-{end}: {rules}",
|
|
7360
7604
|
"prose.fix_item": "{rule}: {fix}",
|
|
7361
7605
|
"prose.warn.command": "{target} tem cara de texto de IA ({spans}). Reescreva antes de enviar. {fixes}. Se for texto de marca escrito de propósito, mostre duas alternativas à pessoa.",
|
|
7362
|
-
"prose.block.command": "
|
|
7363
|
-
"prose.block.
|
|
7364
|
-
"prose.block.
|
|
7606
|
+
"prose.block.command.claim": "{target} tem cara de texto de IA ({spans}), e o preset deste projeto não deixa enviar. {fixes}",
|
|
7607
|
+
"prose.block.command.requires": "Reescreva e rode o comando de novo",
|
|
7608
|
+
"prose.block.not": "o mesmo texto com algumas palavras trocadas",
|
|
7609
|
+
"prose.block.human": "Segurei um texto com cara de escrito por máquina.",
|
|
7610
|
+
"prose.block.unreadable.claim": "não deu para ler {file} para conferir o texto ({detail})",
|
|
7611
|
+
"prose.block.unreadable.requires": "Confira o caminho e rode o comando de novo",
|
|
7612
|
+
"prose.block.marker.claim": "isto acrescentaria o marcador {marker} em {file}, e só a pessoa pode pôr esse marcador",
|
|
7613
|
+
"prose.block.marker.requires": "Reescreva o trecho, ou peça à pessoa que ponha o marcador no editor dela",
|
|
7365
7614
|
"prose.check.file": "{file}: {count, plural, one {# trecho} other {# trechos}} com cara de texto de IA",
|
|
7366
7615
|
"prose.check.span": " {file}:{start}-{end} pontos {score}: {rules}",
|
|
7367
7616
|
"prose.check.fix": " faça no lugar: {fix}",
|
|
@@ -7370,7 +7619,8 @@ var pt_BR_default = {
|
|
|
7370
7619
|
"prose.error.found": "{count, plural, one {# trecho} other {# trechos}} com cara de texto de IA",
|
|
7371
7620
|
"prose.error.no_input": "nada para conferir: passe arquivos, --stdin, ou rode dentro de um repositório git para conferir os Markdown alterados",
|
|
7372
7621
|
"prose.error.unreadable": "não deu para ler {file} ({detail})",
|
|
7373
|
-
"prose.block.marker_shell": "
|
|
7622
|
+
"prose.block.marker_shell.claim": "este comando escreve o marcador {marker}, e só a pessoa pode pôr esse marcador",
|
|
7623
|
+
"prose.block.marker_shell.requires": "Reescreva o trecho, ou peça à pessoa que ponha o marcador no editor dela",
|
|
7374
7624
|
"prose.rule.requester-reference": "diga o motivo técnico da mudança, sem dizer quem pediu",
|
|
7375
7625
|
"prose.rule.person-as-reason": "nomeie a propriedade da mudança no lugar da pessoa",
|
|
7376
7626
|
"prose.rule.origin-project": "descreva a propriedade técnica, sem citar de onde veio",
|
|
@@ -7385,30 +7635,55 @@ var pt_BR_default = {
|
|
|
7385
7635
|
"git.command.undo": "Lista as fotografias ou restaura uma, por padrão num worktree novo",
|
|
7386
7636
|
"git.command.rescue": "Lista commits perdidos pelo reflog e pelo fsck, e restaura um numa branch nova",
|
|
7387
7637
|
"git.command.merge_verify": "Confere que uma mescla manteve toda mudança dos dois lados",
|
|
7388
|
-
"git.block.force_push": "
|
|
7389
|
-
"git.block.
|
|
7390
|
-
"git.block.
|
|
7391
|
-
"git.block.
|
|
7392
|
-
"git.block.
|
|
7393
|
-
"git.block.
|
|
7394
|
-
"git.block.
|
|
7395
|
-
"git.block.
|
|
7396
|
-
"git.block.
|
|
7397
|
-
"git.block.
|
|
7398
|
-
"git.block.
|
|
7399
|
-
"git.block.
|
|
7400
|
-
"git.block.
|
|
7401
|
-
"git.block.
|
|
7402
|
-
"git.block.
|
|
7403
|
-
"git.block.
|
|
7404
|
-
"git.block.
|
|
7638
|
+
"git.block.force_push.claim": "`{command}` reescreve a branch remota e pode apagar commits que outra pessoa enviou",
|
|
7639
|
+
"git.block.force_push.requires": "Busque, integre os commits remotos e envie normalmente. Na sua própria branch depois de um rebase, use `git push --force-with-lease=<branch>:<sha esperado> --force-if-includes`",
|
|
7640
|
+
"git.block.not": "o mesmo efeito por outro comando git, um alias, um script ou um shell aninhado",
|
|
7641
|
+
"git.block.human": "Parei um comando git que podia perder trabalho ou reescrever histórico compartilhado.",
|
|
7642
|
+
"git.block.lease_not_own.claim": "`{command}` força um push em {subject}, que não é a branch em que você trabalha ou é protegida",
|
|
7643
|
+
"git.block.lease_not_own.requires": "Push forçado só vale na sua própria branch, com lease explícito",
|
|
7644
|
+
"git.block.push_delete.claim": "`{command}` apaga {subject} no remoto",
|
|
7645
|
+
"git.block.push_delete.requires": "Apagar branch remota é com a pessoa",
|
|
7646
|
+
"git.block.push_mirror.claim": "`{command}` sobrescreve todas as refs do remoto e publicaria as refs de backup",
|
|
7647
|
+
"git.block.push_mirror.requires": "Envie pelo nome a branch de que precisa",
|
|
7648
|
+
"git.block.clean.claim": "`{command}` apaga de vez arquivos não rastreados, que não estão em commit nenhum",
|
|
7649
|
+
"git.block.clean.requires": "Rode `git clean -n` para ver a lista e mova o que deve sair para .finitemonkey/trash/, ou pergunte à pessoa",
|
|
7650
|
+
"git.block.stash_drop.claim": "`{command}` joga fora trabalho guardado no stash que não existe em outro lugar",
|
|
7651
|
+
"git.block.stash_drop.requires": "Aplique com `git stash apply` ou deixe como está; descartar stash é com a pessoa",
|
|
7652
|
+
"git.block.branch_unmerged.claim": "`{command}` apaga {subject}, cujos commits não estão no upstream nem na branch padrão",
|
|
7653
|
+
"git.block.branch_unmerged.requires": "Envie ou mescle antes; `git branch -d` funciona depois de mesclada",
|
|
7654
|
+
"git.block.worktree_force.claim": "`{command}` remove um worktree mesmo com mudança sem commit",
|
|
7655
|
+
"git.block.worktree_force.requires": "Commite ou arquive o trabalho antes; o `git worktree remove` sem força recusa árvore suja de propósito",
|
|
7656
|
+
"git.block.rm_worktree.claim": "`{command}` apaga {subject}, que é um worktree ou repositório git; o trabalho sem commit iria junto",
|
|
7657
|
+
"git.block.rm_worktree.requires": "Commite ou arquive e depois use `git worktree remove` sem --force",
|
|
7658
|
+
"git.block.rm_root.claim": "`{command}` apaga recursivamente {subject}, uma pasta raiz ou a pasta pessoal",
|
|
7659
|
+
"git.block.rm_root.requires": "Diga a pasta exata a remover",
|
|
7660
|
+
"git.block.rebase_protected.claim": "`{command}` reescreve {subject}, uma branch protegida",
|
|
7661
|
+
"git.block.rebase_protected.requires": "Mude por pull request",
|
|
7662
|
+
"git.block.rebase_shared.claim": "`{command}` reescreve {subject}, que já tem commits de outra pessoa no remoto",
|
|
7663
|
+
"git.block.rebase_shared.requires": "Mescle a base nela em vez de fazer rebase",
|
|
7664
|
+
"git.block.merge_side.claim": "`{command}` resolve conflito escolhendo um lado inteiro ({subject}), o que descarta calado as mudanças do outro lado",
|
|
7665
|
+
"git.block.merge_side.requires": "Resolva cada conflito à mão e rode `finitemonkey merge verify` antes do commit",
|
|
7666
|
+
"git.block.history.claim": "`{command}` apaga histórico ou entradas do reflog de que o undo e o rescue dependem",
|
|
7667
|
+
"git.block.history.requires": "Reescrever histórico é com a pessoa",
|
|
7668
|
+
"git.block.protected_commit.claim": "`{command}` faria commit direto em {subject}, uma branch protegida",
|
|
7669
|
+
"git.block.protected_commit.requires": "Crie uma branch com `git switch -c <tipo>/<nome>` e abra um pull request",
|
|
7670
|
+
"git.block.protected_push.claim": "`{command}` envia para {subject}, uma branch protegida",
|
|
7671
|
+
"git.block.protected_push.requires": "Envie a sua branch e abra um pull request",
|
|
7672
|
+
"git.block.foreign.claim": "`{command}` descartaria mudanças que esta sessão não fez: {files}",
|
|
7673
|
+
"git.block.foreign.requires": "Desfaça só as suas linhas com Edit",
|
|
7405
7674
|
"git.foreign.before": "{path} (tinha mudança antes de você começar)",
|
|
7406
7675
|
"git.foreign.other": "{path} (mudado fora desta sessão)",
|
|
7407
|
-
"git.block.unknown": "
|
|
7408
|
-
"git.block.
|
|
7676
|
+
"git.block.unknown.claim": "`{command}` descarta mudanças, e o `git status` falhou, então não dá para saber de quem elas são",
|
|
7677
|
+
"git.block.unknown.requires": "Conserte o repositório e tente de novo",
|
|
7678
|
+
"git.block.snapshot_failed.claim": "`{command}` é arriscado e a fotografia de segurança antes dele falhou ({detail}), então ele não rodou",
|
|
7679
|
+
"git.block.snapshot_failed.requires": "Resolva a causa e tente de novo",
|
|
7409
7680
|
"git.snapshot.context": "o finitemonkey salvou uma fotografia antes de `{command}`: {ref}. `finitemonkey undo {ref}` traz o estado anterior de volta.",
|
|
7410
|
-
"prod.block.effect": "
|
|
7411
|
-
"prod.block.
|
|
7681
|
+
"prod.block.effect.claim": "`{command}` mexe em produção ({target}) na mão, e servidor editado na mão diverge do repositório",
|
|
7682
|
+
"prod.block.effect.requires": "Mude pelo caminho de deploy ({deploy}): commit, pull request e o deploy roda",
|
|
7683
|
+
"prod.block.not": "a mesma mudança por outra ferramenta, um script ou um shell aninhado",
|
|
7684
|
+
"prod.block.human": "Parei uma edição na mão em produção; mudança vai pelo deploy.",
|
|
7685
|
+
"prod.block.interactive.claim": "`{command}` abre sessão interativa em produção ({target}), onde nada do que for digitado passa por conferência",
|
|
7686
|
+
"prod.block.interactive.requires": "Rode um comando de leitura por vez, como `ssh {target} docker ps`",
|
|
7412
7687
|
"prod.deploy.unknown": "o deploy declarado do projeto",
|
|
7413
7688
|
"git.snapshot.done": "fotografia salva: {ref} ({commit}). `finitemonkey undo {ref}` restaura.",
|
|
7414
7689
|
"git.undo.list_title": "{count, plural, one {# fotografia:} other {# fotografias, da mais nova para a mais antiga:}}",
|
|
@@ -7535,7 +7810,14 @@ var pt_BR_default = {
|
|
|
7535
7810
|
"gate.look.allow_ignored": "exceção ignorada, a decisão não foi registrada: {entry}",
|
|
7536
7811
|
"gate.look.unopened": "{count, plural, one {# captura não aberta} other {# capturas não abertas}} depois da última edição de arquivo de tela",
|
|
7537
7812
|
"gate.look.open": "abra com Read: {file}",
|
|
7538
|
-
"gate.look.ok": "{count, plural, one {# captura} other {# capturas}}, nenhuma falha, todas abertas"
|
|
7813
|
+
"gate.look.ok": "{count, plural, one {# captura} other {# capturas}}, nenhuma falha, todas abertas",
|
|
7814
|
+
"reason.label.blocked": "bloqueado",
|
|
7815
|
+
"reason.label.requires": "exige",
|
|
7816
|
+
"reason.label.not": "não vale",
|
|
7817
|
+
"reason.human.generic": "Bloqueei uma chamada de ferramenta; o motivo foi para o Claude.",
|
|
7818
|
+
"review.command.packet": "Mostra o pacote de revisão para os agentes revisor e verificador: diff, contrato da tarefa, registro de evidências e REVIEW.md, nunca a conversa",
|
|
7819
|
+
"review.guard.read_only": "bloqueado: o agente {agent} é só leitura e não pode usar {tool}. Relate o achado; a sessão principal faz a mudança.",
|
|
7820
|
+
"review.error.no_base": "a base {detail} não é um commit aqui"
|
|
7539
7821
|
};
|
|
7540
7822
|
|
|
7541
7823
|
// src/i18n/review.json
|
|
@@ -7585,6 +7867,7 @@ var review_default = {
|
|
|
7585
7867
|
"i18n.problem.placeholder": "machine",
|
|
7586
7868
|
"i18n.problem.plural": "machine",
|
|
7587
7869
|
"i18n.problem.status": "machine",
|
|
7870
|
+
"i18n.problem.release_hint": "machine",
|
|
7588
7871
|
"i18n.problem.empty": "machine",
|
|
7589
7872
|
"i18n.problem.not_string": "machine",
|
|
7590
7873
|
"doctor.title": "machine",
|
|
@@ -7711,12 +7994,20 @@ var review_default = {
|
|
|
7711
7994
|
"secrets.where.line": "machine",
|
|
7712
7995
|
"secrets.where.file": "machine",
|
|
7713
7996
|
"secrets.where.new_text": "machine",
|
|
7714
|
-
"secrets.block.write": "machine",
|
|
7715
|
-
"secrets.block.
|
|
7716
|
-
"secrets.block.
|
|
7717
|
-
"secrets.block.
|
|
7718
|
-
"secrets.block.
|
|
7719
|
-
"secrets.block.
|
|
7997
|
+
"secrets.block.write.claim": "machine",
|
|
7998
|
+
"secrets.block.write.requires": "machine",
|
|
7999
|
+
"secrets.block.not": "machine",
|
|
8000
|
+
"secrets.block.human": "machine",
|
|
8001
|
+
"secrets.block.bash.claim": "machine",
|
|
8002
|
+
"secrets.block.bash.requires": "machine",
|
|
8003
|
+
"secrets.block.commit.claim": "machine",
|
|
8004
|
+
"secrets.block.commit.requires": "machine",
|
|
8005
|
+
"secrets.block.read.claim": "machine",
|
|
8006
|
+
"secrets.block.read.requires": "machine",
|
|
8007
|
+
"secrets.block.env_dump.claim": "machine",
|
|
8008
|
+
"secrets.block.env_dump.requires": "machine",
|
|
8009
|
+
"secrets.block.person_only.claim": "machine",
|
|
8010
|
+
"secrets.block.person_only.requires": "machine",
|
|
7720
8011
|
"secrets.block.prompt": "machine",
|
|
7721
8012
|
"secrets.post.context": "machine",
|
|
7722
8013
|
"secrets.incident.unsaved": "machine",
|
|
@@ -7909,16 +8200,27 @@ var review_default = {
|
|
|
7909
8200
|
"gate.config.disabled": "machine",
|
|
7910
8201
|
"gate.config.human": "machine",
|
|
7911
8202
|
"gate.config.released": "machine",
|
|
7912
|
-
"gate.guard.bypass.no_verify": "machine",
|
|
7913
|
-
"gate.guard.bypass.
|
|
7914
|
-
"gate.guard.bypass.
|
|
7915
|
-
"gate.guard.bypass.
|
|
7916
|
-
"gate.guard.bypass.
|
|
8203
|
+
"gate.guard.bypass.no_verify.claim": "machine",
|
|
8204
|
+
"gate.guard.bypass.no_verify.requires": "machine",
|
|
8205
|
+
"gate.guard.bypass.not": "machine",
|
|
8206
|
+
"gate.guard.bypass.hooks_path.claim": "machine",
|
|
8207
|
+
"gate.guard.bypass.hooks_path.requires": "machine",
|
|
8208
|
+
"gate.guard.bypass.hook_env.claim": "machine",
|
|
8209
|
+
"gate.guard.bypass.hook_env.requires": "machine",
|
|
8210
|
+
"gate.guard.bypass.fm_allow.claim": "machine",
|
|
8211
|
+
"gate.guard.bypass.fm_allow.requires": "machine",
|
|
8212
|
+
"gate.guard.bypass.fm_pause.claim": "machine",
|
|
8213
|
+
"gate.guard.bypass.fm_pause.requires": "machine",
|
|
7917
8214
|
"gate.guard.bypass.human": "machine",
|
|
7918
|
-
"gate.guard.self.policy": "machine",
|
|
7919
|
-
"gate.guard.self.
|
|
8215
|
+
"gate.guard.self.policy.claim": "machine",
|
|
8216
|
+
"gate.guard.self.policy.requires": "machine",
|
|
8217
|
+
"gate.guard.self.not": "machine",
|
|
8218
|
+
"gate.guard.self.allow_store.claim": "machine",
|
|
8219
|
+
"gate.guard.self.allow_store.requires": "machine",
|
|
7920
8220
|
"gate.guard.self.human": "machine",
|
|
7921
|
-
"gate.guard.tests.
|
|
8221
|
+
"gate.guard.tests.claim": "machine",
|
|
8222
|
+
"gate.guard.tests.requires": "machine",
|
|
8223
|
+
"gate.guard.tests.not": "machine",
|
|
7922
8224
|
"gate.guard.tests.shell": "machine",
|
|
7923
8225
|
"gate.guard.tests.human": "machine",
|
|
7924
8226
|
"gate.release.allowed": "machine",
|
|
@@ -7954,9 +8256,14 @@ var review_default = {
|
|
|
7954
8256
|
"prose.span": "machine",
|
|
7955
8257
|
"prose.fix_item": "machine",
|
|
7956
8258
|
"prose.warn.command": "machine",
|
|
7957
|
-
"prose.block.command": "machine",
|
|
7958
|
-
"prose.block.
|
|
7959
|
-
"prose.block.
|
|
8259
|
+
"prose.block.command.claim": "machine",
|
|
8260
|
+
"prose.block.command.requires": "machine",
|
|
8261
|
+
"prose.block.not": "machine",
|
|
8262
|
+
"prose.block.human": "machine",
|
|
8263
|
+
"prose.block.unreadable.claim": "machine",
|
|
8264
|
+
"prose.block.unreadable.requires": "machine",
|
|
8265
|
+
"prose.block.marker.claim": "machine",
|
|
8266
|
+
"prose.block.marker.requires": "machine",
|
|
7960
8267
|
"prose.check.file": "machine",
|
|
7961
8268
|
"prose.check.span": "machine",
|
|
7962
8269
|
"prose.check.fix": "machine",
|
|
@@ -7965,7 +8272,8 @@ var review_default = {
|
|
|
7965
8272
|
"prose.error.found": "machine",
|
|
7966
8273
|
"prose.error.no_input": "machine",
|
|
7967
8274
|
"prose.error.unreadable": "machine",
|
|
7968
|
-
"prose.block.marker_shell": "machine",
|
|
8275
|
+
"prose.block.marker_shell.claim": "machine",
|
|
8276
|
+
"prose.block.marker_shell.requires": "machine",
|
|
7969
8277
|
"prose.rule.requester-reference": "machine",
|
|
7970
8278
|
"prose.rule.person-as-reason": "machine",
|
|
7971
8279
|
"prose.rule.origin-project": "machine",
|
|
@@ -7980,30 +8288,55 @@ var review_default = {
|
|
|
7980
8288
|
"git.command.undo": "machine",
|
|
7981
8289
|
"git.command.rescue": "machine",
|
|
7982
8290
|
"git.command.merge_verify": "machine",
|
|
7983
|
-
"git.block.force_push": "machine",
|
|
7984
|
-
"git.block.
|
|
7985
|
-
"git.block.
|
|
7986
|
-
"git.block.
|
|
7987
|
-
"git.block.
|
|
7988
|
-
"git.block.
|
|
7989
|
-
"git.block.
|
|
7990
|
-
"git.block.
|
|
7991
|
-
"git.block.
|
|
7992
|
-
"git.block.
|
|
7993
|
-
"git.block.
|
|
7994
|
-
"git.block.
|
|
7995
|
-
"git.block.
|
|
7996
|
-
"git.block.
|
|
7997
|
-
"git.block.
|
|
7998
|
-
"git.block.
|
|
7999
|
-
"git.block.
|
|
8291
|
+
"git.block.force_push.claim": "machine",
|
|
8292
|
+
"git.block.force_push.requires": "machine",
|
|
8293
|
+
"git.block.not": "machine",
|
|
8294
|
+
"git.block.human": "machine",
|
|
8295
|
+
"git.block.lease_not_own.claim": "machine",
|
|
8296
|
+
"git.block.lease_not_own.requires": "machine",
|
|
8297
|
+
"git.block.push_delete.claim": "machine",
|
|
8298
|
+
"git.block.push_delete.requires": "machine",
|
|
8299
|
+
"git.block.push_mirror.claim": "machine",
|
|
8300
|
+
"git.block.push_mirror.requires": "machine",
|
|
8301
|
+
"git.block.clean.claim": "machine",
|
|
8302
|
+
"git.block.clean.requires": "machine",
|
|
8303
|
+
"git.block.stash_drop.claim": "machine",
|
|
8304
|
+
"git.block.stash_drop.requires": "machine",
|
|
8305
|
+
"git.block.branch_unmerged.claim": "machine",
|
|
8306
|
+
"git.block.branch_unmerged.requires": "machine",
|
|
8307
|
+
"git.block.worktree_force.claim": "machine",
|
|
8308
|
+
"git.block.worktree_force.requires": "machine",
|
|
8309
|
+
"git.block.rm_worktree.claim": "machine",
|
|
8310
|
+
"git.block.rm_worktree.requires": "machine",
|
|
8311
|
+
"git.block.rm_root.claim": "machine",
|
|
8312
|
+
"git.block.rm_root.requires": "machine",
|
|
8313
|
+
"git.block.rebase_protected.claim": "machine",
|
|
8314
|
+
"git.block.rebase_protected.requires": "machine",
|
|
8315
|
+
"git.block.rebase_shared.claim": "machine",
|
|
8316
|
+
"git.block.rebase_shared.requires": "machine",
|
|
8317
|
+
"git.block.merge_side.claim": "machine",
|
|
8318
|
+
"git.block.merge_side.requires": "machine",
|
|
8319
|
+
"git.block.history.claim": "machine",
|
|
8320
|
+
"git.block.history.requires": "machine",
|
|
8321
|
+
"git.block.protected_commit.claim": "machine",
|
|
8322
|
+
"git.block.protected_commit.requires": "machine",
|
|
8323
|
+
"git.block.protected_push.claim": "machine",
|
|
8324
|
+
"git.block.protected_push.requires": "machine",
|
|
8325
|
+
"git.block.foreign.claim": "machine",
|
|
8326
|
+
"git.block.foreign.requires": "machine",
|
|
8000
8327
|
"git.foreign.before": "machine",
|
|
8001
8328
|
"git.foreign.other": "machine",
|
|
8002
|
-
"git.block.unknown": "machine",
|
|
8003
|
-
"git.block.
|
|
8329
|
+
"git.block.unknown.claim": "machine",
|
|
8330
|
+
"git.block.unknown.requires": "machine",
|
|
8331
|
+
"git.block.snapshot_failed.claim": "machine",
|
|
8332
|
+
"git.block.snapshot_failed.requires": "machine",
|
|
8004
8333
|
"git.snapshot.context": "machine",
|
|
8005
|
-
"prod.block.effect": "machine",
|
|
8006
|
-
"prod.block.
|
|
8334
|
+
"prod.block.effect.claim": "machine",
|
|
8335
|
+
"prod.block.effect.requires": "machine",
|
|
8336
|
+
"prod.block.not": "machine",
|
|
8337
|
+
"prod.block.human": "machine",
|
|
8338
|
+
"prod.block.interactive.claim": "machine",
|
|
8339
|
+
"prod.block.interactive.requires": "machine",
|
|
8007
8340
|
"prod.deploy.unknown": "machine",
|
|
8008
8341
|
"git.snapshot.done": "machine",
|
|
8009
8342
|
"git.undo.list_title": "machine",
|
|
@@ -8130,7 +8463,14 @@ var review_default = {
|
|
|
8130
8463
|
"gate.look.allow_ignored": "machine",
|
|
8131
8464
|
"gate.look.unopened": "machine",
|
|
8132
8465
|
"gate.look.open": "machine",
|
|
8133
|
-
"gate.look.ok": "machine"
|
|
8466
|
+
"gate.look.ok": "machine",
|
|
8467
|
+
"reason.label.blocked": "machine",
|
|
8468
|
+
"reason.label.requires": "machine",
|
|
8469
|
+
"reason.label.not": "machine",
|
|
8470
|
+
"reason.human.generic": "machine",
|
|
8471
|
+
"review.command.packet": "machine",
|
|
8472
|
+
"review.guard.read_only": "machine",
|
|
8473
|
+
"review.error.no_base": "machine"
|
|
8134
8474
|
},
|
|
8135
8475
|
es: {
|
|
8136
8476
|
"command.init.summary": "machine",
|
|
@@ -8177,6 +8517,7 @@ var review_default = {
|
|
|
8177
8517
|
"i18n.problem.placeholder": "machine",
|
|
8178
8518
|
"i18n.problem.plural": "machine",
|
|
8179
8519
|
"i18n.problem.status": "machine",
|
|
8520
|
+
"i18n.problem.release_hint": "machine",
|
|
8180
8521
|
"i18n.problem.empty": "machine",
|
|
8181
8522
|
"i18n.problem.not_string": "machine",
|
|
8182
8523
|
"doctor.title": "machine",
|
|
@@ -8303,12 +8644,20 @@ var review_default = {
|
|
|
8303
8644
|
"secrets.where.line": "machine",
|
|
8304
8645
|
"secrets.where.file": "machine",
|
|
8305
8646
|
"secrets.where.new_text": "machine",
|
|
8306
|
-
"secrets.block.write": "machine",
|
|
8307
|
-
"secrets.block.
|
|
8308
|
-
"secrets.block.
|
|
8309
|
-
"secrets.block.
|
|
8310
|
-
"secrets.block.
|
|
8311
|
-
"secrets.block.
|
|
8647
|
+
"secrets.block.write.claim": "machine",
|
|
8648
|
+
"secrets.block.write.requires": "machine",
|
|
8649
|
+
"secrets.block.not": "machine",
|
|
8650
|
+
"secrets.block.human": "machine",
|
|
8651
|
+
"secrets.block.bash.claim": "machine",
|
|
8652
|
+
"secrets.block.bash.requires": "machine",
|
|
8653
|
+
"secrets.block.commit.claim": "machine",
|
|
8654
|
+
"secrets.block.commit.requires": "machine",
|
|
8655
|
+
"secrets.block.read.claim": "machine",
|
|
8656
|
+
"secrets.block.read.requires": "machine",
|
|
8657
|
+
"secrets.block.env_dump.claim": "machine",
|
|
8658
|
+
"secrets.block.env_dump.requires": "machine",
|
|
8659
|
+
"secrets.block.person_only.claim": "machine",
|
|
8660
|
+
"secrets.block.person_only.requires": "machine",
|
|
8312
8661
|
"secrets.block.prompt": "machine",
|
|
8313
8662
|
"secrets.post.context": "machine",
|
|
8314
8663
|
"secrets.incident.unsaved": "machine",
|
|
@@ -8501,16 +8850,27 @@ var review_default = {
|
|
|
8501
8850
|
"gate.config.disabled": "machine",
|
|
8502
8851
|
"gate.config.human": "machine",
|
|
8503
8852
|
"gate.config.released": "machine",
|
|
8504
|
-
"gate.guard.bypass.no_verify": "machine",
|
|
8505
|
-
"gate.guard.bypass.
|
|
8506
|
-
"gate.guard.bypass.
|
|
8507
|
-
"gate.guard.bypass.
|
|
8508
|
-
"gate.guard.bypass.
|
|
8853
|
+
"gate.guard.bypass.no_verify.claim": "machine",
|
|
8854
|
+
"gate.guard.bypass.no_verify.requires": "machine",
|
|
8855
|
+
"gate.guard.bypass.not": "machine",
|
|
8856
|
+
"gate.guard.bypass.hooks_path.claim": "machine",
|
|
8857
|
+
"gate.guard.bypass.hooks_path.requires": "machine",
|
|
8858
|
+
"gate.guard.bypass.hook_env.claim": "machine",
|
|
8859
|
+
"gate.guard.bypass.hook_env.requires": "machine",
|
|
8860
|
+
"gate.guard.bypass.fm_allow.claim": "machine",
|
|
8861
|
+
"gate.guard.bypass.fm_allow.requires": "machine",
|
|
8862
|
+
"gate.guard.bypass.fm_pause.claim": "machine",
|
|
8863
|
+
"gate.guard.bypass.fm_pause.requires": "machine",
|
|
8509
8864
|
"gate.guard.bypass.human": "machine",
|
|
8510
|
-
"gate.guard.self.policy": "machine",
|
|
8511
|
-
"gate.guard.self.
|
|
8865
|
+
"gate.guard.self.policy.claim": "machine",
|
|
8866
|
+
"gate.guard.self.policy.requires": "machine",
|
|
8867
|
+
"gate.guard.self.not": "machine",
|
|
8868
|
+
"gate.guard.self.allow_store.claim": "machine",
|
|
8869
|
+
"gate.guard.self.allow_store.requires": "machine",
|
|
8512
8870
|
"gate.guard.self.human": "machine",
|
|
8513
|
-
"gate.guard.tests.
|
|
8871
|
+
"gate.guard.tests.claim": "machine",
|
|
8872
|
+
"gate.guard.tests.requires": "machine",
|
|
8873
|
+
"gate.guard.tests.not": "machine",
|
|
8514
8874
|
"gate.guard.tests.shell": "machine",
|
|
8515
8875
|
"gate.guard.tests.human": "machine",
|
|
8516
8876
|
"gate.release.allowed": "machine",
|
|
@@ -8546,9 +8906,14 @@ var review_default = {
|
|
|
8546
8906
|
"prose.span": "machine",
|
|
8547
8907
|
"prose.fix_item": "machine",
|
|
8548
8908
|
"prose.warn.command": "machine",
|
|
8549
|
-
"prose.block.command": "machine",
|
|
8550
|
-
"prose.block.
|
|
8551
|
-
"prose.block.
|
|
8909
|
+
"prose.block.command.claim": "machine",
|
|
8910
|
+
"prose.block.command.requires": "machine",
|
|
8911
|
+
"prose.block.not": "machine",
|
|
8912
|
+
"prose.block.human": "machine",
|
|
8913
|
+
"prose.block.unreadable.claim": "machine",
|
|
8914
|
+
"prose.block.unreadable.requires": "machine",
|
|
8915
|
+
"prose.block.marker.claim": "machine",
|
|
8916
|
+
"prose.block.marker.requires": "machine",
|
|
8552
8917
|
"prose.check.file": "machine",
|
|
8553
8918
|
"prose.check.span": "machine",
|
|
8554
8919
|
"prose.check.fix": "machine",
|
|
@@ -8557,7 +8922,8 @@ var review_default = {
|
|
|
8557
8922
|
"prose.error.found": "machine",
|
|
8558
8923
|
"prose.error.no_input": "machine",
|
|
8559
8924
|
"prose.error.unreadable": "machine",
|
|
8560
|
-
"prose.block.marker_shell": "machine",
|
|
8925
|
+
"prose.block.marker_shell.claim": "machine",
|
|
8926
|
+
"prose.block.marker_shell.requires": "machine",
|
|
8561
8927
|
"prose.rule.requester-reference": "machine",
|
|
8562
8928
|
"prose.rule.person-as-reason": "machine",
|
|
8563
8929
|
"prose.rule.origin-project": "machine",
|
|
@@ -8572,30 +8938,55 @@ var review_default = {
|
|
|
8572
8938
|
"git.command.undo": "machine",
|
|
8573
8939
|
"git.command.rescue": "machine",
|
|
8574
8940
|
"git.command.merge_verify": "machine",
|
|
8575
|
-
"git.block.force_push": "machine",
|
|
8576
|
-
"git.block.
|
|
8577
|
-
"git.block.
|
|
8578
|
-
"git.block.
|
|
8579
|
-
"git.block.
|
|
8580
|
-
"git.block.
|
|
8581
|
-
"git.block.
|
|
8582
|
-
"git.block.
|
|
8583
|
-
"git.block.
|
|
8584
|
-
"git.block.
|
|
8585
|
-
"git.block.
|
|
8586
|
-
"git.block.
|
|
8587
|
-
"git.block.
|
|
8588
|
-
"git.block.
|
|
8589
|
-
"git.block.
|
|
8590
|
-
"git.block.
|
|
8591
|
-
"git.block.
|
|
8941
|
+
"git.block.force_push.claim": "machine",
|
|
8942
|
+
"git.block.force_push.requires": "machine",
|
|
8943
|
+
"git.block.not": "machine",
|
|
8944
|
+
"git.block.human": "machine",
|
|
8945
|
+
"git.block.lease_not_own.claim": "machine",
|
|
8946
|
+
"git.block.lease_not_own.requires": "machine",
|
|
8947
|
+
"git.block.push_delete.claim": "machine",
|
|
8948
|
+
"git.block.push_delete.requires": "machine",
|
|
8949
|
+
"git.block.push_mirror.claim": "machine",
|
|
8950
|
+
"git.block.push_mirror.requires": "machine",
|
|
8951
|
+
"git.block.clean.claim": "machine",
|
|
8952
|
+
"git.block.clean.requires": "machine",
|
|
8953
|
+
"git.block.stash_drop.claim": "machine",
|
|
8954
|
+
"git.block.stash_drop.requires": "machine",
|
|
8955
|
+
"git.block.branch_unmerged.claim": "machine",
|
|
8956
|
+
"git.block.branch_unmerged.requires": "machine",
|
|
8957
|
+
"git.block.worktree_force.claim": "machine",
|
|
8958
|
+
"git.block.worktree_force.requires": "machine",
|
|
8959
|
+
"git.block.rm_worktree.claim": "machine",
|
|
8960
|
+
"git.block.rm_worktree.requires": "machine",
|
|
8961
|
+
"git.block.rm_root.claim": "machine",
|
|
8962
|
+
"git.block.rm_root.requires": "machine",
|
|
8963
|
+
"git.block.rebase_protected.claim": "machine",
|
|
8964
|
+
"git.block.rebase_protected.requires": "machine",
|
|
8965
|
+
"git.block.rebase_shared.claim": "machine",
|
|
8966
|
+
"git.block.rebase_shared.requires": "machine",
|
|
8967
|
+
"git.block.merge_side.claim": "machine",
|
|
8968
|
+
"git.block.merge_side.requires": "machine",
|
|
8969
|
+
"git.block.history.claim": "machine",
|
|
8970
|
+
"git.block.history.requires": "machine",
|
|
8971
|
+
"git.block.protected_commit.claim": "machine",
|
|
8972
|
+
"git.block.protected_commit.requires": "machine",
|
|
8973
|
+
"git.block.protected_push.claim": "machine",
|
|
8974
|
+
"git.block.protected_push.requires": "machine",
|
|
8975
|
+
"git.block.foreign.claim": "machine",
|
|
8976
|
+
"git.block.foreign.requires": "machine",
|
|
8592
8977
|
"git.foreign.before": "machine",
|
|
8593
8978
|
"git.foreign.other": "machine",
|
|
8594
|
-
"git.block.unknown": "machine",
|
|
8595
|
-
"git.block.
|
|
8979
|
+
"git.block.unknown.claim": "machine",
|
|
8980
|
+
"git.block.unknown.requires": "machine",
|
|
8981
|
+
"git.block.snapshot_failed.claim": "machine",
|
|
8982
|
+
"git.block.snapshot_failed.requires": "machine",
|
|
8596
8983
|
"git.snapshot.context": "machine",
|
|
8597
|
-
"prod.block.effect": "machine",
|
|
8598
|
-
"prod.block.
|
|
8984
|
+
"prod.block.effect.claim": "machine",
|
|
8985
|
+
"prod.block.effect.requires": "machine",
|
|
8986
|
+
"prod.block.not": "machine",
|
|
8987
|
+
"prod.block.human": "machine",
|
|
8988
|
+
"prod.block.interactive.claim": "machine",
|
|
8989
|
+
"prod.block.interactive.requires": "machine",
|
|
8599
8990
|
"prod.deploy.unknown": "machine",
|
|
8600
8991
|
"git.snapshot.done": "machine",
|
|
8601
8992
|
"git.undo.list_title": "machine",
|
|
@@ -8722,7 +9113,14 @@ var review_default = {
|
|
|
8722
9113
|
"gate.look.allow_ignored": "machine",
|
|
8723
9114
|
"gate.look.unopened": "machine",
|
|
8724
9115
|
"gate.look.open": "machine",
|
|
8725
|
-
"gate.look.ok": "machine"
|
|
9116
|
+
"gate.look.ok": "machine",
|
|
9117
|
+
"reason.label.blocked": "machine",
|
|
9118
|
+
"reason.label.requires": "machine",
|
|
9119
|
+
"reason.label.not": "machine",
|
|
9120
|
+
"reason.human.generic": "machine",
|
|
9121
|
+
"review.command.packet": "machine",
|
|
9122
|
+
"review.guard.read_only": "machine",
|
|
9123
|
+
"review.error.no_base": "machine"
|
|
8726
9124
|
}
|
|
8727
9125
|
};
|
|
8728
9126
|
|
|
@@ -8953,9 +9351,9 @@ var notRunLine = (t, runtime, paths) => {
|
|
|
8953
9351
|
var markSession = (input, paths) => {
|
|
8954
9352
|
const { runtime } = input;
|
|
8955
9353
|
const sessionId = input.payload.sessionId;
|
|
8956
|
-
const
|
|
8957
|
-
if (sessionId === void 0 || headSha(
|
|
8958
|
-
const tree = treeHash(
|
|
9354
|
+
const git2 = gitAt(runtime, paths.root);
|
|
9355
|
+
if (sessionId === void 0 || headSha(git2) === void 0) return;
|
|
9356
|
+
const tree = treeHash(git2);
|
|
8959
9357
|
if (tree === void 0) return;
|
|
8960
9358
|
writeSession(runtime, paths, { sessionId, startTree: tree, at: nowMs(runtime) });
|
|
8961
9359
|
startBaseline(runtime, paths, tree);
|
|
@@ -9161,22 +9559,22 @@ var current = (runtime, root, path) => {
|
|
|
9161
9559
|
return text7.ok ? text7.value : "";
|
|
9162
9560
|
};
|
|
9163
9561
|
var keyOf = (language, name) => `${language.id}\0${name}`;
|
|
9164
|
-
var redefinedNames = (runtime,
|
|
9562
|
+
var redefinedNames = (runtime, git2, changes) => {
|
|
9165
9563
|
const keys = /* @__PURE__ */ new Set();
|
|
9166
9564
|
for (const change of changes) {
|
|
9167
9565
|
const language = languageOf(change.path);
|
|
9168
9566
|
if (language === void 0 || change.status === "D") continue;
|
|
9169
|
-
for (const name of definitionsIn(language, current(runtime,
|
|
9567
|
+
for (const name of definitionsIn(language, current(runtime, git2.cwd, change.path))) keys.add(keyOf(language, name));
|
|
9170
9568
|
}
|
|
9171
9569
|
return keys;
|
|
9172
9570
|
};
|
|
9173
|
-
var removedSymbols = (runtime,
|
|
9174
|
-
const redefined = redefinedNames(runtime,
|
|
9571
|
+
var removedSymbols = (runtime, git2, base, changes) => {
|
|
9572
|
+
const redefined = redefinedNames(runtime, git2, changes);
|
|
9175
9573
|
const found = /* @__PURE__ */ new Map();
|
|
9176
9574
|
for (const change of changes) {
|
|
9177
9575
|
const language = languageOf(change.oldPath ?? change.path);
|
|
9178
9576
|
if (language === void 0 || change.status === "A") continue;
|
|
9179
|
-
const before = definitionsIn(language, fileAt(
|
|
9577
|
+
const before = definitionsIn(language, fileAt(git2, base, change.oldPath ?? change.path) ?? "");
|
|
9180
9578
|
const origins = [change.path, ...change.oldPath === void 0 ? [] : [change.oldPath]];
|
|
9181
9579
|
for (const name of before) {
|
|
9182
9580
|
const key = keyOf(language, name);
|
|
@@ -9192,19 +9590,19 @@ var parseGrep = (output) => output.split("\n").filter((row) => row !== "").flatM
|
|
|
9192
9590
|
const number2 = Number(line2);
|
|
9193
9591
|
return file === "" || !Number.isInteger(number2) ? [] : [{ file: toPosixPath(file), line: number2, text: rest.join("\0") }];
|
|
9194
9592
|
});
|
|
9195
|
-
var grep = (
|
|
9593
|
+
var grep = (git2, names) => {
|
|
9196
9594
|
if (names.length === 0) return [];
|
|
9197
9595
|
const patterns = names.flatMap((name) => ["-e", name]);
|
|
9198
9596
|
return parseGrep(
|
|
9199
|
-
|
|
9597
|
+
git2.out(["grep", "-n", "-z", "-w", "-I", "--untracked", "--no-color", ...patterns, "--", ".", EXCLUDE_FM]) ?? ""
|
|
9200
9598
|
);
|
|
9201
9599
|
};
|
|
9202
9600
|
var mentions = (text7, name) => new RegExp(`(?<![\\w$])${name.replace(/[$?!]/g, "\\$&")}(?![\\w$])`).test(text7);
|
|
9203
|
-
var callSites = (runtime,
|
|
9204
|
-
const hits = grep(
|
|
9601
|
+
var callSites = (runtime, git2, symbols) => {
|
|
9602
|
+
const hits = grep(git2, [...new Set(symbols.map((symbol) => symbol.name))]);
|
|
9205
9603
|
const texts2 = /* @__PURE__ */ new Map();
|
|
9206
9604
|
const textOf4 = (file) => {
|
|
9207
|
-
const cached2 = texts2.get(file) ?? current(runtime,
|
|
9605
|
+
const cached2 = texts2.get(file) ?? current(runtime, git2.cwd, file);
|
|
9208
9606
|
texts2.set(file, cached2);
|
|
9209
9607
|
return cached2;
|
|
9210
9608
|
};
|
|
@@ -9956,9 +10354,9 @@ var splitBlock = (block4) => {
|
|
|
9956
10354
|
});
|
|
9957
10355
|
};
|
|
9958
10356
|
var unitsOf = (blocks) => {
|
|
9959
|
-
let
|
|
10357
|
+
let section2 = 0;
|
|
9960
10358
|
return blocks.flatMap((block4, paragraph) => {
|
|
9961
|
-
if (block4.kind === "heading")
|
|
10359
|
+
if (block4.kind === "heading") section2 += 1;
|
|
9962
10360
|
const pieces = splitBlock(block4);
|
|
9963
10361
|
const next2 = blocks[paragraph + 1];
|
|
9964
10362
|
const closesSection = block4.kind === "sentence" && (next2 === void 0 || next2.kind === "heading");
|
|
@@ -9966,7 +10364,7 @@ var unitsOf = (blocks) => {
|
|
|
9966
10364
|
text: piece.text,
|
|
9967
10365
|
line: piece.line,
|
|
9968
10366
|
paragraph,
|
|
9969
|
-
section,
|
|
10367
|
+
section: section2,
|
|
9970
10368
|
kind: block4.kind,
|
|
9971
10369
|
lastInParagraph: index === pieces.length - 1,
|
|
9972
10370
|
closesSection
|
|
@@ -10194,9 +10592,9 @@ var phraseHits = (units, catalog) => units.flatMap(
|
|
|
10194
10592
|
})
|
|
10195
10593
|
);
|
|
10196
10594
|
var windowEnd = (units, from, size) => {
|
|
10197
|
-
const
|
|
10595
|
+
const section2 = units[from]?.section;
|
|
10198
10596
|
let to = from;
|
|
10199
|
-
while (to + 1 < Math.min(units.length, from + size) && units[to + 1]?.section ===
|
|
10597
|
+
while (to + 1 < Math.min(units.length, from + size) && units[to + 1]?.section === section2) to += 1;
|
|
10200
10598
|
return to;
|
|
10201
10599
|
};
|
|
10202
10600
|
var windowScore = (hits, window) => hits.filter((item2) => item2.unit >= window.from && item2.unit <= window.to).reduce((sum, item2) => sum + item2.weight, 0);
|
|
@@ -10708,10 +11106,10 @@ var SKIPPED = /(?:^|\/)(?:\.git|node_modules|\.finitemonkey)\//;
|
|
|
10708
11106
|
var DIFF_FLAGS = ["-U0", "--no-color", "--no-ext-diff", "--no-renames"];
|
|
10709
11107
|
var binary = (text7) => text7.slice(0, 8e3).includes("\0");
|
|
10710
11108
|
var listed2 = (runtime, root, target2) => {
|
|
10711
|
-
const
|
|
10712
|
-
if (
|
|
11109
|
+
const git2 = runtime.git(["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", target2], root);
|
|
11110
|
+
if (git2.ok)
|
|
10713
11111
|
return accept(
|
|
10714
|
-
|
|
11112
|
+
git2.stdout.split("\0").filter((name) => name !== "").map(toPosixPath)
|
|
10715
11113
|
);
|
|
10716
11114
|
const base = toAbsolutePath(target2, root);
|
|
10717
11115
|
const walked = runtime.walkFiles(base);
|
|
@@ -10735,15 +11133,15 @@ var diffFindings = (diff) => {
|
|
|
10735
11133
|
};
|
|
10736
11134
|
var scanStaged = (runtime, root, includeUnstaged = false) => {
|
|
10737
11135
|
const args2 = includeUnstaged ? ["diff", "HEAD", ...DIFF_FLAGS] : ["diff", "--cached", ...DIFF_FLAGS];
|
|
10738
|
-
const
|
|
10739
|
-
if (!
|
|
10740
|
-
return accept(diffFindings(
|
|
11136
|
+
const git2 = runtime.git(args2, root);
|
|
11137
|
+
if (!git2.ok) return reject(git2.found ? "git diff failed" : "git not found");
|
|
11138
|
+
return accept(diffFindings(git2.stdout));
|
|
10741
11139
|
};
|
|
10742
11140
|
var scanHistory = async (runtime, root) => {
|
|
10743
11141
|
const scanner = diffScanner();
|
|
10744
11142
|
const args2 = ["log", "-p", "--all", "--format=commit %H", ...DIFF_FLAGS];
|
|
10745
|
-
const
|
|
10746
|
-
if (!
|
|
11143
|
+
const git2 = await runtime.gitLines(args2, root, scanner.push);
|
|
11144
|
+
if (!git2.ok) return reject(git2.found ? "git log failed" : "git not found");
|
|
10747
11145
|
return accept(scanner.end().filter((finding4) => !SKIPPED.test(finding4.file)));
|
|
10748
11146
|
};
|
|
10749
11147
|
|
|
@@ -12090,7 +12488,7 @@ var subagentStart = (input) => {
|
|
|
12090
12488
|
|
|
12091
12489
|
// src/gate/hooks/index.ts
|
|
12092
12490
|
var PORTAO_HANDLERS = {
|
|
12093
|
-
PreToolUse: [selfGuard, bypassGuard, testsGuard],
|
|
12491
|
+
PreToolUse: [reviewAgentGuard, selfGuard, bypassGuard, testsGuard],
|
|
12094
12492
|
UserPromptSubmit: [pausePrompt, promptRelease],
|
|
12095
12493
|
Stop: [unlessPaused(stopGate)],
|
|
12096
12494
|
SubagentStop: [unlessPaused(subagentStopGate)],
|
|
@@ -12634,7 +13032,11 @@ var isProdHostOf = (runtime, root, targets2) => memoBy((raw) => {
|
|
|
12634
13032
|
});
|
|
12635
13033
|
var blockOf = (t, hit3, command, deploy) => ({
|
|
12636
13034
|
kind: "block",
|
|
12637
|
-
reason: hit3.effect === "interactive" ? t
|
|
13035
|
+
reason: hit3.effect === "interactive" ? modelReason(t, "prod.block.interactive", { command, target: hit3.target }) : modelReason(t, "prod.block.effect", {
|
|
13036
|
+
command,
|
|
13037
|
+
target: hit3.target,
|
|
13038
|
+
deploy: deploy ?? t("prod.deploy.unknown")
|
|
13039
|
+
})
|
|
12638
13040
|
});
|
|
12639
13041
|
var prodGuard = (runtime, root, t) => {
|
|
12640
13042
|
const targets2 = memo2(() => loadProdTargets(runtime, root, loadProjectConfig(runtime, root)));
|
|
@@ -13629,7 +14031,7 @@ var RULE_KEYS = {
|
|
|
13629
14031
|
"protected-push": "git.block.protected_push"
|
|
13630
14032
|
};
|
|
13631
14033
|
var MAX_FILES2 = 8;
|
|
13632
|
-
var ruleMessage = (t, rule, command, subject2) => t
|
|
14034
|
+
var ruleMessage = (t, rule, command, subject2) => modelReason(t, RULE_KEYS[rule], { command, subject: subject2 === "" ? command : subject2 });
|
|
13633
14035
|
var fileList = (items) => {
|
|
13634
14036
|
const head = items.slice(0, MAX_FILES2).join(", ");
|
|
13635
14037
|
return items.length > MAX_FILES2 ? `${head} (+${String(items.length - MAX_FILES2)})` : head;
|
|
@@ -13640,12 +14042,12 @@ var outcomeMessage = (t, outcome2) => {
|
|
|
13640
14042
|
case "block":
|
|
13641
14043
|
return ruleMessage(t, outcome2.rule, outcome2.command, outcome2.subject);
|
|
13642
14044
|
case "foreign":
|
|
13643
|
-
return t
|
|
14045
|
+
return modelReason(t, "git.block.foreign", {
|
|
13644
14046
|
command: outcome2.command,
|
|
13645
14047
|
files: fileList(outcome2.files.map((file) => foreignLabel(t, file)))
|
|
13646
14048
|
});
|
|
13647
14049
|
case "unknown":
|
|
13648
|
-
return t
|
|
14050
|
+
return modelReason(t, "git.block.unknown", { command: outcome2.command });
|
|
13649
14051
|
case "pass":
|
|
13650
14052
|
case "snapshot":
|
|
13651
14053
|
return void 0;
|
|
@@ -13667,7 +14069,8 @@ var snapshotsOf = (input, owner, pending) => {
|
|
|
13667
14069
|
if (!taken.ok) {
|
|
13668
14070
|
return {
|
|
13669
14071
|
kind: "block",
|
|
13670
|
-
reason: input.t
|
|
14072
|
+
reason: modelReason(input.t, "git.block.snapshot_failed", { command: item2.command, detail: taken.error }),
|
|
14073
|
+
human: input.t("git.block.human")
|
|
13671
14074
|
};
|
|
13672
14075
|
}
|
|
13673
14076
|
done.set(item2.repo.root, input.t("git.snapshot.context", { command: item2.command, ref: taken.value.ref }));
|
|
@@ -13685,13 +14088,13 @@ var review = (input, commands, owner) => {
|
|
|
13685
14088
|
const chain = { branch: void 0 };
|
|
13686
14089
|
for (const command of commands) {
|
|
13687
14090
|
const removal = rmOutcome(input, command, worktrees);
|
|
13688
|
-
if (removal !== void 0) return { blocked: removal, snapshots, reads };
|
|
14091
|
+
if (removal !== void 0) return { blocked: removal, human: "git.block.human", snapshots, reads };
|
|
13689
14092
|
const decision = PROD_PROGRAMS.has(command.program) ? prod().decide(command) : void 0;
|
|
13690
|
-
if (decision?.kind === "block") return { blocked: decision.reason, snapshots, reads };
|
|
14093
|
+
if (decision?.kind === "block") return { blocked: decision.reason, human: "prod.block.human", snapshots, reads };
|
|
13691
14094
|
if (decision !== void 0) reads.push(decision);
|
|
13692
14095
|
for (const outcome2 of guardGitCommand({ runtime: input.runtime, cwd: input.cwd, owner, chain }, command)) {
|
|
13693
14096
|
const message = outcomeMessage(input.t, outcome2);
|
|
13694
|
-
if (message !== void 0) return { blocked: message, snapshots, reads };
|
|
14097
|
+
if (message !== void 0) return { blocked: message, human: "git.block.human", snapshots, reads };
|
|
13695
14098
|
if (outcome2.kind === "snapshot") snapshots.push(outcome2);
|
|
13696
14099
|
}
|
|
13697
14100
|
}
|
|
@@ -13704,7 +14107,9 @@ var gitPreToolUse = (input) => {
|
|
|
13704
14107
|
if (commands.length === 0) return ALLOW;
|
|
13705
14108
|
const owner = ownerOf2(input.payload);
|
|
13706
14109
|
const result = review(input, commands, owner);
|
|
13707
|
-
if (result.blocked !== void 0)
|
|
14110
|
+
if (result.blocked !== void 0) {
|
|
14111
|
+
return { kind: "block", reason: result.blocked, human: input.t(result.human ?? "git.block.human") };
|
|
14112
|
+
}
|
|
13708
14113
|
if (result.reads.length > 0) {
|
|
13709
14114
|
recordEvidence(
|
|
13710
14115
|
input.runtime,
|
|
@@ -13947,14 +14352,19 @@ var shown2 = (cwd, file) => {
|
|
|
13947
14352
|
};
|
|
13948
14353
|
var blocked = (input, file) => ({
|
|
13949
14354
|
kind: "block",
|
|
13950
|
-
reason: input.t
|
|
14355
|
+
reason: modelReason(input.t, "prose.block.marker", { marker: ALLOW_COMMENT, file }),
|
|
14356
|
+
human: input.t("prose.block.human")
|
|
13951
14357
|
});
|
|
13952
14358
|
var proseMarkerGuard = (input) => {
|
|
13953
14359
|
const tool = toolName2(input.payload);
|
|
13954
14360
|
const inputs = toolInput2(input.payload);
|
|
13955
14361
|
if (SHELLS2.has(tool)) {
|
|
13956
14362
|
if (!shellAddsMarker(optionalString(inputs, "command") ?? "")) return ALLOW3;
|
|
13957
|
-
return {
|
|
14363
|
+
return {
|
|
14364
|
+
kind: "block",
|
|
14365
|
+
reason: modelReason(input.t, "prose.block.marker_shell", { marker: ALLOW_COMMENT }),
|
|
14366
|
+
human: input.t("prose.block.human")
|
|
14367
|
+
};
|
|
13958
14368
|
}
|
|
13959
14369
|
const file = optionalString(inputs, "file_path") ?? optionalString(inputs, "notebook_path") ?? "";
|
|
13960
14370
|
return addsMarker(input, tool, inputs, file) ? blocked(input, shown2(input.cwd, file)) : ALLOW3;
|
|
@@ -14097,7 +14507,11 @@ var textOf2 = (input, found) => {
|
|
|
14097
14507
|
var decisionFor = (input, found, blocking2) => {
|
|
14098
14508
|
const text7 = textOf2(input, found);
|
|
14099
14509
|
if (!text7.ok) {
|
|
14100
|
-
return blocking2 ? {
|
|
14510
|
+
return blocking2 ? {
|
|
14511
|
+
kind: "block",
|
|
14512
|
+
reason: modelReason(input.t, "prose.block.unreadable", { ...text7.error }),
|
|
14513
|
+
human: input.t("prose.block.human")
|
|
14514
|
+
} : void 0;
|
|
14101
14515
|
}
|
|
14102
14516
|
const report2 = scoreProse(text7.value, { context: "record" });
|
|
14103
14517
|
if (report2.allowed || report2.spans.length === 0) return void 0;
|
|
@@ -14106,7 +14520,12 @@ var decisionFor = (input, found, blocking2) => {
|
|
|
14106
14520
|
spans: describeSpans(input.t, report2.spans),
|
|
14107
14521
|
fixes: describeFixes(input.t, report2.spans)
|
|
14108
14522
|
};
|
|
14109
|
-
if (blocking2)
|
|
14523
|
+
if (blocking2)
|
|
14524
|
+
return {
|
|
14525
|
+
kind: "block",
|
|
14526
|
+
reason: modelReason(input.t, "prose.block.command", params2),
|
|
14527
|
+
human: input.t("prose.block.human")
|
|
14528
|
+
};
|
|
14110
14529
|
const additionalContext = `finitemonkey: ${input.t("prose.warn.command", params2)}`;
|
|
14111
14530
|
return { kind: "allow", output: { hookSpecificOutput: { hookEventName: "PreToolUse", additionalContext } } };
|
|
14112
14531
|
};
|
|
@@ -14303,18 +14722,22 @@ var writeDecision = (input, tool) => {
|
|
|
14303
14722
|
ruleId: finding4.ruleId,
|
|
14304
14723
|
where: written.whole ? t("secrets.where.file", { file, line: finding4.line, column: finding4.column }) : t("secrets.where.new_text", { file, line: finding4.line })
|
|
14305
14724
|
}));
|
|
14306
|
-
return {
|
|
14725
|
+
return {
|
|
14726
|
+
kind: "block",
|
|
14727
|
+
reason: modelReason(t, "secrets.block.write", { file, found: describeFindings(t, items) }),
|
|
14728
|
+
human: t("secrets.block.human")
|
|
14729
|
+
};
|
|
14307
14730
|
}
|
|
14308
14731
|
return warningDecision(t, warnings);
|
|
14309
14732
|
};
|
|
14310
14733
|
var bashMessage = (t, finding4) => {
|
|
14311
14734
|
switch (finding4.kind) {
|
|
14312
14735
|
case "read":
|
|
14313
|
-
return t
|
|
14736
|
+
return modelReason(t, "secrets.block.read", { target: finding4.target });
|
|
14314
14737
|
case "env-dump":
|
|
14315
|
-
return t
|
|
14738
|
+
return modelReason(t, "secrets.block.env_dump", { command: finding4.command });
|
|
14316
14739
|
case "person-only":
|
|
14317
|
-
return t
|
|
14740
|
+
return modelReason(t, "secrets.block.person_only", { command: finding4.command });
|
|
14318
14741
|
}
|
|
14319
14742
|
};
|
|
14320
14743
|
var commitDecision = (input, includeUnstaged) => {
|
|
@@ -14325,7 +14748,11 @@ var commitDecision = (input, includeUnstaged) => {
|
|
|
14325
14748
|
ruleId: finding4.ruleId,
|
|
14326
14749
|
where: input.t("secrets.where.file", { file: finding4.file, line: finding4.line, column: finding4.column })
|
|
14327
14750
|
}));
|
|
14328
|
-
return {
|
|
14751
|
+
return {
|
|
14752
|
+
kind: "block",
|
|
14753
|
+
reason: modelReason(input.t, "secrets.block.commit", { found: describeFindings(input.t, items) }),
|
|
14754
|
+
human: input.t("secrets.block.human")
|
|
14755
|
+
};
|
|
14329
14756
|
};
|
|
14330
14757
|
var bashDecision = (input) => {
|
|
14331
14758
|
const command = optionalString(toolInput2(input.payload), "command") ?? "";
|
|
@@ -14336,7 +14763,11 @@ var bashDecision = (input) => {
|
|
|
14336
14763
|
const found = blocking(scanText(command));
|
|
14337
14764
|
if (found.length > 0) {
|
|
14338
14765
|
const items = found.map((finding4) => ({ ruleId: finding4.ruleId, where: lineWhere(input.t, finding4) }));
|
|
14339
|
-
return {
|
|
14766
|
+
return {
|
|
14767
|
+
kind: "block",
|
|
14768
|
+
reason: modelReason(input.t, "secrets.block.bash", { found: describeFindings(input.t, items) }),
|
|
14769
|
+
human: input.t("secrets.block.human")
|
|
14770
|
+
};
|
|
14340
14771
|
}
|
|
14341
14772
|
const commit2 = commitIntent(segments);
|
|
14342
14773
|
return commit2 === void 0 ? ALLOW5 : commitDecision(input, commit2.includeUnstaged);
|
|
@@ -14345,7 +14776,11 @@ var readDecision = (input) => {
|
|
|
14345
14776
|
const inputs = toolInput2(input.payload);
|
|
14346
14777
|
const target2 = optionalString(inputs, "file_path") ?? optionalString(inputs, "path") ?? "";
|
|
14347
14778
|
if (target2 === "" || !isSensitivePath(target2)) return ALLOW5;
|
|
14348
|
-
return {
|
|
14779
|
+
return {
|
|
14780
|
+
kind: "block",
|
|
14781
|
+
reason: modelReason(input.t, "secrets.block.read", { target: relativeFile(input.cwd, target2) }),
|
|
14782
|
+
human: input.t("secrets.block.human")
|
|
14783
|
+
};
|
|
14349
14784
|
};
|
|
14350
14785
|
var WRITERS2 = /* @__PURE__ */ new Set(["Write", "Edit", "MultiEdit", "NotebookEdit"]);
|
|
14351
14786
|
var SHELLS4 = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
|
|
@@ -14427,11 +14862,23 @@ var mergeOutputs = (left, right) => {
|
|
|
14427
14862
|
if (specific !== void 0) merged2["hookSpecificOutput"] = specific;
|
|
14428
14863
|
return merged2;
|
|
14429
14864
|
};
|
|
14865
|
+
var shaped = (input, decision) => {
|
|
14866
|
+
if (input.event !== "PreToolUse" || decision.output !== void 0) return decision;
|
|
14867
|
+
const reason = capReason(decision.reason);
|
|
14868
|
+
return {
|
|
14869
|
+
...decision,
|
|
14870
|
+
reason,
|
|
14871
|
+
output: {
|
|
14872
|
+
hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: reason },
|
|
14873
|
+
systemMessage: `finitemonkey: ${decision.human ?? input.t("reason.human.generic")}`
|
|
14874
|
+
}
|
|
14875
|
+
};
|
|
14876
|
+
};
|
|
14430
14877
|
var composeDecisions = async (input, handlers) => {
|
|
14431
14878
|
let output = {};
|
|
14432
14879
|
for (const handler of handlers) {
|
|
14433
14880
|
const decision = await handler(input);
|
|
14434
|
-
if (decision.kind === "block") return decision;
|
|
14881
|
+
if (decision.kind === "block") return shaped(input, decision);
|
|
14435
14882
|
output = mergeOutputs(output, decision.output);
|
|
14436
14883
|
}
|
|
14437
14884
|
return { kind: "allow", output };
|
|
@@ -16390,14 +16837,38 @@ var sourceProblems = (catalog) => Object.keys(catalog).sort().flatMap((key) => {
|
|
|
16390
16837
|
const value = catalog[key];
|
|
16391
16838
|
return typeof value === "string" ? pluralProblems(SOURCE_LANG, key, value, value) : [];
|
|
16392
16839
|
});
|
|
16840
|
+
var MODEL_FACING = /\.(claim|requires|not)$|^gate\.tests\.|^reason\.label\./;
|
|
16841
|
+
var RELEASE_HINTS = [
|
|
16842
|
+
/--yes\b/,
|
|
16843
|
+
/\b(finitemonkey|fm)\b[^\n]{0,20}\b(allow|off|on|trust|approve)\b/i,
|
|
16844
|
+
/\ballow\s+(bypass|tests|self|config)\b/i,
|
|
16845
|
+
/\bFINITEMONKEY_[A-Z_]+/,
|
|
16846
|
+
/\b[A-Z][A-Z0-9_]{2,}=/
|
|
16847
|
+
];
|
|
16848
|
+
var releaseHints = (text7) => RELEASE_HINTS.flatMap((pattern) => {
|
|
16849
|
+
const match = pattern.exec(text7);
|
|
16850
|
+
return match === null ? [] : [match[0]];
|
|
16851
|
+
});
|
|
16852
|
+
var hintProblems = (lang, catalog) => Object.keys(catalog).filter((key) => MODEL_FACING.test(key)).sort().flatMap((key) => {
|
|
16853
|
+
const value = catalog[key];
|
|
16854
|
+
const found = typeof value === "string" ? releaseHints(value) : [];
|
|
16855
|
+
return found.length === 0 ? [] : [{ lang, key, kind: "release_hint", actual: found }];
|
|
16856
|
+
});
|
|
16393
16857
|
var checkCatalogs = (input, review2) => {
|
|
16394
16858
|
const reference = input[SOURCE_LANG];
|
|
16395
16859
|
const problems = LANGUAGES.flatMap((lang) => {
|
|
16396
|
-
if (lang === SOURCE_LANG)
|
|
16860
|
+
if (lang === SOURCE_LANG) {
|
|
16861
|
+
return [
|
|
16862
|
+
...valueProblems(lang, input[lang], reference),
|
|
16863
|
+
...sourceProblems(reference),
|
|
16864
|
+
...hintProblems(lang, reference)
|
|
16865
|
+
];
|
|
16866
|
+
}
|
|
16397
16867
|
const statuses = review2?.[lang];
|
|
16398
16868
|
return [
|
|
16399
16869
|
...valueProblems(lang, input[lang], reference),
|
|
16400
16870
|
...parityProblems(lang, input[lang], reference),
|
|
16871
|
+
...hintProblems(lang, input[lang]),
|
|
16401
16872
|
...review2 === void 0 ? [] : statusProblems(lang, input[lang], statuses ?? {})
|
|
16402
16873
|
];
|
|
16403
16874
|
});
|
|
@@ -17377,6 +17848,96 @@ var proseCheckCommand = {
|
|
|
17377
17848
|
}
|
|
17378
17849
|
};
|
|
17379
17850
|
|
|
17851
|
+
// src/review/packet.ts
|
|
17852
|
+
import { join as join45 } from "node:path";
|
|
17853
|
+
var REVIEW_STATE_FILE = ".finitemonkey/review/state.json";
|
|
17854
|
+
var FULL_ROUNDS = 3;
|
|
17855
|
+
var REVIEW_FILE_MAX_LINES = 200;
|
|
17856
|
+
var LEDGER_MAX_LINES = 100;
|
|
17857
|
+
var statePath = (root) => toAbsolutePath(join45(root, REVIEW_STATE_FILE));
|
|
17858
|
+
var readReviewState = (runtime, root) => {
|
|
17859
|
+
const raw = runtime.readText(statePath(root));
|
|
17860
|
+
const parsed = raw.ok ? parseJsonObject(raw.value) : void 0;
|
|
17861
|
+
if (parsed === void 0 || !parsed.ok || !isRecord(parsed.value)) return void 0;
|
|
17862
|
+
const { base, rounds, lastHead } = parsed.value;
|
|
17863
|
+
return typeof base === "string" && typeof rounds === "number" && typeof lastHead === "string" ? { base, rounds, lastHead } : void 0;
|
|
17864
|
+
};
|
|
17865
|
+
var git = (runtime, root, args2) => {
|
|
17866
|
+
const result = runtime.git(args2, root);
|
|
17867
|
+
return result.ok ? result.stdout : void 0;
|
|
17868
|
+
};
|
|
17869
|
+
var lastLines = (text7, max) => text7.split("\n").slice(-max).join("\n");
|
|
17870
|
+
var section = (title, body2, empty) => `## ${title}
|
|
17871
|
+
|
|
17872
|
+
${body2 === void 0 || body2.trim() === "" ? empty : body2.trimEnd()}
|
|
17873
|
+
`;
|
|
17874
|
+
var taskSections = (runtime, root) => {
|
|
17875
|
+
const paths = fmPaths(root);
|
|
17876
|
+
const active = readState(runtime, paths).active;
|
|
17877
|
+
if (active === void 0)
|
|
17878
|
+
return [section("Contract", void 0, "(no active task)"), section("Evidence ledger", void 0, "(none)")];
|
|
17879
|
+
const contract = runtime.readText(toAbsolutePath(join45(root, taskRelative(active))));
|
|
17880
|
+
const ledger = runtime.readText(paths.evidence(active));
|
|
17881
|
+
return [
|
|
17882
|
+
section("Contract", contract.ok ? contract.value : void 0, "(contract file missing)"),
|
|
17883
|
+
section("Evidence ledger", ledger.ok ? lastLines(ledger.value, LEDGER_MAX_LINES) : void 0, "(none)")
|
|
17884
|
+
];
|
|
17885
|
+
};
|
|
17886
|
+
var reviewFile = (runtime, root, base) => {
|
|
17887
|
+
const text7 = git(runtime, root, ["show", `${base}:REVIEW.md`]);
|
|
17888
|
+
return text7 === void 0 ? void 0 : text7.split("\n").slice(0, REVIEW_FILE_MAX_LINES).join("\n");
|
|
17889
|
+
};
|
|
17890
|
+
var nextState = (previous, base, head) => {
|
|
17891
|
+
if (previous?.base !== base) return { base, rounds: 1, lastHead: head };
|
|
17892
|
+
return { base, rounds: previous.rounds + 1, lastHead: head };
|
|
17893
|
+
};
|
|
17894
|
+
var buildPacket = (runtime, root, base, head) => {
|
|
17895
|
+
const previous = readReviewState(runtime, root);
|
|
17896
|
+
const state = nextState(previous, base, head);
|
|
17897
|
+
const from = state.rounds > FULL_ROUNDS && previous !== void 0 ? previous.lastHead : base;
|
|
17898
|
+
const range = `${from}...${head}`;
|
|
17899
|
+
const diff = git(runtime, root, ["diff", "--no-color", "--no-ext-diff", range, "--", ".", ":(exclude).finitemonkey"]);
|
|
17900
|
+
const owners = reviewFile(runtime, root, base);
|
|
17901
|
+
const text7 = [
|
|
17902
|
+
`# Review packet (round ${String(state.rounds)}, ${range})`,
|
|
17903
|
+
"",
|
|
17904
|
+
...state.rounds > FULL_ROUNDS ? ["Only the diff since the last review round is included.", ""] : [],
|
|
17905
|
+
section("Diff", diff, "(empty diff)"),
|
|
17906
|
+
...taskSections(runtime, root),
|
|
17907
|
+
...owners === void 0 ? [] : [section("REVIEW.md (base branch)", owners, "")]
|
|
17908
|
+
].join("\n");
|
|
17909
|
+
runtime.mkdir(toAbsolutePath(join45(root, ".finitemonkey", "review")));
|
|
17910
|
+
runtime.writeAtomic(statePath(root), `${JSON.stringify(state, null, 2)}
|
|
17911
|
+
`);
|
|
17912
|
+
return { round: state.rounds, base, head, range, text: text7 };
|
|
17913
|
+
};
|
|
17914
|
+
|
|
17915
|
+
// src/commands/review.ts
|
|
17916
|
+
var REVIEW_PACKET_SCHEMA = "finitemonkey/review-packet@1";
|
|
17917
|
+
var reviewPacketCommand = {
|
|
17918
|
+
path: ["review", "packet"],
|
|
17919
|
+
summaryKey: "review.command.packet",
|
|
17920
|
+
usage: "finitemonkey review packet [--base <ref>] [--json]",
|
|
17921
|
+
status: "ready",
|
|
17922
|
+
options: { base: { type: "string" } },
|
|
17923
|
+
run: (ctx, args2) => {
|
|
17924
|
+
const repo = repoOf2(ctx);
|
|
17925
|
+
if (repo === void 0) return Promise.resolve(failureFrom(ctx.t, notRepository(ctx.cwd)));
|
|
17926
|
+
const base = stringOption(args2.values, "base") ?? repoFacts(ctx.runtime, repo).defaultBranch();
|
|
17927
|
+
const resolved = ctx.runtime.git(["rev-parse", "--verify", "--quiet", `${base}^{commit}`], repo.root);
|
|
17928
|
+
const head = ctx.runtime.git(["rev-parse", "HEAD"], repo.root).stdout.trim();
|
|
17929
|
+
if (!resolved.ok || head === "")
|
|
17930
|
+
return Promise.resolve(
|
|
17931
|
+
failureFrom(
|
|
17932
|
+
ctx.t,
|
|
17933
|
+
gitProblem("invalid_arguments", "review.error.no_base", base, "finitemonkey review packet --base <ref>")
|
|
17934
|
+
)
|
|
17935
|
+
);
|
|
17936
|
+
const packet = buildPacket(ctx.runtime, repo.root, base, head);
|
|
17937
|
+
return Promise.resolve(success({ schema: REVIEW_PACKET_SCHEMA, ...packet }, [packet.text]));
|
|
17938
|
+
}
|
|
17939
|
+
};
|
|
17940
|
+
|
|
17380
17941
|
// src/commands/secrets-problems.ts
|
|
17381
17942
|
var baselineInvalid = (detail) => ({
|
|
17382
17943
|
code: "secrets_baseline_invalid",
|
|
@@ -17500,7 +18061,7 @@ var secretsAcceptCommand = {
|
|
|
17500
18061
|
};
|
|
17501
18062
|
|
|
17502
18063
|
// src/secrets/runbooks.ts
|
|
17503
|
-
import { dirname as dirname9, join as
|
|
18064
|
+
import { dirname as dirname9, join as join46 } from "node:path";
|
|
17504
18065
|
var PROVIDER_OF_RULE = {
|
|
17505
18066
|
"aws-access-token": "aws",
|
|
17506
18067
|
"aws-secret-access-key": "aws",
|
|
@@ -17522,7 +18083,7 @@ var RUNBOOKS_URL = "https://github.com/gabrielvieiraf/finitemonkey/blob/main/run
|
|
|
17522
18083
|
var runbookProvider = (rule) => PROVIDER_OF_RULE[rule] ?? "generic";
|
|
17523
18084
|
var runbookLink = (runtime, rule) => {
|
|
17524
18085
|
const file = `${runbookProvider(rule)}.md`;
|
|
17525
|
-
const local = toAbsolutePath(
|
|
18086
|
+
const local = toAbsolutePath(join46(dirname9(dirname9(runtime.selfPath)), "runbooks", file));
|
|
17526
18087
|
return runtime.stat(local)?.kind === "file" ? local : `${RUNBOOKS_URL}/${file}`;
|
|
17527
18088
|
};
|
|
17528
18089
|
|
|
@@ -17884,7 +18445,7 @@ var secretsScanCommand = {
|
|
|
17884
18445
|
};
|
|
17885
18446
|
|
|
17886
18447
|
// src/task/create.ts
|
|
17887
|
-
import { join as
|
|
18448
|
+
import { join as join47 } from "node:path";
|
|
17888
18449
|
|
|
17889
18450
|
// src/core/yaml/write.ts
|
|
17890
18451
|
var PLAIN_SAFE = /^[A-Za-z_./][A-Za-z0-9_./ ()-]*$/;
|
|
@@ -17973,10 +18534,10 @@ var explicitChecks = (draft) => [
|
|
|
17973
18534
|
var draftChecks = (runtime, paths, draft) => {
|
|
17974
18535
|
const disk = {
|
|
17975
18536
|
read: (relative11) => {
|
|
17976
|
-
const text7 = runtime.readText(toAbsolutePath(
|
|
18537
|
+
const text7 = runtime.readText(toAbsolutePath(join47(paths.root, relative11)));
|
|
17977
18538
|
return text7.ok ? text7.value : void 0;
|
|
17978
18539
|
},
|
|
17979
|
-
exists: (relative11) => runtime.stat(toAbsolutePath(
|
|
18540
|
+
exists: (relative11) => runtime.stat(toAbsolutePath(join47(paths.root, relative11))) !== void 0
|
|
17980
18541
|
};
|
|
17981
18542
|
const detected = detectChecks(disk);
|
|
17982
18543
|
const base = draft.runs.length > 0 || draft.tests !== void 0 ? explicitChecks(draft) : detected;
|
|
@@ -17991,7 +18552,7 @@ var draftChecks = (runtime, paths, draft) => {
|
|
|
17991
18552
|
{ id: "merge", kind: "merge" },
|
|
17992
18553
|
{ id: "callers", kind: "callers" },
|
|
17993
18554
|
...draft.paths.length > 0 || draft.budget !== void 0 ? [{ id: "diff", kind: "diff" }] : [],
|
|
17994
|
-
...runtime.stat(toAbsolutePath(
|
|
18555
|
+
...runtime.stat(toAbsolutePath(join47(paths.root, LOOK_CONFIG))) === void 0 ? [] : [{ id: "look", kind: "look" }],
|
|
17995
18556
|
...draft.git.length > 0 ? [{ id: "git", kind: "git", require: draft.git }] : [],
|
|
17996
18557
|
{ id: "pr", kind: "pr", requireOpen: draft.git.includes("pr_open") },
|
|
17997
18558
|
...draft.manual.map((description, index) => ({
|
|
@@ -18393,12 +18954,13 @@ var REGISTRY = [
|
|
|
18393
18954
|
pushCheckCommand,
|
|
18394
18955
|
ciGateCommand,
|
|
18395
18956
|
lookCommand,
|
|
18957
|
+
reviewPacketCommand,
|
|
18396
18958
|
versionCommand,
|
|
18397
18959
|
helpCommand
|
|
18398
18960
|
];
|
|
18399
18961
|
|
|
18400
18962
|
// src/i18n/lang-settings.ts
|
|
18401
|
-
import { join as
|
|
18963
|
+
import { join as join48 } from "node:path";
|
|
18402
18964
|
var read3 = (runtime, path) => {
|
|
18403
18965
|
const text7 = runtime.readText(toAbsolutePath(path));
|
|
18404
18966
|
return text7.ok ? text7.value : void 0;
|
|
@@ -18413,14 +18975,14 @@ var languageIn = (text7) => {
|
|
|
18413
18975
|
const value = parsed.ok && isRecord(parsed.value) ? parsed.value["language"] : void 0;
|
|
18414
18976
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
18415
18977
|
};
|
|
18416
|
-
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ??
|
|
18978
|
+
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join48(runtime.homeDir(), ".claude");
|
|
18417
18979
|
var langSettings = (runtime, cwd, env, platform) => {
|
|
18418
18980
|
const root = findGitRoot(cwd, runtime) ?? cwd;
|
|
18419
|
-
const config = parseConfigYaml(read3(runtime,
|
|
18981
|
+
const config = parseConfigYaml(read3(runtime, join48(root, CONFIG_FILE)) ?? "");
|
|
18420
18982
|
const claudeFiles = [
|
|
18421
|
-
|
|
18422
|
-
|
|
18423
|
-
|
|
18983
|
+
join48(root, ".claude", "settings.local.json"),
|
|
18984
|
+
join48(root, ".claude", "settings.json"),
|
|
18985
|
+
join48(claudeDir(runtime, env), "settings.json")
|
|
18424
18986
|
];
|
|
18425
18987
|
return {
|
|
18426
18988
|
config: configText(config, "lang"),
|