finitemonkey 0.1.0-next.20 → 0.1.0-next.21
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 +2 -1
- package/README.md +2 -1
- package/README.pt-BR.md +2 -1
- package/dist/finitemonkey.mjs +1211 -617
- package/package.json +2 -1
package/dist/finitemonkey.mjs
CHANGED
|
@@ -28,15 +28,15 @@ import { dirname } from "node:path";
|
|
|
28
28
|
var accept = (value) => ({ ok: true, value });
|
|
29
29
|
var reject = (error) => ({ ok: false, error });
|
|
30
30
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31
|
-
var parseJson = (
|
|
31
|
+
var parseJson = (text7) => {
|
|
32
32
|
try {
|
|
33
|
-
return accept(JSON.parse(
|
|
33
|
+
return accept(JSON.parse(text7));
|
|
34
34
|
} catch (error) {
|
|
35
35
|
return reject(error instanceof Error ? error.message : String(error));
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
|
-
var parseJsonObject = (
|
|
39
|
-
const parsed = parseJson(
|
|
38
|
+
var parseJsonObject = (text7) => {
|
|
39
|
+
const parsed = parseJson(text7);
|
|
40
40
|
if (!parsed.ok) return parsed;
|
|
41
41
|
return isRecord(parsed.value) ? accept(parsed.value) : reject("expected a JSON object");
|
|
42
42
|
};
|
|
@@ -160,13 +160,13 @@ var attempt = (work) => {
|
|
|
160
160
|
return reject(messageOf(error));
|
|
161
161
|
}
|
|
162
162
|
};
|
|
163
|
-
var nodeWriteText = (path,
|
|
163
|
+
var nodeWriteText = (path, text7) => {
|
|
164
164
|
const temp = `${path}.${String(process.pid)}.tmp`;
|
|
165
165
|
try {
|
|
166
166
|
mkdirSync2(dirname2(path), { recursive: true, mode: 448 });
|
|
167
167
|
const fd = openSync(temp, "w", 384);
|
|
168
168
|
try {
|
|
169
|
-
writeSync(fd,
|
|
169
|
+
writeSync(fd, text7);
|
|
170
170
|
fsyncSync(fd);
|
|
171
171
|
} finally {
|
|
172
172
|
closeSync(fd);
|
|
@@ -778,7 +778,7 @@ import { dirname as dirname3 } from "node:path";
|
|
|
778
778
|
|
|
779
779
|
// src/install/hash.ts
|
|
780
780
|
import { createHash } from "node:crypto";
|
|
781
|
-
var hashText = (
|
|
781
|
+
var hashText = (text7) => `sha256:${createHash("sha256").update(text7, "utf8").digest("hex")}`;
|
|
782
782
|
|
|
783
783
|
// src/install/layout.ts
|
|
784
784
|
var STATE_DIR = ".finitemonkey";
|
|
@@ -863,36 +863,36 @@ var fromRecord = (record3) => {
|
|
|
863
863
|
}
|
|
864
864
|
return accept({ schema, version, preset: known2, installedAt, updatedAt, createdDirs, entries });
|
|
865
865
|
};
|
|
866
|
-
var parseLock = (
|
|
867
|
-
const parsed = parseJsonObject(
|
|
866
|
+
var parseLock = (text7) => {
|
|
867
|
+
const parsed = parseJsonObject(text7);
|
|
868
868
|
return parsed.ok ? fromRecord(parsed.value) : reject(parsed.error);
|
|
869
869
|
};
|
|
870
870
|
var renderLock = (lock) => `${JSON.stringify(lock, null, 2)}
|
|
871
871
|
`;
|
|
872
872
|
|
|
873
873
|
// src/install/managed-block.ts
|
|
874
|
-
var lineBreak = (
|
|
874
|
+
var lineBreak = (text7) => text7.includes("\r\n") ? "\r\n" : "\n";
|
|
875
875
|
var blockRange = (lines3, markers) => {
|
|
876
876
|
const start = lines3.findIndex((line2) => line2.trimEnd() === markers.begin);
|
|
877
877
|
if (start < 0) return void 0;
|
|
878
878
|
const end = lines3.findIndex((line2, index) => index > start && line2.trimEnd() === markers.end);
|
|
879
879
|
return end < 0 ? void 0 : { start, end };
|
|
880
880
|
};
|
|
881
|
-
var withManagedBlock = (
|
|
882
|
-
const eol =
|
|
881
|
+
var withManagedBlock = (text7, markers, body2) => {
|
|
882
|
+
const eol = text7 === void 0 ? "\n" : lineBreak(text7);
|
|
883
883
|
const block4 = [markers.begin, ...body2, markers.end];
|
|
884
|
-
if (
|
|
885
|
-
const lines3 =
|
|
884
|
+
if (text7 === void 0 || text7 === "") return `${block4.join(eol)}${eol}`;
|
|
885
|
+
const lines3 = text7.split(eol);
|
|
886
886
|
const range = blockRange(lines3, markers);
|
|
887
887
|
if (range !== void 0) return [...lines3.slice(0, range.start), ...block4, ...lines3.slice(range.end + 1)].join(eol);
|
|
888
|
-
const separator =
|
|
889
|
-
return `${
|
|
888
|
+
const separator = text7.endsWith(eol) ? "" : eol;
|
|
889
|
+
return `${text7}${separator}${block4.join(eol)}${eol}`;
|
|
890
890
|
};
|
|
891
|
-
var withoutManagedBlock = (
|
|
892
|
-
const eol = lineBreak(
|
|
893
|
-
const lines3 =
|
|
891
|
+
var withoutManagedBlock = (text7, markers) => {
|
|
892
|
+
const eol = lineBreak(text7);
|
|
893
|
+
const lines3 = text7.split(eol);
|
|
894
894
|
const range = blockRange(lines3, markers);
|
|
895
|
-
if (range === void 0) return
|
|
895
|
+
if (range === void 0) return text7;
|
|
896
896
|
return [...lines3.slice(0, range.start), ...lines3.slice(range.end + 1)].join(eol);
|
|
897
897
|
};
|
|
898
898
|
|
|
@@ -989,7 +989,7 @@ var configTemplate = (preset, lang) => [
|
|
|
989
989
|
`lang: ${lang}`,
|
|
990
990
|
""
|
|
991
991
|
].join("\n");
|
|
992
|
-
var hasMarker = (
|
|
992
|
+
var hasMarker = (text7) => text7.includes(GENERATED_MARKER);
|
|
993
993
|
|
|
994
994
|
// src/install/hook-entries.ts
|
|
995
995
|
var INSTALLED_HOOKS = [
|
|
@@ -1095,7 +1095,7 @@ var lefthookBody = (runner) => [
|
|
|
1095
1095
|
` run: ${cliCommand(runner, "push check {1}")}`,
|
|
1096
1096
|
" use_stdin: true"
|
|
1097
1097
|
];
|
|
1098
|
-
var definesHook = (
|
|
1098
|
+
var definesHook = (text7) => GIT_HOOK_NAMES.some((hook) => new RegExp(`^${hook}:`, "m").test(withoutManagedBlock(text7, HOOK_MARKERS)));
|
|
1099
1099
|
var lefthookItem = (project, runner, path) => {
|
|
1100
1100
|
const before = project.read(path);
|
|
1101
1101
|
const after = withManagedBlock(before, HOOK_MARKERS, lefthookBody(runner));
|
|
@@ -1120,8 +1120,8 @@ var preCommitBody = (runner, indent) => {
|
|
|
1120
1120
|
];
|
|
1121
1121
|
};
|
|
1122
1122
|
var TOP_KEY = /^[A-Za-z_][\w-]*:/;
|
|
1123
|
-
var reposIsLast = (
|
|
1124
|
-
const keys = withoutManagedBlock(
|
|
1123
|
+
var reposIsLast = (text7) => {
|
|
1124
|
+
const keys = withoutManagedBlock(text7, HOOK_MARKERS).split(/\r?\n/).filter((line2) => TOP_KEY.test(line2));
|
|
1125
1125
|
return keys.at(-1)?.startsWith("repos:") === true;
|
|
1126
1126
|
};
|
|
1127
1127
|
var preCommitItem = (project, runner) => {
|
|
@@ -1202,10 +1202,10 @@ var ensureTree = (tree, key) => {
|
|
|
1202
1202
|
tree[key] = fresh3;
|
|
1203
1203
|
return fresh3;
|
|
1204
1204
|
};
|
|
1205
|
-
var parseConfigYaml = (
|
|
1205
|
+
var parseConfigYaml = (text7) => {
|
|
1206
1206
|
const root = {};
|
|
1207
1207
|
const stack = [{ indent: -1, tree: root, key: void 0 }];
|
|
1208
|
-
for (const raw of
|
|
1208
|
+
for (const raw of text7.split(/\r?\n/)) {
|
|
1209
1209
|
const line2 = stripComment(raw).trimEnd();
|
|
1210
1210
|
if (line2.trim() === "" || line2.trim() === "---") continue;
|
|
1211
1211
|
const indent = line2.length - line2.trimStart().length;
|
|
@@ -1228,8 +1228,8 @@ var configMap = (tree, ...path) => {
|
|
|
1228
1228
|
);
|
|
1229
1229
|
};
|
|
1230
1230
|
var loadProjectConfig = (runtime, root) => {
|
|
1231
|
-
const
|
|
1232
|
-
return
|
|
1231
|
+
const text7 = runtime.readText(toAbsolutePath(join2(root, CONFIG_FILE2)));
|
|
1232
|
+
return text7.ok ? parseConfigYaml(text7.value) : {};
|
|
1233
1233
|
};
|
|
1234
1234
|
|
|
1235
1235
|
// src/policy/policy.ts
|
|
@@ -1247,8 +1247,8 @@ var DEFAULT_POLICY = {
|
|
|
1247
1247
|
guards: {}
|
|
1248
1248
|
};
|
|
1249
1249
|
var pick = (options, value, fallback) => options.find((option) => option === value) ?? fallback;
|
|
1250
|
-
var parsePolicy = (
|
|
1251
|
-
const tree = parseConfigYaml(
|
|
1250
|
+
var parsePolicy = (text7) => {
|
|
1251
|
+
const tree = parseConfigYaml(text7);
|
|
1252
1252
|
const raw = configAt(tree, "promoteAfter");
|
|
1253
1253
|
const promoteAfter = typeof raw === "string" ? Number.parseInt(raw, 10) : Number.NaN;
|
|
1254
1254
|
const guards = Object.fromEntries(
|
|
@@ -1267,23 +1267,23 @@ var parsePolicy = (text6) => {
|
|
|
1267
1267
|
};
|
|
1268
1268
|
var policyPath = (root) => toAbsolutePath(join3(root, POLICY_FILE));
|
|
1269
1269
|
var loadPolicy = (runtime, root) => {
|
|
1270
|
-
const
|
|
1271
|
-
return
|
|
1270
|
+
const text7 = runtime.readText(policyPath(root));
|
|
1271
|
+
return text7.ok ? parsePolicy(text7.value) : DEFAULT_POLICY;
|
|
1272
1272
|
};
|
|
1273
1273
|
|
|
1274
1274
|
// src/install/agents-block.ts
|
|
1275
1275
|
var MARKDOWN_MARKERS = { begin: MD_BLOCK_BEGIN, end: MD_BLOCK_END };
|
|
1276
1276
|
var IMPORT_LINE = /^@AGENTS\.md[ \t]*$/m;
|
|
1277
|
-
var withAgentsBlock = (
|
|
1278
|
-
|
|
1277
|
+
var withAgentsBlock = (text7, runner, lang) => withManagedBlock(
|
|
1278
|
+
text7,
|
|
1279
1279
|
MARKDOWN_MARKERS,
|
|
1280
1280
|
TEMPLATES[lang].agentsBlock({
|
|
1281
1281
|
gate: cliCommand(runner, "gate --fast --json"),
|
|
1282
1282
|
doctor: cliCommand(runner, "doctor --json")
|
|
1283
1283
|
})
|
|
1284
1284
|
);
|
|
1285
|
-
var withAgentsImport = (
|
|
1286
|
-
var withoutMarkdownBlock = (
|
|
1285
|
+
var withAgentsImport = (text7) => text7 !== void 0 && IMPORT_LINE.test(withoutManagedBlock(text7, MARKDOWN_MARKERS)) ? text7 : withManagedBlock(text7, MARKDOWN_MARKERS, [AGENTS_IMPORT]);
|
|
1286
|
+
var withoutMarkdownBlock = (text7) => withoutManagedBlock(text7, MARKDOWN_MARKERS);
|
|
1287
1287
|
|
|
1288
1288
|
// src/install/ask-floor.ts
|
|
1289
1289
|
var DEFAULT_BRANCHES = ["main", "master"];
|
|
@@ -1357,9 +1357,9 @@ var ruleFor = (raw) => {
|
|
|
1357
1357
|
const path = line2.endsWith("/") ? `${body2}/**` : body2;
|
|
1358
1358
|
return `Read(${anchored || body2.includes("/") ? "./" : ""}${path})`;
|
|
1359
1359
|
};
|
|
1360
|
-
var claudeignoreRules = (
|
|
1360
|
+
var claudeignoreRules = (texts2) => [
|
|
1361
1361
|
...new Set(
|
|
1362
|
-
|
|
1362
|
+
texts2.flatMap((text7) => (text7 ?? "").split("\n")).map(ruleFor).filter((rule) => rule !== void 0)
|
|
1363
1363
|
)
|
|
1364
1364
|
];
|
|
1365
1365
|
|
|
@@ -1392,12 +1392,12 @@ import { join as join4 } from "node:path";
|
|
|
1392
1392
|
|
|
1393
1393
|
// src/install/json-text.ts
|
|
1394
1394
|
var DEFAULT_STYLE = { indent: " ", finalNewline: true };
|
|
1395
|
-
var styleOf = (
|
|
1396
|
-
const indented = /\n([ \t]+)\S/.exec(
|
|
1397
|
-
return { indent: indented?.[1] ?? DEFAULT_STYLE.indent, finalNewline:
|
|
1395
|
+
var styleOf = (text7) => {
|
|
1396
|
+
const indented = /\n([ \t]+)\S/.exec(text7);
|
|
1397
|
+
return { indent: indented?.[1] ?? DEFAULT_STYLE.indent, finalNewline: text7.endsWith("\n") };
|
|
1398
1398
|
};
|
|
1399
1399
|
var renderJson = (value, style) => `${JSON.stringify(value, null, style.indent)}${style.finalNewline ? "\n" : ""}`;
|
|
1400
|
-
var parseSettings = (
|
|
1400
|
+
var parseSettings = (text7) => text7 === void 0 || text7.trim() === "" ? { ok: true, value: {} } : parseJsonObject(text7);
|
|
1401
1401
|
var asList = (value) => Array.isArray(value) ? value : [];
|
|
1402
1402
|
var asRecord = (value) => isRecord(value) ? value : {};
|
|
1403
1403
|
var setKey = (record3, key, value) => {
|
|
@@ -1434,8 +1434,8 @@ var WORKFLOW_DIRS = [
|
|
|
1434
1434
|
{ dir: "openspec", name: "openspec" }
|
|
1435
1435
|
];
|
|
1436
1436
|
var fromPackageJson = (project) => {
|
|
1437
|
-
const
|
|
1438
|
-
const parsed =
|
|
1437
|
+
const text7 = project.read("package.json");
|
|
1438
|
+
const parsed = text7 === void 0 ? void 0 : parseJsonObject(text7);
|
|
1439
1439
|
const field = parsed?.ok === true ? parsed.value["packageManager"] : void 0;
|
|
1440
1440
|
if (typeof field !== "string") return void 0;
|
|
1441
1441
|
return PACKAGE_MANAGERS.find(({ name }) => field.startsWith(`${name}@`))?.name;
|
|
@@ -1451,9 +1451,9 @@ var allHandlers = (settings) => {
|
|
|
1451
1451
|
const hooks = settings["hooks"];
|
|
1452
1452
|
return isRecord(hooks) ? Object.keys(hooks).flatMap((event) => handlersOf(settings, event)) : [];
|
|
1453
1453
|
};
|
|
1454
|
-
var settingsInfo = (
|
|
1455
|
-
if (
|
|
1456
|
-
const parsed =
|
|
1454
|
+
var settingsInfo = (text7) => {
|
|
1455
|
+
if (text7 === void 0) return { exists: false, valid: true, userHooks: 0, ownHooks: 0 };
|
|
1456
|
+
const parsed = text7.trim() === "" ? { ok: true, value: {} } : parseJsonObject(text7);
|
|
1457
1457
|
if (!parsed.ok) return { exists: true, valid: false, userHooks: 0, ownHooks: 0 };
|
|
1458
1458
|
const handlers = allHandlers(parsed.value);
|
|
1459
1459
|
const own = handlers.filter(isOwnHandler).length;
|
|
@@ -1467,8 +1467,8 @@ var pluginAuthorities = (settings, source) => {
|
|
|
1467
1467
|
return LOOP_PLUGINS.includes(name) ? [{ kind: "loop-plugin", source, name }] : [];
|
|
1468
1468
|
});
|
|
1469
1469
|
};
|
|
1470
|
-
var scopeAuthorities = (
|
|
1471
|
-
const parsed =
|
|
1470
|
+
var scopeAuthorities = (text7, source) => {
|
|
1471
|
+
const parsed = text7 === void 0 ? void 0 : parseJsonObject(text7);
|
|
1472
1472
|
if (parsed?.ok !== true) return [];
|
|
1473
1473
|
const stops = ["Stop", "SubagentStop"].flatMap(
|
|
1474
1474
|
(event) => handlersOf(parsed.value, event).filter((handler) => !isOwnHandler(handler)).map((handler) => {
|
|
@@ -1482,8 +1482,8 @@ var userSettingsText = (project, env) => {
|
|
|
1482
1482
|
const home = env["CLAUDE_CONFIG_DIR"] ?? env["HOME"] ?? env["USERPROFILE"];
|
|
1483
1483
|
if (home === void 0) return void 0;
|
|
1484
1484
|
const file = env["CLAUDE_CONFIG_DIR"] === void 0 ? join4(home, ".claude", "settings.json") : join4(home, "settings.json");
|
|
1485
|
-
const
|
|
1486
|
-
return
|
|
1485
|
+
const text7 = project.runtime.readText(toAbsolutePath(file));
|
|
1486
|
+
return text7.ok ? text7.value : void 0;
|
|
1487
1487
|
};
|
|
1488
1488
|
var detectAuthorities = (project, env) => [
|
|
1489
1489
|
...scopeAuthorities(project.read(SETTINGS_FILE), "project"),
|
|
@@ -1503,8 +1503,8 @@ var chooseRunner = (project, bin) => {
|
|
|
1503
1503
|
// src/install/gitignore-block.ts
|
|
1504
1504
|
var MARKERS = { begin: BLOCK_BEGIN, end: BLOCK_END };
|
|
1505
1505
|
var body = () => [`${STATE_DIR}/*`, ...COMMITTED_STATE.map((path) => `!${STATE_DIR}/${path}`)];
|
|
1506
|
-
var withBlock = (
|
|
1507
|
-
var withoutBlock = (
|
|
1506
|
+
var withBlock = (text7) => withManagedBlock(text7, MARKERS, body());
|
|
1507
|
+
var withoutBlock = (text7) => withoutManagedBlock(text7, MARKERS);
|
|
1508
1508
|
|
|
1509
1509
|
// src/core/semver.ts
|
|
1510
1510
|
var SEMVER = /^v?(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
@@ -1547,7 +1547,7 @@ var pendingMigrations = (migrations, from, to) => {
|
|
|
1547
1547
|
};
|
|
1548
1548
|
var migrateItem = (item2, previous, steps) => {
|
|
1549
1549
|
if (steps.length === 0 || item2.before === void 0) return item2;
|
|
1550
|
-
const migrated = steps.reduce((
|
|
1550
|
+
const migrated = steps.reduce((text7, step3) => step3(text7), item2.after);
|
|
1551
1551
|
if (migrated === item2.after) return item2;
|
|
1552
1552
|
const recorded = previous.entries.find((entry2) => entry2.path === item2.path)?.hash;
|
|
1553
1553
|
if (recorded !== hashText(item2.before)) {
|
|
@@ -1688,9 +1688,9 @@ var unmergeSettings = (current2, added, recorded) => {
|
|
|
1688
1688
|
var ownHandlersIn = (settings) => Object.values(asRecord(settings["hooks"])).flatMap((groups) => asList(groups)).flatMap((group) => asList(asRecord(group)["hooks"])).filter(isOwnHandler).length;
|
|
1689
1689
|
|
|
1690
1690
|
// src/install/vendor.ts
|
|
1691
|
-
var isBundle = (
|
|
1692
|
-
var isOwnPackage = (
|
|
1693
|
-
const parsed = parseJsonObject(
|
|
1691
|
+
var isBundle = (text7) => text7.startsWith("#!/usr/bin/env node") && text7.includes("finitemonkey");
|
|
1692
|
+
var isOwnPackage = (text7) => {
|
|
1693
|
+
const parsed = parseJsonObject(text7);
|
|
1694
1694
|
return parsed.ok && parsed.value["name"] === "finitemonkey";
|
|
1695
1695
|
};
|
|
1696
1696
|
var vendoredPackage = (version) => `${JSON.stringify({ name: "finitemonkey", version, private: true, type: "module" }, null, 2)}
|
|
@@ -1721,9 +1721,9 @@ var detect = (project, gitRoot2, input) => ({
|
|
|
1721
1721
|
location: locationOf(project.root, input.env)
|
|
1722
1722
|
});
|
|
1723
1723
|
var readPreviousLock = (project) => {
|
|
1724
|
-
const
|
|
1725
|
-
if (
|
|
1726
|
-
const parsed = parseLock(
|
|
1724
|
+
const text7 = project.read(LOCK_FILE);
|
|
1725
|
+
if (text7 === void 0) return accept(void 0);
|
|
1726
|
+
const parsed = parseLock(text7);
|
|
1727
1727
|
return parsed.ok ? accept(parsed.value) : reject({ kind: "lock", detail: parsed.error });
|
|
1728
1728
|
};
|
|
1729
1729
|
var actionFor = (before, after, existing) => {
|
|
@@ -1770,7 +1770,7 @@ var settingsItem = (project, detection, input, previous) => {
|
|
|
1770
1770
|
});
|
|
1771
1771
|
};
|
|
1772
1772
|
var markdownItems = (project, runner, lang) => [
|
|
1773
|
-
{ path: AGENTS_FILE, render: (
|
|
1773
|
+
{ path: AGENTS_FILE, render: (text7) => withAgentsBlock(text7, runner, lang) },
|
|
1774
1774
|
{ path: CLAUDE_FILE, render: withAgentsImport }
|
|
1775
1775
|
].map(({ path, render: render3 }) => {
|
|
1776
1776
|
const before = project.read(path);
|
|
@@ -1836,11 +1836,11 @@ var needsWrite = (plan) => hasChanges(plan) || plan.previous === void 0;
|
|
|
1836
1836
|
|
|
1837
1837
|
// src/install/apply.ts
|
|
1838
1838
|
var stamp = (iso) => iso.replace(/[:.]/g, "-");
|
|
1839
|
-
var ensureParent = (project,
|
|
1840
|
-
var writeText = (project,
|
|
1841
|
-
const parent = ensureParent(project,
|
|
1842
|
-
const written = parent.ok ? project.runtime.writeAtomic(project.abs(
|
|
1843
|
-
return written.ok ? accept(void 0) : reject({ path:
|
|
1839
|
+
var ensureParent = (project, relative11) => project.runtime.mkdir(project.abs(dirname3(relative11)));
|
|
1840
|
+
var writeText = (project, relative11, text7) => {
|
|
1841
|
+
const parent = ensureParent(project, relative11);
|
|
1842
|
+
const written = parent.ok ? project.runtime.writeAtomic(project.abs(relative11), text7) : parent;
|
|
1843
|
+
return written.ok ? accept(void 0) : reject({ path: relative11, detail: written.error });
|
|
1844
1844
|
};
|
|
1845
1845
|
var restore = (project, items) => {
|
|
1846
1846
|
for (const item2 of items) {
|
|
@@ -1981,17 +1981,17 @@ var detectLang = (flag2, env, settings = {}) => {
|
|
|
1981
1981
|
// src/install/project.ts
|
|
1982
1982
|
import { dirname as dirname4, join as join5 } from "node:path";
|
|
1983
1983
|
var projectAt = (root, runtime) => {
|
|
1984
|
-
const abs = (
|
|
1984
|
+
const abs = (relative11) => toAbsolutePath(join5(root, ...relative11.split("/")));
|
|
1985
1985
|
return {
|
|
1986
1986
|
root,
|
|
1987
1987
|
runtime,
|
|
1988
1988
|
abs,
|
|
1989
|
-
read: (
|
|
1990
|
-
const
|
|
1991
|
-
return
|
|
1989
|
+
read: (relative11) => {
|
|
1990
|
+
const text7 = runtime.readText(abs(relative11));
|
|
1991
|
+
return text7.ok ? text7.value : void 0;
|
|
1992
1992
|
},
|
|
1993
|
-
exists: (
|
|
1994
|
-
isDir: (
|
|
1993
|
+
exists: (relative11) => runtime.stat(abs(relative11)) !== void 0,
|
|
1994
|
+
isDir: (relative11) => runtime.stat(abs(relative11))?.kind === "dir"
|
|
1995
1995
|
};
|
|
1996
1996
|
};
|
|
1997
1997
|
var findGitRoot = (start, runtime) => {
|
|
@@ -2061,8 +2061,8 @@ var invalidPreset = (raw) => problem("invalid_arguments", "error.invalid_preset"
|
|
|
2061
2061
|
});
|
|
2062
2062
|
var selfBundle = (ctx) => {
|
|
2063
2063
|
if (!isBundlePath(ctx.runtime.selfPath)) return void 0;
|
|
2064
|
-
const
|
|
2065
|
-
return
|
|
2064
|
+
const text7 = ctx.runtime.readText(ctx.runtime.selfPath);
|
|
2065
|
+
return text7.ok ? text7.value : void 0;
|
|
2066
2066
|
};
|
|
2067
2067
|
var INHERITED = /* @__PURE__ */ new Set(["default", "config", "claude-settings"]);
|
|
2068
2068
|
var planInputFor = (ctx, preset, bin, marketplace) => ({
|
|
@@ -2166,13 +2166,13 @@ var isPlaceholder = (value) => {
|
|
|
2166
2166
|
var isTestOrLockPath = (path) => path !== void 0 && (LOCKFILES.test(toPosixPath(path)) || TEST_PATH.test(toPosixPath(path)));
|
|
2167
2167
|
|
|
2168
2168
|
// src/secrets/entropy.ts
|
|
2169
|
-
var shannonEntropy = (
|
|
2170
|
-
if (
|
|
2169
|
+
var shannonEntropy = (text7) => {
|
|
2170
|
+
if (text7.length === 0) return 0;
|
|
2171
2171
|
const counts = /* @__PURE__ */ new Map();
|
|
2172
|
-
for (const char of
|
|
2172
|
+
for (const char of text7) counts.set(char, (counts.get(char) ?? 0) + 1);
|
|
2173
2173
|
let entropy = 0;
|
|
2174
2174
|
for (const count4 of counts.values()) {
|
|
2175
|
-
const p = count4 /
|
|
2175
|
+
const p = count4 / text7.length;
|
|
2176
2176
|
entropy -= p * Math.log2(p);
|
|
2177
2177
|
}
|
|
2178
2178
|
return entropy;
|
|
@@ -2180,7 +2180,7 @@ var shannonEntropy = (text6) => {
|
|
|
2180
2180
|
|
|
2181
2181
|
// src/secrets/fingerprint.ts
|
|
2182
2182
|
import { createHash as createHash2 } from "node:crypto";
|
|
2183
|
-
var sha256Hex = (
|
|
2183
|
+
var sha256Hex = (text7) => createHash2("sha256").update(text7).digest("hex");
|
|
2184
2184
|
var fingerprintOf = (secret) => sha256Hex(`finitemonkey:secret:${secret}`).slice(0, 16);
|
|
2185
2185
|
var ALNUM = /[A-Za-z0-9]/g;
|
|
2186
2186
|
var MIN_TAIL_LENGTH = 12;
|
|
@@ -2190,9 +2190,9 @@ var tailOf = (secret) => {
|
|
|
2190
2190
|
};
|
|
2191
2191
|
|
|
2192
2192
|
// src/secrets/lines.ts
|
|
2193
|
-
var lineLocator = (
|
|
2193
|
+
var lineLocator = (text7) => {
|
|
2194
2194
|
const starts = [0];
|
|
2195
|
-
for (let index =
|
|
2195
|
+
for (let index = text7.indexOf("\n"); index !== -1; index = text7.indexOf("\n", index + 1)) starts.push(index + 1);
|
|
2196
2196
|
return (offset) => {
|
|
2197
2197
|
let low = 0;
|
|
2198
2198
|
let high = starts.length - 1;
|
|
@@ -2221,8 +2221,8 @@ var defineRule = (spec2) => ({
|
|
|
2221
2221
|
skipTestPaths: spec2.skipTestPaths ?? false,
|
|
2222
2222
|
placeholders: spec2.placeholders ?? true
|
|
2223
2223
|
});
|
|
2224
|
-
var isWarning = (
|
|
2225
|
-
var blocking = (findings) => findings.filter((
|
|
2224
|
+
var isWarning = (finding4) => finding4.severity === "warning";
|
|
2225
|
+
var blocking = (findings) => findings.filter((finding4) => !isWarning(finding4));
|
|
2226
2226
|
|
|
2227
2227
|
// src/secrets/rules/exposure.ts
|
|
2228
2228
|
var ARGUMENT_CONTEXT = String.raw`(?:^|\n)[ \t-]*(?:command|entrypoint|CMD|ENTRYPOINT|RUN|ExecStart(?:Pre|Post)?|ExecReload)\b[^\n]*?`;
|
|
@@ -2486,12 +2486,12 @@ var hasRandomRun = (secret, length) => (secret.match(new RegExp(`[A-Za-z0-9]{${S
|
|
|
2486
2486
|
);
|
|
2487
2487
|
var accepted = (rule, secret) => secret.length >= rule.minLength && (!rule.requireDigit || DIGIT.test(secret)) && (rule.minRandomRun === 0 || hasRandomRun(secret, rule.minRandomRun)) && (!rule.placeholders || !isPlaceholder(secret)) && (rule.minEntropy === 0 || shannonEntropy(secret) >= rule.minEntropy);
|
|
2488
2488
|
var NAME_CHAR = /[A-Za-z0-9_.-]/;
|
|
2489
|
-
var nameAround = (
|
|
2489
|
+
var nameAround = (text7, at) => {
|
|
2490
2490
|
let start = at;
|
|
2491
|
-
while (start > 0 && NAME_CHAR.test(
|
|
2491
|
+
while (start > 0 && NAME_CHAR.test(text7.charAt(start - 1))) start -= 1;
|
|
2492
2492
|
let end = at;
|
|
2493
|
-
while (end <
|
|
2494
|
-
return
|
|
2493
|
+
while (end < text7.length && NAME_CHAR.test(text7.charAt(end))) end += 1;
|
|
2494
|
+
return text7.slice(start, end).toLowerCase();
|
|
2495
2495
|
};
|
|
2496
2496
|
var ignoredName = (rule, match) => rule.ignoredNames.length > 0 && rule.ignoredNames.includes(nameAround(match.input, match.index));
|
|
2497
2497
|
var spanOf = (rule, match) => {
|
|
@@ -2501,9 +2501,9 @@ var spanOf = (rule, match) => {
|
|
|
2501
2501
|
const secret = match.input.slice(start, end);
|
|
2502
2502
|
return accepted(rule, secret) ? { rule, start, end, secret } : void 0;
|
|
2503
2503
|
};
|
|
2504
|
-
var ruleSpans = (rule,
|
|
2504
|
+
var ruleSpans = (rule, text7) => {
|
|
2505
2505
|
const spans = [];
|
|
2506
|
-
for (const match of
|
|
2506
|
+
for (const match of text7.matchAll(rule.pattern)) {
|
|
2507
2507
|
const span = spanOf(rule, match);
|
|
2508
2508
|
if (span !== void 0) spans.push(span);
|
|
2509
2509
|
}
|
|
@@ -2529,31 +2529,31 @@ var toFinding = (span, locate2) => {
|
|
|
2529
2529
|
tail: tailOf(span.secret)
|
|
2530
2530
|
};
|
|
2531
2531
|
};
|
|
2532
|
-
var scanText = (
|
|
2533
|
-
if (
|
|
2534
|
-
const rules = applicable(options.rules ?? RULES,
|
|
2532
|
+
var scanText = (text7, options = {}) => {
|
|
2533
|
+
if (text7.length === 0) return [];
|
|
2534
|
+
const rules = applicable(options.rules ?? RULES, text7.toLowerCase(), options.path);
|
|
2535
2535
|
if (rules.length === 0) return [];
|
|
2536
2536
|
const taken = [];
|
|
2537
2537
|
for (const rule of rules) {
|
|
2538
|
-
for (const span of ruleSpans(rule,
|
|
2538
|
+
for (const span of ruleSpans(rule, text7)) if (!overlaps(taken, span)) taken.push(span);
|
|
2539
2539
|
}
|
|
2540
2540
|
if (taken.length === 0) return [];
|
|
2541
|
-
const locate2 = lineLocator(
|
|
2541
|
+
const locate2 = lineLocator(text7);
|
|
2542
2542
|
return taken.sort((a, b) => a.start - b.start).map((span) => toFinding(span, locate2));
|
|
2543
2543
|
};
|
|
2544
2544
|
|
|
2545
2545
|
// src/mask/mask.ts
|
|
2546
|
-
var maskToken = (
|
|
2547
|
-
var maskText = (
|
|
2548
|
-
const findings = scanText(
|
|
2549
|
-
if (findings.length === 0) return
|
|
2546
|
+
var maskToken = (finding4) => `«secret:${finding4.ruleId}:${finding4.tail}»`;
|
|
2547
|
+
var maskText = (text7) => {
|
|
2548
|
+
const findings = scanText(text7, { rules: CREDENTIAL_RULES });
|
|
2549
|
+
if (findings.length === 0) return text7;
|
|
2550
2550
|
let out = "";
|
|
2551
2551
|
let cursor = 0;
|
|
2552
|
-
for (const
|
|
2553
|
-
out +=
|
|
2554
|
-
cursor =
|
|
2552
|
+
for (const finding4 of findings) {
|
|
2553
|
+
out += text7.slice(cursor, finding4.start) + maskToken(finding4);
|
|
2554
|
+
cursor = finding4.end;
|
|
2555
2555
|
}
|
|
2556
|
-
return out +
|
|
2556
|
+
return out + text7.slice(cursor);
|
|
2557
2557
|
};
|
|
2558
2558
|
var maskDeep = (value) => {
|
|
2559
2559
|
if (typeof value === "string") return maskText(value);
|
|
@@ -2771,14 +2771,14 @@ var toolVersion = (env, command) => {
|
|
|
2771
2771
|
return result.ok ? result.stdout.trim().split("\n")[0] ?? "" : "not found";
|
|
2772
2772
|
};
|
|
2773
2773
|
var lastHookFailures = (env) => {
|
|
2774
|
-
const
|
|
2775
|
-
if (!
|
|
2776
|
-
return
|
|
2774
|
+
const text7 = env.runtime.readText(fmPaths(env.root).log);
|
|
2775
|
+
if (!text7.ok) return [];
|
|
2776
|
+
return text7.value.split("\n").filter((line2) => {
|
|
2777
2777
|
const parsed = line2.trim() === "" ? void 0 : parseJsonObject(line2);
|
|
2778
2778
|
return parsed?.ok === true && isRecord(parsed.value) && parsed.value["event"] === HOOK_FAILURE_EVENT;
|
|
2779
2779
|
}).slice(-MAX_FAILURES);
|
|
2780
2780
|
};
|
|
2781
|
-
var withoutHome = (
|
|
2781
|
+
var withoutHome = (text7, home) => home.length > 1 ? text7.split(home).join("~") : text7;
|
|
2782
2782
|
var reportMarkdown = (env, ctx, envelope) => {
|
|
2783
2783
|
const failures = lastHookFailures(env);
|
|
2784
2784
|
const lines3 = [
|
|
@@ -2814,9 +2814,9 @@ var YIELD_STDOUT = `${JSON.stringify({ continue: true })}
|
|
|
2814
2814
|
`;
|
|
2815
2815
|
var projectOwnsHooks = (ctx) => {
|
|
2816
2816
|
const root = ctx.env["CLAUDE_PROJECT_DIR"] ?? ctx.cwd;
|
|
2817
|
-
const
|
|
2818
|
-
if (!
|
|
2819
|
-
const parsed = parseJsonObject(
|
|
2817
|
+
const text7 = ctx.runtime.readText(toAbsolutePath(join8(root, ...SETTINGS_FILE.split("/"))));
|
|
2818
|
+
if (!text7.ok) return false;
|
|
2819
|
+
const parsed = parseJsonObject(text7.value);
|
|
2820
2820
|
return parsed.ok && ownHandlersIn(parsed.value) > 0;
|
|
2821
2821
|
};
|
|
2822
2822
|
|
|
@@ -2850,8 +2850,8 @@ var settingsPaths = (env) => {
|
|
|
2850
2850
|
];
|
|
2851
2851
|
};
|
|
2852
2852
|
var readSettings = (env) => settingsPaths(env).flatMap(({ scope: scope2, path }) => {
|
|
2853
|
-
const
|
|
2854
|
-
const parsed =
|
|
2853
|
+
const text7 = env.runtime.readText(path);
|
|
2854
|
+
const parsed = text7.ok ? parseJsonObject(text7.value) : void 0;
|
|
2855
2855
|
return parsed?.ok === true ? [{ scope: scope2, path, settings: parsed.value }] : [];
|
|
2856
2856
|
});
|
|
2857
2857
|
var scopesWith = (all, test) => all.filter(({ settings }) => test(settings)).map(({ scope: scope2 }) => scope2);
|
|
@@ -3062,7 +3062,7 @@ var checkAuthVariables = ({ env, platform }) => {
|
|
|
3062
3062
|
};
|
|
3063
3063
|
|
|
3064
3064
|
// src/doctor/hooks.ts
|
|
3065
|
-
import { join as
|
|
3065
|
+
import { join as join34 } from "node:path";
|
|
3066
3066
|
|
|
3067
3067
|
// src/hooks/events.ts
|
|
3068
3068
|
var HOOK_EVENTS = [
|
|
@@ -3137,7 +3137,7 @@ var editsOf = (input) => {
|
|
|
3137
3137
|
});
|
|
3138
3138
|
};
|
|
3139
3139
|
var applyEdits = (before, edits) => edits.reduce(
|
|
3140
|
-
(
|
|
3140
|
+
(text7, edit) => edit.all ? text7.split(edit.oldText).join(edit.newText) : text7.replace(edit.oldText, () => edit.newText),
|
|
3141
3141
|
before
|
|
3142
3142
|
);
|
|
3143
3143
|
|
|
@@ -3154,9 +3154,9 @@ var textRecord = (value) => isRecord(value) ? Object.fromEntries(
|
|
|
3154
3154
|
Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
|
|
3155
3155
|
) : {};
|
|
3156
3156
|
var readJsonFile = (runtime, path) => {
|
|
3157
|
-
const
|
|
3158
|
-
if (!
|
|
3159
|
-
const parsed = parseJsonObject(
|
|
3157
|
+
const text7 = runtime.readText(path);
|
|
3158
|
+
if (!text7.ok) return void 0;
|
|
3159
|
+
const parsed = parseJsonObject(text7.value);
|
|
3160
3160
|
return parsed.ok ? parsed.value : void 0;
|
|
3161
3161
|
};
|
|
3162
3162
|
var writeJsonFile = (runtime, path, value) => runtime.writeText(path, `${JSON.stringify({ schemaVersion: 1, ...value }, null, 2)}
|
|
@@ -3268,7 +3268,7 @@ var SHELL_TOOLS = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
|
|
|
3268
3268
|
import { posix } from "node:path";
|
|
3269
3269
|
|
|
3270
3270
|
// src/shell/scan.ts
|
|
3271
|
-
var cursorOf = (
|
|
3271
|
+
var cursorOf = (text7) => ({ text: text7, pos: 0, incomplete: false, subs: [] });
|
|
3272
3272
|
var peek = (cursor, offset = 0) => cursor.text.charAt(cursor.pos + offset);
|
|
3273
3273
|
var atEnd = (cursor) => cursor.pos >= cursor.text.length;
|
|
3274
3274
|
var skipQuoted = (cursor, quote) => {
|
|
@@ -3483,8 +3483,8 @@ var nextToken = (cursor, tokens, pending) => {
|
|
|
3483
3483
|
tokens.push({ kind: "op", text: op });
|
|
3484
3484
|
if (op === "\n") flushHeredocs(cursor, tokens, pending);
|
|
3485
3485
|
};
|
|
3486
|
-
var lexBash = (
|
|
3487
|
-
const cursor = cursorOf(
|
|
3486
|
+
var lexBash = (text7) => {
|
|
3487
|
+
const cursor = cursorOf(text7.replace(/\r\n/g, "\n"));
|
|
3488
3488
|
const tokens = [];
|
|
3489
3489
|
const pending = [];
|
|
3490
3490
|
skipBlank(cursor);
|
|
@@ -3632,9 +3632,9 @@ var next = (cursor, tokens, cmd) => {
|
|
|
3632
3632
|
}
|
|
3633
3633
|
const op = OPERATORS2.find((candidate) => cursor.text.startsWith(candidate, cursor.pos));
|
|
3634
3634
|
if (op !== void 0 || cmd && peek(cursor) === "&") {
|
|
3635
|
-
const
|
|
3636
|
-
cursor.pos +=
|
|
3637
|
-
tokens.push({ kind: "op", text:
|
|
3635
|
+
const text7 = op ?? "&";
|
|
3636
|
+
cursor.pos += text7.length;
|
|
3637
|
+
tokens.push({ kind: "op", text: text7 });
|
|
3638
3638
|
return;
|
|
3639
3639
|
}
|
|
3640
3640
|
if ((peek(cursor) === "&" || peek(cursor) === ".") && peek(cursor, 1) === " " && commandStart(tokens)) {
|
|
@@ -3643,8 +3643,8 @@ var next = (cursor, tokens, cmd) => {
|
|
|
3643
3643
|
}
|
|
3644
3644
|
tokens.push(word(cursor, cmd));
|
|
3645
3645
|
};
|
|
3646
|
-
var lexPowerShell = (
|
|
3647
|
-
const cursor = cursorOf(
|
|
3646
|
+
var lexPowerShell = (text7, cmd = false) => {
|
|
3647
|
+
const cursor = cursorOf(text7.replace(/\r\n/g, "\n"));
|
|
3648
3648
|
const tokens = [];
|
|
3649
3649
|
const skip = () => {
|
|
3650
3650
|
while (peek(cursor) === " " || peek(cursor) === " ") cursor.pos += 1;
|
|
@@ -3847,7 +3847,7 @@ var MAX_DEPTH = 8;
|
|
|
3847
3847
|
var ASSIGNMENT2 = /^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/s;
|
|
3848
3848
|
var PS_ENV = /^\$env:([A-Za-z_][A-Za-z0-9_]*)=?(.*)$/is;
|
|
3849
3849
|
var CHANGES_DIR = /* @__PURE__ */ new Set(["cd", "pushd", "set-location", "push-location"]);
|
|
3850
|
-
var lex = (
|
|
3850
|
+
var lex = (text7, language) => language === "bash" ? lexBash(text7) : lexPowerShell(text7, language === "cmd");
|
|
3851
3851
|
var isWindowsAbsolute = (path) => /^[A-Za-z]:\//.test(path);
|
|
3852
3852
|
var joinDir = (current2, target2) => {
|
|
3853
3853
|
const next2 = toPosixPath(target2);
|
|
@@ -3918,21 +3918,21 @@ var visit = (raw, language, context, depth, collector) => {
|
|
|
3918
3918
|
emit(result.stage, raw, language, context, collector);
|
|
3919
3919
|
for (const extra of result.extra) emit(extra, raw, language, context, collector);
|
|
3920
3920
|
};
|
|
3921
|
-
var collect = (
|
|
3921
|
+
var collect = (text7, language, context, depth, collector) => {
|
|
3922
3922
|
if (depth > MAX_DEPTH) {
|
|
3923
3923
|
collector.incomplete = true;
|
|
3924
3924
|
return;
|
|
3925
3925
|
}
|
|
3926
|
-
const lexed = lex(
|
|
3926
|
+
const lexed = lex(text7, language);
|
|
3927
3927
|
if (lexed.incomplete) collector.incomplete = true;
|
|
3928
3928
|
for (const substitution of lexed.substitutions) {
|
|
3929
3929
|
collect(substitution, language, { ...context }, depth + 1, collector);
|
|
3930
3930
|
}
|
|
3931
3931
|
for (const raw of rawCommands(lexed.tokens)) visit(raw, language, context, depth, collector);
|
|
3932
3932
|
};
|
|
3933
|
-
var normalizeCommand = (
|
|
3933
|
+
var normalizeCommand = (text7, dialect = "bash") => {
|
|
3934
3934
|
const collector = { out: [], incomplete: false };
|
|
3935
|
-
collect(
|
|
3935
|
+
collect(text7, dialect, { env: {}, dir: void 0, via: [] }, 0, collector);
|
|
3936
3936
|
return { commands: collector.out, incomplete: collector.incomplete };
|
|
3937
3937
|
};
|
|
3938
3938
|
var dialectOf = (toolName3) => toolName3 === "PowerShell" ? "powershell" : "bash";
|
|
@@ -3944,7 +3944,7 @@ var segmentsOf = (command) => normalizeCommand(command).commands.map((item2) =>
|
|
|
3944
3944
|
|
|
3945
3945
|
// src/guards/shell.ts
|
|
3946
3946
|
var HEREDOC = /^<<[-<]?$/;
|
|
3947
|
-
var parseShell = (
|
|
3947
|
+
var parseShell = (text7) => normalizeCommand(text7).commands.map((command) => ({
|
|
3948
3948
|
env: command.env,
|
|
3949
3949
|
argv: command.program === "" ? [] : [command.program, ...command.args],
|
|
3950
3950
|
redirects: command.redirects.filter((redirect) => !HEREDOC.test(redirect.op)).map((redirect) => redirect.target)
|
|
@@ -4097,9 +4097,9 @@ var writtenPaths = (command) => {
|
|
|
4097
4097
|
if (name === "git" && GIT_WRITERS.has(operands2[0] ?? "")) return [...command.redirects, ...operands2.slice(1)];
|
|
4098
4098
|
return command.redirects;
|
|
4099
4099
|
};
|
|
4100
|
-
var mentioned = (
|
|
4101
|
-
if (/\.finitemonkey[\\/]allow/.test(
|
|
4102
|
-
if (/\.finitemonkey|\.gitleaksignore/.test(
|
|
4100
|
+
var mentioned = (text7) => {
|
|
4101
|
+
if (/\.finitemonkey[\\/]allow/.test(text7)) return "allow_store";
|
|
4102
|
+
if (/\.finitemonkey|\.gitleaksignore/.test(text7)) return "policy";
|
|
4103
4103
|
return void 0;
|
|
4104
4104
|
};
|
|
4105
4105
|
var bashSelfTargets = (root, commandLine) => parseShell(commandLine).flatMap(
|
|
@@ -4300,8 +4300,8 @@ var compareConfig = (path, before, after, accepted2) => {
|
|
|
4300
4300
|
// src/guards/tests/hook.ts
|
|
4301
4301
|
var localPath = (root, path) => toPosixPath(relative3(root, toAbsolutePath(path, root)));
|
|
4302
4302
|
var read = (input, path) => {
|
|
4303
|
-
const
|
|
4304
|
-
return
|
|
4303
|
+
const text7 = input.runtime.readText(path);
|
|
4304
|
+
return text7.ok ? text7.value : void 0;
|
|
4305
4305
|
};
|
|
4306
4306
|
var proposed = (tool, args2, before) => {
|
|
4307
4307
|
if (tool === "Write") return textField(args2, "content") ?? "";
|
|
@@ -4345,6 +4345,9 @@ var testsGuard = (input) => {
|
|
|
4345
4345
|
return deny(t("gate.guard.tests.model", { detail: reason }), t("gate.guard.tests.human", { detail: reason }));
|
|
4346
4346
|
};
|
|
4347
4347
|
|
|
4348
|
+
// src/look/reads.ts
|
|
4349
|
+
import { relative as relative4 } from "node:path";
|
|
4350
|
+
|
|
4348
4351
|
// src/gate/hooks/common.ts
|
|
4349
4352
|
import { dirname as dirname5, join as join12 } from "node:path";
|
|
4350
4353
|
var DEFAULT_GATE_BUDGET_MS = 3e5;
|
|
@@ -4373,7 +4376,32 @@ var contextOutput = (event, additionalContext, systemMessage) => ({
|
|
|
4373
4376
|
hookSpecificOutput: { hookEventName: event, additionalContext },
|
|
4374
4377
|
...systemMessage === void 0 ? {} : { systemMessage }
|
|
4375
4378
|
});
|
|
4376
|
-
var human = (
|
|
4379
|
+
var human = (text7) => `finitemonkey: ${text7}`;
|
|
4380
|
+
|
|
4381
|
+
// src/look/reads.ts
|
|
4382
|
+
var READS_FILE = "look-reads.json";
|
|
4383
|
+
var KEEP = 500;
|
|
4384
|
+
var readLookReads = (runtime, paths) => {
|
|
4385
|
+
const raw = readJsonFile(runtime, paths.cache(READS_FILE))?.["reads"];
|
|
4386
|
+
return isRecord(raw) ? Object.fromEntries(Object.entries(raw).filter((entry2) => typeof entry2[1] === "string")) : {};
|
|
4387
|
+
};
|
|
4388
|
+
var recordLookRead = (runtime, paths, path) => {
|
|
4389
|
+
const entries = Object.entries({ ...readLookReads(runtime, paths), [path]: runtime.now() }).slice(-KEEP);
|
|
4390
|
+
return writeJsonFile(runtime, paths.cache(READS_FILE), { reads: Object.fromEntries(entries) }).ok;
|
|
4391
|
+
};
|
|
4392
|
+
var lookCaptureOf = (paths, file) => {
|
|
4393
|
+
const relativePath = toPosixPath(relative4(paths.root, toAbsolutePath(file, paths.root)));
|
|
4394
|
+
return relativePath.startsWith(".finitemonkey/look/") && relativePath.endsWith(".png") ? relativePath : void 0;
|
|
4395
|
+
};
|
|
4396
|
+
var lookReadTracker = (input) => {
|
|
4397
|
+
if (toolName(input.payload.raw) !== "Read") return { kind: "allow" };
|
|
4398
|
+
const file = textField(toolInput(input.payload.raw), "file_path");
|
|
4399
|
+
const paths = projectPaths(input);
|
|
4400
|
+
if (file === void 0 || paths === void 0) return { kind: "allow" };
|
|
4401
|
+
const capture2 = lookCaptureOf(paths, file);
|
|
4402
|
+
if (capture2 !== void 0) recordLookRead(input.runtime, paths, capture2);
|
|
4403
|
+
return { kind: "allow" };
|
|
4404
|
+
};
|
|
4377
4405
|
|
|
4378
4406
|
// src/guards/release/markers.ts
|
|
4379
4407
|
var LINE = /^\s*(?:finitemonkey|fm):\s+(allow|approve|trust)\s+(\S+)(?:\s+(.*))?$/i;
|
|
@@ -4410,8 +4438,8 @@ var parseMarkers = (prompt) => {
|
|
|
4410
4438
|
// src/policy/pause.ts
|
|
4411
4439
|
var PAUSE_FILE = "pause.json";
|
|
4412
4440
|
var readPause = (runtime, paths) => {
|
|
4413
|
-
const
|
|
4414
|
-
const parsed =
|
|
4441
|
+
const text7 = runtime.readText(paths.cache(PAUSE_FILE));
|
|
4442
|
+
const parsed = text7.ok ? parseJsonObject(text7.value) : void 0;
|
|
4415
4443
|
if (parsed?.ok !== true || !isRecord(parsed.value)) return void 0;
|
|
4416
4444
|
const { sessionId, since, by } = parsed.value;
|
|
4417
4445
|
if (typeof sessionId !== "string" || typeof since !== "string") return void 0;
|
|
@@ -4466,9 +4494,9 @@ var countIn = (value) => {
|
|
|
4466
4494
|
if (Array.isArray(value)) return value.reduce((sum, item2) => sum + countIn(item2), 0);
|
|
4467
4495
|
return isRecord(value) ? Object.values(value).reduce((sum, item2) => sum + countIn(item2), 0) : 0;
|
|
4468
4496
|
};
|
|
4469
|
-
var censusOf = (
|
|
4470
|
-
if (
|
|
4471
|
-
const parsed = parseJsonObject(
|
|
4497
|
+
var censusOf = (text7) => {
|
|
4498
|
+
if (text7 === void 0) return { hooks: 0, disabled: false };
|
|
4499
|
+
const parsed = parseJsonObject(text7);
|
|
4472
4500
|
if (!parsed.ok) return void 0;
|
|
4473
4501
|
return { hooks: countIn(parsed.value["hooks"]), disabled: parsed.value["disableAllHooks"] === true };
|
|
4474
4502
|
};
|
|
@@ -4478,8 +4506,8 @@ var settingsFiles = (paths, env) => [
|
|
|
4478
4506
|
...env["HOME"] === void 0 ? [] : [toAbsolutePath(join13(env["HOME"], ".claude", "settings.json"))]
|
|
4479
4507
|
];
|
|
4480
4508
|
var read2 = (runtime, path) => {
|
|
4481
|
-
const
|
|
4482
|
-
return
|
|
4509
|
+
const text7 = runtime.readText(path);
|
|
4510
|
+
return text7.ok ? text7.value : void 0;
|
|
4483
4511
|
};
|
|
4484
4512
|
var known = (runtime, paths) => {
|
|
4485
4513
|
const raw = readJsonFile(runtime, paths.cache(CONFIG_FILE3))?.["hooks"];
|
|
@@ -4534,9 +4562,9 @@ var LOCKFILES2 = [
|
|
|
4534
4562
|
];
|
|
4535
4563
|
var packageManager = (files) => LOCKFILES2.find(([file]) => files.exists(file))?.[1] ?? "npm";
|
|
4536
4564
|
var scriptChecks = (files) => {
|
|
4537
|
-
const
|
|
4538
|
-
if (
|
|
4539
|
-
const parsed = parseJsonObject(
|
|
4565
|
+
const text7 = files.read("package.json");
|
|
4566
|
+
if (text7 === void 0) return [];
|
|
4567
|
+
const parsed = parseJsonObject(text7);
|
|
4540
4568
|
const scripts = parsed.ok ? parsed.value["scripts"] : void 0;
|
|
4541
4569
|
if (!isRecord(scripts)) return [];
|
|
4542
4570
|
const pm = packageManager(files);
|
|
@@ -4626,16 +4654,16 @@ var unescapeDouble = (body2) => {
|
|
|
4626
4654
|
}
|
|
4627
4655
|
return accept(out);
|
|
4628
4656
|
};
|
|
4629
|
-
var quotedEnd = (
|
|
4630
|
-
const quote =
|
|
4631
|
-
for (let index = start + 1; index <
|
|
4632
|
-
const char =
|
|
4657
|
+
var quotedEnd = (text7, start) => {
|
|
4658
|
+
const quote = text7[start];
|
|
4659
|
+
for (let index = start + 1; index < text7.length; index += 1) {
|
|
4660
|
+
const char = text7[index];
|
|
4633
4661
|
if (quote === '"' && char === "\\") {
|
|
4634
4662
|
index += 1;
|
|
4635
4663
|
continue;
|
|
4636
4664
|
}
|
|
4637
4665
|
if (char !== quote) continue;
|
|
4638
|
-
if (quote === "'" &&
|
|
4666
|
+
if (quote === "'" && text7[index + 1] === "'") {
|
|
4639
4667
|
index += 1;
|
|
4640
4668
|
continue;
|
|
4641
4669
|
}
|
|
@@ -4643,20 +4671,20 @@ var quotedEnd = (text6, start) => {
|
|
|
4643
4671
|
}
|
|
4644
4672
|
return -1;
|
|
4645
4673
|
};
|
|
4646
|
-
var parseQuoted = (
|
|
4647
|
-
const end = quotedEnd(
|
|
4648
|
-
if (end !==
|
|
4649
|
-
const body2 =
|
|
4650
|
-
return
|
|
4674
|
+
var parseQuoted = (text7) => {
|
|
4675
|
+
const end = quotedEnd(text7, 0);
|
|
4676
|
+
if (end !== text7.length - 1) return reject(`unterminated or trailing text after quoted string`);
|
|
4677
|
+
const body2 = text7.slice(1, -1);
|
|
4678
|
+
return text7.startsWith('"') ? unescapeDouble(body2) : accept(body2.replace(/''/g, "'"));
|
|
4651
4679
|
};
|
|
4652
4680
|
var NUMBER = /^-?(0|[1-9]\d*)(\.\d+)?([eE][-+]?\d+)?$/;
|
|
4653
4681
|
var UNSUPPORTED = /^[&*!|>%@`]/;
|
|
4654
|
-
var parsePlain = (
|
|
4655
|
-
if (UNSUPPORTED.test(
|
|
4656
|
-
if (
|
|
4657
|
-
if (
|
|
4658
|
-
if (NUMBER.test(
|
|
4659
|
-
return accept(
|
|
4682
|
+
var parsePlain = (text7) => {
|
|
4683
|
+
if (UNSUPPORTED.test(text7)) return reject(`unsupported YAML syntax "${text7.slice(0, 1)}"`);
|
|
4684
|
+
if (text7 === "true" || text7 === "false") return accept(text7 === "true");
|
|
4685
|
+
if (text7 === "null" || text7 === "~") return accept(null);
|
|
4686
|
+
if (NUMBER.test(text7)) return accept(Number(text7));
|
|
4687
|
+
return accept(text7);
|
|
4660
4688
|
};
|
|
4661
4689
|
|
|
4662
4690
|
// src/core/yaml/flow.ts
|
|
@@ -4683,17 +4711,17 @@ var splitTop = (body2) => {
|
|
|
4683
4711
|
if (last.trim() !== "" || parts.length > 0) parts.push(last);
|
|
4684
4712
|
return accept(parts.map((part2) => part2.trim()));
|
|
4685
4713
|
};
|
|
4686
|
-
var splitKey = (
|
|
4714
|
+
var splitKey = (text7) => {
|
|
4687
4715
|
let index = 0;
|
|
4688
|
-
if (
|
|
4689
|
-
index = quotedEnd(
|
|
4716
|
+
if (text7.startsWith('"') || text7.startsWith("'")) {
|
|
4717
|
+
index = quotedEnd(text7, 0) + 1;
|
|
4690
4718
|
if (index <= 0) return void 0;
|
|
4691
4719
|
}
|
|
4692
|
-
for (; index <
|
|
4693
|
-
const char =
|
|
4694
|
-
if (char === "#" &&
|
|
4695
|
-
if (char === ":" && (index + 1 ===
|
|
4696
|
-
return { key:
|
|
4720
|
+
for (; index < text7.length; index += 1) {
|
|
4721
|
+
const char = text7[index];
|
|
4722
|
+
if (char === "#" && text7[index - 1] === " ") return void 0;
|
|
4723
|
+
if (char === ":" && (index + 1 === text7.length || text7[index + 1] === " ")) {
|
|
4724
|
+
return { key: text7.slice(0, index).trim(), rest: text7.slice(index + 1).trim() };
|
|
4697
4725
|
}
|
|
4698
4726
|
}
|
|
4699
4727
|
return void 0;
|
|
@@ -4722,39 +4750,39 @@ var flowSeq = (parts) => {
|
|
|
4722
4750
|
}
|
|
4723
4751
|
return accept(out);
|
|
4724
4752
|
};
|
|
4725
|
-
var parseValue = (
|
|
4726
|
-
if (
|
|
4727
|
-
const first =
|
|
4728
|
-
if (first === '"' || first === "'") return parseQuoted(
|
|
4753
|
+
var parseValue = (text7) => {
|
|
4754
|
+
if (text7 === "") return accept(null);
|
|
4755
|
+
const first = text7[0] ?? "";
|
|
4756
|
+
if (first === '"' || first === "'") return parseQuoted(text7);
|
|
4729
4757
|
const close2 = OPEN[first];
|
|
4730
|
-
if (close2 === void 0) return parsePlain(
|
|
4731
|
-
if (!
|
|
4732
|
-
const parts = splitTop(
|
|
4758
|
+
if (close2 === void 0) return parsePlain(text7);
|
|
4759
|
+
if (!text7.endsWith(close2)) return reject(`flow collection must end on the same line with "${close2}"`);
|
|
4760
|
+
const parts = splitTop(text7.slice(1, -1));
|
|
4733
4761
|
if (!parts.ok) return parts;
|
|
4734
4762
|
return first === "[" ? flowSeq(parts.value) : flowMap(parts.value);
|
|
4735
4763
|
};
|
|
4736
4764
|
|
|
4737
4765
|
// src/core/yaml/parse.ts
|
|
4738
4766
|
var BOM = new RegExp(`^${String.fromCharCode(65279)}`);
|
|
4739
|
-
var stripComment2 = (
|
|
4740
|
-
for (let index = 0; index <
|
|
4741
|
-
const char =
|
|
4767
|
+
var stripComment2 = (text7) => {
|
|
4768
|
+
for (let index = 0; index < text7.length; index += 1) {
|
|
4769
|
+
const char = text7[index];
|
|
4742
4770
|
if (char === '"' || char === "'") {
|
|
4743
|
-
const end = quotedEnd(
|
|
4744
|
-
if (end < 0) return
|
|
4771
|
+
const end = quotedEnd(text7, index);
|
|
4772
|
+
if (end < 0) return text7;
|
|
4745
4773
|
index = end;
|
|
4746
|
-
} else if (char === "#" && (index === 0 ||
|
|
4774
|
+
} else if (char === "#" && (index === 0 || text7[index - 1] === " ")) return text7.slice(0, index);
|
|
4747
4775
|
}
|
|
4748
|
-
return
|
|
4776
|
+
return text7;
|
|
4749
4777
|
};
|
|
4750
4778
|
var toLines = (source) => {
|
|
4751
4779
|
const lines3 = [];
|
|
4752
4780
|
const raw = source.replace(BOM, "").split(/\r?\n/);
|
|
4753
4781
|
for (const [index, full] of raw.entries()) {
|
|
4754
|
-
const
|
|
4755
|
-
const body2 =
|
|
4782
|
+
const text7 = stripComment2(full).trimEnd();
|
|
4783
|
+
const body2 = text7.trimStart();
|
|
4756
4784
|
if (body2 === "" || body2 === "---" && lines3.length === 0) continue;
|
|
4757
|
-
const indentText =
|
|
4785
|
+
const indentText = text7.slice(0, text7.length - body2.length);
|
|
4758
4786
|
if (indentText.includes(" ")) return reject({ line: index + 1, message: "tabs are not allowed for indentation" });
|
|
4759
4787
|
if (body2 === "---" || body2 === "...")
|
|
4760
4788
|
return reject({ line: index + 1, message: "only one YAML document is allowed" });
|
|
@@ -4763,11 +4791,11 @@ var toLines = (source) => {
|
|
|
4763
4791
|
return accept(lines3);
|
|
4764
4792
|
};
|
|
4765
4793
|
var fail = (line2, message) => reject({ line: line2?.number ?? 0, message });
|
|
4766
|
-
var scalar = (line2,
|
|
4767
|
-
const value = parseValue(
|
|
4794
|
+
var scalar = (line2, text7) => {
|
|
4795
|
+
const value = parseValue(text7);
|
|
4768
4796
|
return value.ok ? value : fail(line2, value.error);
|
|
4769
4797
|
};
|
|
4770
|
-
var isItem = (
|
|
4798
|
+
var isItem = (text7) => text7 === "-" || text7.startsWith("- ");
|
|
4771
4799
|
var child = (lines3, index, parent, sameIndentSeq) => {
|
|
4772
4800
|
const next2 = lines3[index];
|
|
4773
4801
|
if (next2 !== void 0 && next2.indent > parent.indent) return block(lines3, index, next2.indent);
|
|
@@ -5010,8 +5038,8 @@ var validateContract = (value, implicit = false) => {
|
|
|
5010
5038
|
// src/task/load.ts
|
|
5011
5039
|
var contractHash = (contract) => sha256Hex(JSON.stringify(contract.acceptance.map((check2) => [check2.id, check2.kind, check2.run ?? ""])));
|
|
5012
5040
|
var taskRelative = (id) => `.finitemonkey/tasks/${id}.yml`;
|
|
5013
|
-
var parseContract = (id,
|
|
5014
|
-
const yaml = parseYaml(
|
|
5041
|
+
var parseContract = (id, text7) => {
|
|
5042
|
+
const yaml = parseYaml(text7);
|
|
5015
5043
|
if (!yaml.ok)
|
|
5016
5044
|
return reject({ kind: "invalid", id, issues: [`line ${String(yaml.error.line)}: ${yaml.error.message}`] });
|
|
5017
5045
|
const valid = validateContract(yaml.value);
|
|
@@ -5121,10 +5149,10 @@ var resolveBase = (git, declared) => {
|
|
|
5121
5149
|
const merged2 = remote === void 0 ? void 0 : git.out(["merge-base", "HEAD", remote])?.trim();
|
|
5122
5150
|
return nonEmpty(merged2) ?? nonEmpty(headSha(git)) ?? EMPTY_TREE;
|
|
5123
5151
|
};
|
|
5124
|
-
var nulFields = (
|
|
5152
|
+
var nulFields = (text7) => text7.split("\0").filter((part2) => part2 !== "");
|
|
5125
5153
|
var untracked = (git) => nulFields(git.out(["ls-files", "--others", "--exclude-standard", "-z", "--", ".", EXCLUDE_FM]) ?? "");
|
|
5126
|
-
var parseNameStatus = (
|
|
5127
|
-
const fields = nulFields(
|
|
5154
|
+
var parseNameStatus = (text7) => {
|
|
5155
|
+
const fields = nulFields(text7);
|
|
5128
5156
|
const changes = [];
|
|
5129
5157
|
for (let index = 0; index < fields.length; index += 1) {
|
|
5130
5158
|
const code = fields[index] ?? "";
|
|
@@ -5192,8 +5220,8 @@ var aheadOfUpstream = (git) => {
|
|
|
5192
5220
|
|
|
5193
5221
|
// src/gate/diff-shape.ts
|
|
5194
5222
|
var untrackedLines = (runtime, root, changes) => changes.filter((change) => change.status === "A").reduce((sum, change) => {
|
|
5195
|
-
const
|
|
5196
|
-
return sum + (
|
|
5223
|
+
const text7 = runtime.readText(toAbsolutePath(join14(root, change.path)));
|
|
5224
|
+
return sum + (text7.ok ? text7.value.split("\n").length : 0);
|
|
5197
5225
|
}, 0);
|
|
5198
5226
|
var diffShape = (runtime, git, base, changes) => ({
|
|
5199
5227
|
files: changes.map((change) => change.path),
|
|
@@ -5216,14 +5244,14 @@ var ciChecks = (value) => Array.isArray(value) ? value.flatMap((item2) => {
|
|
|
5216
5244
|
const name = optionalString(item2, "name") ?? optionalString(item2, "context") ?? "?";
|
|
5217
5245
|
return [{ name, state: ciState(item2) }];
|
|
5218
5246
|
}) : [];
|
|
5219
|
-
var parsePrView = (
|
|
5220
|
-
const parsed = parseJsonObject(
|
|
5247
|
+
var parsePrView = (text7) => {
|
|
5248
|
+
const parsed = parseJsonObject(text7);
|
|
5221
5249
|
if (!parsed.ok) return void 0;
|
|
5222
5250
|
const record3 = parsed.value;
|
|
5223
|
-
const
|
|
5224
|
-
if (typeof
|
|
5251
|
+
const number2 = record3["number"];
|
|
5252
|
+
if (typeof number2 !== "number") return void 0;
|
|
5225
5253
|
return {
|
|
5226
|
-
number,
|
|
5254
|
+
number: number2,
|
|
5227
5255
|
url: optionalString(record3, "url") ?? "",
|
|
5228
5256
|
state: optionalString(record3, "state") ?? "",
|
|
5229
5257
|
mergeable: optionalString(record3, "mergeable") ?? "UNKNOWN",
|
|
@@ -5397,21 +5425,21 @@ var applySize = (contract, verdict) => {
|
|
|
5397
5425
|
|
|
5398
5426
|
// src/gate/prepare.ts
|
|
5399
5427
|
var diskFiles = (runtime, root) => ({
|
|
5400
|
-
read: (
|
|
5401
|
-
const
|
|
5402
|
-
return
|
|
5428
|
+
read: (relative11) => {
|
|
5429
|
+
const text7 = runtime.readText(toAbsolutePath(join15(root, relative11)));
|
|
5430
|
+
return text7.ok ? text7.value : void 0;
|
|
5403
5431
|
},
|
|
5404
|
-
exists: (
|
|
5432
|
+
exists: (relative11) => runtime.stat(toAbsolutePath(join15(root, relative11))) !== void 0
|
|
5405
5433
|
});
|
|
5406
5434
|
var baseFiles = (git, base, disk) => ({
|
|
5407
|
-
read: (
|
|
5435
|
+
read: (relative11) => base === EMPTY_TREE ? disk.read(relative11) : fileAt(git, base, relative11),
|
|
5408
5436
|
exists: disk.exists
|
|
5409
5437
|
});
|
|
5410
5438
|
var loadContract = (options, paths, git, base) => {
|
|
5411
5439
|
const disk = diskFiles(options.runtime, paths.root);
|
|
5412
5440
|
const id = activeTaskId(options.runtime, paths, options.taskId);
|
|
5413
5441
|
if (id === void 0) return accept(loadImplicit(options.runtime, paths, baseFiles(git, base, disk)));
|
|
5414
|
-
return loadTask(options.runtime, paths, { disk: disk.read, base: (
|
|
5442
|
+
return loadTask(options.runtime, paths, { disk: disk.read, base: (relative11) => fileAt(git, base, relative11) }, id);
|
|
5415
5443
|
};
|
|
5416
5444
|
var memo = (make) => {
|
|
5417
5445
|
let value;
|
|
@@ -5520,11 +5548,11 @@ var trust = (input, paths, task) => {
|
|
|
5520
5548
|
});
|
|
5521
5549
|
if (ready.kind !== "ready" || ready.prepared.ctx.contract.id !== task) return false;
|
|
5522
5550
|
const lock = readTrust(runtime, paths);
|
|
5523
|
-
const
|
|
5551
|
+
const text7 = runtime.readText(paths.task(task));
|
|
5524
5552
|
return writeTrust(runtime, paths, {
|
|
5525
5553
|
...lock,
|
|
5526
5554
|
contracts: { ...lock.contracts, [task]: ready.prepared.loaded.hash },
|
|
5527
|
-
approved: task === IMPLICIT_ID || !
|
|
5555
|
+
approved: task === IMPLICIT_ID || !text7.ok ? lock.approved : { ...lock.approved, [task]: text7.value }
|
|
5528
5556
|
}).ok;
|
|
5529
5557
|
};
|
|
5530
5558
|
var apply = (input, paths, markers) => {
|
|
@@ -5576,8 +5604,8 @@ var promptRelease = (input) => {
|
|
|
5576
5604
|
var PRS_CACHE_MS = 10 * 6e4;
|
|
5577
5605
|
var PRS_TIMEOUT_MS = 3e3;
|
|
5578
5606
|
var PRS_AUTH_TIMEOUT_MS = 2e3;
|
|
5579
|
-
var parsePrList = (
|
|
5580
|
-
const parsed = parseJson(
|
|
5607
|
+
var parsePrList = (text7) => {
|
|
5608
|
+
const parsed = parseJson(text7);
|
|
5581
5609
|
if (!parsed.ok || !Array.isArray(parsed.value)) return void 0;
|
|
5582
5610
|
return parsed.value.flatMap((item2) => {
|
|
5583
5611
|
if (!isRecord(item2) || typeof item2["number"] !== "number") return [];
|
|
@@ -6303,7 +6331,23 @@ var en_default = {
|
|
|
6303
6331
|
"githooks.pass": "ok {check}",
|
|
6304
6332
|
"githooks.fail": "FAIL {check}: {detail}. Fix: {fix}",
|
|
6305
6333
|
"githooks.error.failed": "{count, plural, one {# check failed} other {# checks failed}}",
|
|
6306
|
-
"githooks.error.no_base": "the base {detail} does not exist here; fetch it first (actions/checkout needs fetch-depth: 0)"
|
|
6334
|
+
"githooks.error.no_base": "the base {detail} does not exist here; fetch it first (actions/checkout needs fetch-depth: 0)",
|
|
6335
|
+
"command.look.summary": "Capture the screens in .finitemonkey/look.yml at 1440 and 390 px and measure them",
|
|
6336
|
+
"look.done": "{count, plural, one {# capture} other {# captures}}, {failures, plural, one {# failure} other {# failures}}. Open every capture with Read.",
|
|
6337
|
+
"look.recorded": "{captures} captures, {failures} failures",
|
|
6338
|
+
"look.error.not_a_repo": "look runs inside a git repository",
|
|
6339
|
+
"look.error.not_configured": ".finitemonkey/look.yml is missing or has no port",
|
|
6340
|
+
"look.error.playwright_missing": "Playwright is not installed in this project",
|
|
6341
|
+
"look.error.browser_missing": "no Chromium, Chrome or Edge could be started",
|
|
6342
|
+
"look.error.app_failed": "the app did not come up: {detail}",
|
|
6343
|
+
"gate.require.look": "changed screens captured with zero failures and every capture opened after the last edit",
|
|
6344
|
+
"gate.look.not_run": "no capture of the current tree: run `finitemonkey look`",
|
|
6345
|
+
"gate.look.no_captures": "the last look run produced no capture",
|
|
6346
|
+
"gate.look.finding": "{rule} on {route} at {viewport}px: {detail}",
|
|
6347
|
+
"gate.look.allow_ignored": "exception ignored, its decision is not recorded: {entry}",
|
|
6348
|
+
"gate.look.unopened": "{count, plural, one {# capture} other {# captures}} not opened after the last edit to a screen file",
|
|
6349
|
+
"gate.look.open": "open with Read: {file}",
|
|
6350
|
+
"gate.look.ok": "{count, plural, one {# capture} other {# captures}}, no failure, all opened"
|
|
6307
6351
|
};
|
|
6308
6352
|
|
|
6309
6353
|
// src/i18n/es.json
|
|
@@ -6881,7 +6925,23 @@ var es_default = {
|
|
|
6881
6925
|
"githooks.pass": "ok {check}",
|
|
6882
6926
|
"githooks.fail": "FALLÓ {check}: {detail}. Arreglo: {fix}",
|
|
6883
6927
|
"githooks.error.failed": "{count, plural, one {# revisión falló} other {# revisiones fallaron}}",
|
|
6884
|
-
"githooks.error.no_base": "la base {detail} no existe aquí; tráela antes (actions/checkout necesita fetch-depth: 0)"
|
|
6928
|
+
"githooks.error.no_base": "la base {detail} no existe aquí; tráela antes (actions/checkout necesita fetch-depth: 0)",
|
|
6929
|
+
"command.look.summary": "Captura las pantallas de .finitemonkey/look.yml a 1440 y 390 px y las mide",
|
|
6930
|
+
"look.done": "{count, plural, one {# captura} other {# capturas}}, {failures, plural, one {# fallo} other {# fallos}}. Abre cada captura con Read.",
|
|
6931
|
+
"look.recorded": "{captures} capturas, {failures} fallos",
|
|
6932
|
+
"look.error.not_a_repo": "look se ejecuta dentro de un repositorio git",
|
|
6933
|
+
"look.error.not_configured": ".finitemonkey/look.yml no existe o no tiene port",
|
|
6934
|
+
"look.error.playwright_missing": "Playwright no está instalado en este proyecto",
|
|
6935
|
+
"look.error.browser_missing": "no se pudo abrir ningún Chromium, Chrome o Edge",
|
|
6936
|
+
"look.error.app_failed": "la app no arrancó: {detail}",
|
|
6937
|
+
"gate.require.look": "pantallas cambiadas capturadas sin fallos y cada captura abierta después de la última edición",
|
|
6938
|
+
"gate.look.not_run": "ninguna captura del árbol actual: ejecuta `finitemonkey look`",
|
|
6939
|
+
"gate.look.no_captures": "la última ejecución de look no produjo capturas",
|
|
6940
|
+
"gate.look.finding": "{rule} en {route} a {viewport}px: {detail}",
|
|
6941
|
+
"gate.look.allow_ignored": "excepción ignorada, su decisión no está registrada: {entry}",
|
|
6942
|
+
"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
|
+
"gate.look.open": "abre con Read: {file}",
|
|
6944
|
+
"gate.look.ok": "{count, plural, one {# captura} other {# capturas}}, ningún fallo, todas abiertas"
|
|
6885
6945
|
};
|
|
6886
6946
|
|
|
6887
6947
|
// src/i18n/pt-BR.json
|
|
@@ -7459,7 +7519,23 @@ var pt_BR_default = {
|
|
|
7459
7519
|
"githooks.pass": "ok {check}",
|
|
7460
7520
|
"githooks.fail": "FALHOU {check}: {detail}. Conserto: {fix}",
|
|
7461
7521
|
"githooks.error.failed": "{count, plural, one {# checagem falhou} other {# checagens falharam}}",
|
|
7462
|
-
"githooks.error.no_base": "a base {detail} não existe aqui; busque antes (o actions/checkout precisa de fetch-depth: 0)"
|
|
7522
|
+
"githooks.error.no_base": "a base {detail} não existe aqui; busque antes (o actions/checkout precisa de fetch-depth: 0)",
|
|
7523
|
+
"command.look.summary": "Captura as telas de .finitemonkey/look.yml em 1440 e 390 px e mede cada uma",
|
|
7524
|
+
"look.done": "{count, plural, one {# captura} other {# capturas}}, {failures, plural, one {# falha} other {# falhas}}. Abra cada captura com Read.",
|
|
7525
|
+
"look.recorded": "{captures} capturas, {failures} falhas",
|
|
7526
|
+
"look.error.not_a_repo": "o look roda dentro de um repositório git",
|
|
7527
|
+
"look.error.not_configured": ".finitemonkey/look.yml não existe ou não tem port",
|
|
7528
|
+
"look.error.playwright_missing": "o Playwright não está instalado neste projeto",
|
|
7529
|
+
"look.error.browser_missing": "nenhum Chromium, Chrome ou Edge pôde ser aberto",
|
|
7530
|
+
"look.error.app_failed": "o app não subiu: {detail}",
|
|
7531
|
+
"gate.require.look": "telas alteradas capturadas sem falha e cada captura aberta depois da última edição",
|
|
7532
|
+
"gate.look.not_run": "nenhuma captura da árvore atual: rode `finitemonkey look`",
|
|
7533
|
+
"gate.look.no_captures": "a última rodada do look não gerou captura",
|
|
7534
|
+
"gate.look.finding": "{rule} em {route} a {viewport}px: {detail}",
|
|
7535
|
+
"gate.look.allow_ignored": "exceção ignorada, a decisão não foi registrada: {entry}",
|
|
7536
|
+
"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
|
+
"gate.look.open": "abra com Read: {file}",
|
|
7538
|
+
"gate.look.ok": "{count, plural, one {# captura} other {# capturas}}, nenhuma falha, todas abertas"
|
|
7463
7539
|
};
|
|
7464
7540
|
|
|
7465
7541
|
// src/i18n/review.json
|
|
@@ -8038,7 +8114,23 @@ var review_default = {
|
|
|
8038
8114
|
"githooks.pass": "machine",
|
|
8039
8115
|
"githooks.fail": "machine",
|
|
8040
8116
|
"githooks.error.failed": "machine",
|
|
8041
|
-
"githooks.error.no_base": "machine"
|
|
8117
|
+
"githooks.error.no_base": "machine",
|
|
8118
|
+
"command.look.summary": "machine",
|
|
8119
|
+
"look.done": "machine",
|
|
8120
|
+
"look.recorded": "machine",
|
|
8121
|
+
"look.error.not_a_repo": "machine",
|
|
8122
|
+
"look.error.not_configured": "machine",
|
|
8123
|
+
"look.error.playwright_missing": "machine",
|
|
8124
|
+
"look.error.browser_missing": "machine",
|
|
8125
|
+
"look.error.app_failed": "machine",
|
|
8126
|
+
"gate.require.look": "machine",
|
|
8127
|
+
"gate.look.not_run": "machine",
|
|
8128
|
+
"gate.look.no_captures": "machine",
|
|
8129
|
+
"gate.look.finding": "machine",
|
|
8130
|
+
"gate.look.allow_ignored": "machine",
|
|
8131
|
+
"gate.look.unopened": "machine",
|
|
8132
|
+
"gate.look.open": "machine",
|
|
8133
|
+
"gate.look.ok": "machine"
|
|
8042
8134
|
},
|
|
8043
8135
|
es: {
|
|
8044
8136
|
"command.init.summary": "machine",
|
|
@@ -8614,7 +8706,23 @@ var review_default = {
|
|
|
8614
8706
|
"githooks.pass": "machine",
|
|
8615
8707
|
"githooks.fail": "machine",
|
|
8616
8708
|
"githooks.error.failed": "machine",
|
|
8617
|
-
"githooks.error.no_base": "machine"
|
|
8709
|
+
"githooks.error.no_base": "machine",
|
|
8710
|
+
"command.look.summary": "machine",
|
|
8711
|
+
"look.done": "machine",
|
|
8712
|
+
"look.recorded": "machine",
|
|
8713
|
+
"look.error.not_a_repo": "machine",
|
|
8714
|
+
"look.error.not_configured": "machine",
|
|
8715
|
+
"look.error.playwright_missing": "machine",
|
|
8716
|
+
"look.error.browser_missing": "machine",
|
|
8717
|
+
"look.error.app_failed": "machine",
|
|
8718
|
+
"gate.require.look": "machine",
|
|
8719
|
+
"gate.look.not_run": "machine",
|
|
8720
|
+
"gate.look.no_captures": "machine",
|
|
8721
|
+
"gate.look.finding": "machine",
|
|
8722
|
+
"gate.look.allow_ignored": "machine",
|
|
8723
|
+
"gate.look.unopened": "machine",
|
|
8724
|
+
"gate.look.open": "machine",
|
|
8725
|
+
"gate.look.ok": "machine"
|
|
8618
8726
|
}
|
|
8619
8727
|
};
|
|
8620
8728
|
|
|
@@ -8875,7 +8983,7 @@ var preCompact = (input) => {
|
|
|
8875
8983
|
// src/gate/review.ts
|
|
8876
8984
|
var REVIEWS_FILE = "reviews.json";
|
|
8877
8985
|
var REVIEWER_AGENT = /(^|:)[\w-]*reviewer$/i;
|
|
8878
|
-
var
|
|
8986
|
+
var KEEP2 = 50;
|
|
8879
8987
|
var toReview = (value) => {
|
|
8880
8988
|
if (!isRecord(value)) return void 0;
|
|
8881
8989
|
const { treeHash: treeHash2, agent, at } = value;
|
|
@@ -8889,7 +8997,7 @@ var readReviews = (runtime, paths) => {
|
|
|
8889
8997
|
}) : [];
|
|
8890
8998
|
};
|
|
8891
8999
|
var recordReview = (runtime, paths, treeHash2, agent) => {
|
|
8892
|
-
const reviews = [...readReviews(runtime, paths), { treeHash: treeHash2, agent, at: runtime.now() }].slice(-
|
|
9000
|
+
const reviews = [...readReviews(runtime, paths), { treeHash: treeHash2, agent, at: runtime.now() }].slice(-KEEP2);
|
|
8893
9001
|
return writeJsonFile(runtime, paths.cache(REVIEWS_FILE), { reviews: reviews.map((review2) => ({ ...review2 })) }).ok;
|
|
8894
9002
|
};
|
|
8895
9003
|
|
|
@@ -8904,10 +9012,10 @@ var pairs = (record3) => {
|
|
|
8904
9012
|
return names.filter((name) => typeof name === "string").map((name) => `${task}\0${name}`);
|
|
8905
9013
|
};
|
|
8906
9014
|
var callersRate = (runtime, paths) => {
|
|
8907
|
-
const
|
|
9015
|
+
const text7 = runtime.readText(paths.log);
|
|
8908
9016
|
const flagged = /* @__PURE__ */ new Set();
|
|
8909
9017
|
const released2 = /* @__PURE__ */ new Set();
|
|
8910
|
-
for (const line2 of
|
|
9018
|
+
for (const line2 of text7.ok ? text7.value.split("\n") : []) {
|
|
8911
9019
|
const parsed = line2.trim() === "" ? void 0 : parseJsonObject(line2);
|
|
8912
9020
|
if (parsed === void 0 || !parsed.ok || !isRecord(parsed.value)) continue;
|
|
8913
9021
|
const event = parsed.value["event"];
|
|
@@ -8932,7 +9040,7 @@ import { posix as posix2 } from "node:path";
|
|
|
8932
9040
|
var JS_SPECIFIER = /(?:\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)["']([^"']+)["']/g;
|
|
8933
9041
|
var JS_EXTENSION = /\.(?:[cm]?[jt]sx?)$/;
|
|
8934
9042
|
var stripModule = (path) => path.replace(JS_EXTENSION, "").replace(/\/index$/, "");
|
|
8935
|
-
var jsSpecifiers = (
|
|
9043
|
+
var jsSpecifiers = (text7) => [...text7.matchAll(JS_SPECIFIER)].map((match) => match[1] ?? "");
|
|
8936
9044
|
var jsImports = (callerPath, callerText, origin) => {
|
|
8937
9045
|
const target2 = stripModule(origin);
|
|
8938
9046
|
return jsSpecifiers(callerText).some((specifier) => {
|
|
@@ -9034,9 +9142,9 @@ var languageOf = (path) => {
|
|
|
9034
9142
|
const extension = extname(path).toLowerCase();
|
|
9035
9143
|
return LANGUAGES2.find((language) => language.extensions.includes(extension));
|
|
9036
9144
|
};
|
|
9037
|
-
var definitionsIn = (language,
|
|
9145
|
+
var definitionsIn = (language, text7) => {
|
|
9038
9146
|
const names = /* @__PURE__ */ new Set();
|
|
9039
|
-
for (const line2 of
|
|
9147
|
+
for (const line2 of text7.split(/\r?\n/)) {
|
|
9040
9148
|
for (const pattern of language.definitions) {
|
|
9041
9149
|
const name = pattern.exec(line2)?.[1];
|
|
9042
9150
|
if (name !== void 0 && !KEYWORDS.has(name)) names.add(name);
|
|
@@ -9049,8 +9157,8 @@ var definesName = (language, line2, name) => language.definitions.some((pattern)
|
|
|
9049
9157
|
// src/gate/callers/scan.ts
|
|
9050
9158
|
var MAX_SYMBOLS = 200;
|
|
9051
9159
|
var current = (runtime, root, path) => {
|
|
9052
|
-
const
|
|
9053
|
-
return
|
|
9160
|
+
const text7 = runtime.readText(toAbsolutePath(join16(root, path)));
|
|
9161
|
+
return text7.ok ? text7.value : "";
|
|
9054
9162
|
};
|
|
9055
9163
|
var keyOf = (language, name) => `${language.id}\0${name}`;
|
|
9056
9164
|
var redefinedNames = (runtime, git, changes) => {
|
|
@@ -9081,8 +9189,8 @@ var removedSymbols = (runtime, git, base, changes) => {
|
|
|
9081
9189
|
};
|
|
9082
9190
|
var parseGrep = (output) => output.split("\n").filter((row) => row !== "").flatMap((row) => {
|
|
9083
9191
|
const [file = "", line2 = "", ...rest] = row.split("\0");
|
|
9084
|
-
const
|
|
9085
|
-
return file === "" || !Number.isInteger(
|
|
9192
|
+
const number2 = Number(line2);
|
|
9193
|
+
return file === "" || !Number.isInteger(number2) ? [] : [{ file: toPosixPath(file), line: number2, text: rest.join("\0") }];
|
|
9086
9194
|
});
|
|
9087
9195
|
var grep = (git, names) => {
|
|
9088
9196
|
if (names.length === 0) return [];
|
|
@@ -9091,13 +9199,13 @@ var grep = (git, names) => {
|
|
|
9091
9199
|
git.out(["grep", "-n", "-z", "-w", "-I", "--untracked", "--no-color", ...patterns, "--", ".", EXCLUDE_FM]) ?? ""
|
|
9092
9200
|
);
|
|
9093
9201
|
};
|
|
9094
|
-
var mentions = (
|
|
9202
|
+
var mentions = (text7, name) => new RegExp(`(?<![\\w$])${name.replace(/[$?!]/g, "\\$&")}(?![\\w$])`).test(text7);
|
|
9095
9203
|
var callSites = (runtime, git, symbols) => {
|
|
9096
9204
|
const hits = grep(git, [...new Set(symbols.map((symbol) => symbol.name))]);
|
|
9097
|
-
const
|
|
9205
|
+
const texts2 = /* @__PURE__ */ new Map();
|
|
9098
9206
|
const textOf4 = (file) => {
|
|
9099
|
-
const cached2 =
|
|
9100
|
-
|
|
9207
|
+
const cached2 = texts2.get(file) ?? current(runtime, git.cwd, file);
|
|
9208
|
+
texts2.set(file, cached2);
|
|
9101
9209
|
return cached2;
|
|
9102
9210
|
};
|
|
9103
9211
|
return symbols.flatMap(
|
|
@@ -9166,31 +9274,31 @@ var ZERO = [
|
|
|
9166
9274
|
/^ℹ tests 0$/m,
|
|
9167
9275
|
/No tests found/
|
|
9168
9276
|
];
|
|
9169
|
-
var pyTotal = (
|
|
9170
|
-
const line2 = /^=+ (.*) in [\d.]+s.*=+$/m.exec(
|
|
9277
|
+
var pyTotal = (text7) => {
|
|
9278
|
+
const line2 = /^=+ (.*) in [\d.]+s.*=+$/m.exec(text7)?.[1];
|
|
9171
9279
|
if (line2 === void 0) return void 0;
|
|
9172
9280
|
return [...line2.matchAll(/(\d+) (passed|failed|error|errors|skipped|xfailed|xpassed)/g)].reduce(
|
|
9173
9281
|
(sum, match) => sum + Number(match[1] ?? "0"),
|
|
9174
9282
|
0
|
|
9175
9283
|
);
|
|
9176
9284
|
};
|
|
9177
|
-
var counted = (
|
|
9285
|
+
var counted = (text7) => {
|
|
9178
9286
|
for (const counter of COUNTERS) {
|
|
9179
|
-
const total = counter.total.exec(
|
|
9287
|
+
const total = counter.total.exec(text7)?.[1];
|
|
9180
9288
|
if (total === void 0) continue;
|
|
9181
|
-
const failed4 = counter.failed?.exec(
|
|
9289
|
+
const failed4 = counter.failed?.exec(text7)?.[1];
|
|
9182
9290
|
return { total: Number(total), ...failed4 === void 0 ? {} : { failed: Number(failed4) } };
|
|
9183
9291
|
}
|
|
9184
|
-
const py = pyTotal(
|
|
9292
|
+
const py = pyTotal(text7);
|
|
9185
9293
|
return py === void 0 ? {} : { total: py };
|
|
9186
9294
|
};
|
|
9187
9295
|
var parseTestSummary = (output) => {
|
|
9188
|
-
const
|
|
9296
|
+
const text7 = output.replace(ANSI2, "");
|
|
9189
9297
|
const failures = [
|
|
9190
|
-
...new Set(FAILURE_LINES.flatMap((pattern) => [...
|
|
9298
|
+
...new Set(FAILURE_LINES.flatMap((pattern) => [...text7.matchAll(pattern)].map((match) => (match[1] ?? "").trim())))
|
|
9191
9299
|
].filter((name) => name !== "" && !/^failing tests:?$/i.test(name));
|
|
9192
|
-
if (ZERO.some((pattern) => pattern.test(
|
|
9193
|
-
return { ...counted(
|
|
9300
|
+
if (ZERO.some((pattern) => pattern.test(text7))) return { total: 0, failures };
|
|
9301
|
+
return { ...counted(text7), failures };
|
|
9194
9302
|
};
|
|
9195
9303
|
|
|
9196
9304
|
// src/gate/checks/command.ts
|
|
@@ -9201,8 +9309,8 @@ var REWRITERS = /^\s*(rtk)\b/;
|
|
|
9201
9309
|
var TRIVIAL = /* @__PURE__ */ new Set(["echo", "true", ":", "printf", "exit", "sleep", "cat", "yes", "pwd", "ls", "date", "env"]);
|
|
9202
9310
|
var TAIL = 160;
|
|
9203
9311
|
var MAX_FAILURES2 = 5;
|
|
9204
|
-
var tail = (
|
|
9205
|
-
const lines3 =
|
|
9312
|
+
var tail = (text7) => {
|
|
9313
|
+
const lines3 = text7.split(/\r?\n/).filter((line2) => line2.trim() !== "");
|
|
9206
9314
|
const last = lines3[lines3.length - 1]?.trim() ?? "";
|
|
9207
9315
|
return last.length > TAIL ? `${last.slice(0, TAIL)}…` : last;
|
|
9208
9316
|
};
|
|
@@ -9321,6 +9429,147 @@ var gitCheck = (check2, ctx) => {
|
|
|
9321
9429
|
return missing2.length > 0 ? failed(missing2) : passed([note("gate.git.ok", { require: wants.join(", ") })]);
|
|
9322
9430
|
};
|
|
9323
9431
|
|
|
9432
|
+
// src/gate/checks/look.ts
|
|
9433
|
+
import { join as join19 } from "node:path";
|
|
9434
|
+
|
|
9435
|
+
// src/look/config.ts
|
|
9436
|
+
import { join as join17 } from "node:path";
|
|
9437
|
+
|
|
9438
|
+
// src/look/types.ts
|
|
9439
|
+
var LOOK_RULES = [
|
|
9440
|
+
"look/overflow",
|
|
9441
|
+
"look/console-error",
|
|
9442
|
+
"look/request-failed",
|
|
9443
|
+
"look/i18n-key",
|
|
9444
|
+
"look/broken-image",
|
|
9445
|
+
"look/touch-target"
|
|
9446
|
+
];
|
|
9447
|
+
var BLOCKING_RULES = [
|
|
9448
|
+
"look/overflow",
|
|
9449
|
+
"look/console-error",
|
|
9450
|
+
"look/request-failed",
|
|
9451
|
+
"look/i18n-key",
|
|
9452
|
+
"look/broken-image"
|
|
9453
|
+
];
|
|
9454
|
+
var VIEWPORTS = [1440, 390];
|
|
9455
|
+
var LOOK_SCHEMA = "finitemonkey/look@1";
|
|
9456
|
+
|
|
9457
|
+
// src/look/config.ts
|
|
9458
|
+
var LOOK_CONFIG = ".finitemonkey/look.yml";
|
|
9459
|
+
var DEFAULT_SCREENS = [
|
|
9460
|
+
"**/*.tsx",
|
|
9461
|
+
"**/*.jsx",
|
|
9462
|
+
"**/*.vue",
|
|
9463
|
+
"**/*.svelte",
|
|
9464
|
+
"**/*.astro",
|
|
9465
|
+
"**/*.html",
|
|
9466
|
+
"**/*.css",
|
|
9467
|
+
"**/*.scss",
|
|
9468
|
+
"**/*.sass",
|
|
9469
|
+
"**/*.less"
|
|
9470
|
+
];
|
|
9471
|
+
var text5 = (value) => typeof value === "string" && value !== "" ? value : void 0;
|
|
9472
|
+
var number = (value) => {
|
|
9473
|
+
const parsed = typeof value === "string" ? Number(value) : Number.NaN;
|
|
9474
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : void 0;
|
|
9475
|
+
};
|
|
9476
|
+
var allowEntries = (tree) => LOOK_RULES.flatMap(
|
|
9477
|
+
(rule) => configList(tree, "allow", rule).flatMap((entry2) => {
|
|
9478
|
+
const [route, decision] = entry2.trim().split(/\s+/);
|
|
9479
|
+
return route === void 0 || decision === void 0 ? [] : [{ rule, route, decision }];
|
|
9480
|
+
})
|
|
9481
|
+
);
|
|
9482
|
+
var parseLookConfig = (source) => {
|
|
9483
|
+
const tree = parseConfigYaml(source);
|
|
9484
|
+
const screens = configAt(tree, "screens");
|
|
9485
|
+
const top = configMap(tree);
|
|
9486
|
+
return {
|
|
9487
|
+
port: number(top["port"]),
|
|
9488
|
+
start: text5(top["start"]),
|
|
9489
|
+
host: text5(top["host"]) ?? "127.0.0.1",
|
|
9490
|
+
routes: configList(tree, "routes").length === 0 ? ["/"] : configList(tree, "routes"),
|
|
9491
|
+
mocks: configList(tree, "mocks"),
|
|
9492
|
+
screens: screens === void 0 ? DEFAULT_SCREENS : configList(tree, "screens"),
|
|
9493
|
+
allow: allowEntries(tree),
|
|
9494
|
+
timeoutS: number(top["timeout_s"]) ?? 60
|
|
9495
|
+
};
|
|
9496
|
+
};
|
|
9497
|
+
var loadLookConfig = (runtime, root) => {
|
|
9498
|
+
const source = runtime.readText(toAbsolutePath(join17(root, LOOK_CONFIG)));
|
|
9499
|
+
return source.ok ? parseLookConfig(source.value) : void 0;
|
|
9500
|
+
};
|
|
9501
|
+
var withMock = (route, mock) => `${route}${route.includes("?") ? "&" : "?"}mock=${encodeURIComponent(mock)}`;
|
|
9502
|
+
var lookRoutes = (config) => config.routes.flatMap((route) => [route, ...config.mocks.map((mock) => withMock(route, mock))]);
|
|
9503
|
+
|
|
9504
|
+
// src/look/paths.ts
|
|
9505
|
+
import { join as join18, relative as relative5 } from "node:path";
|
|
9506
|
+
var LOOK_DIR = "look";
|
|
9507
|
+
var SUMMARY_FILE = "summary.json";
|
|
9508
|
+
var lookDir = (paths, treeHash2) => toAbsolutePath(join18(paths.dir, LOOK_DIR, treeHash2));
|
|
9509
|
+
var summaryPath = (paths, treeHash2) => toAbsolutePath(join18(lookDir(paths, treeHash2), SUMMARY_FILE));
|
|
9510
|
+
var projectRelative = (paths, absolute) => toPosixPath(relative5(paths.root, absolute));
|
|
9511
|
+
|
|
9512
|
+
// src/gate/checks/look.ts
|
|
9513
|
+
var MAX_LISTED2 = 10;
|
|
9514
|
+
var findingNote = (value) => {
|
|
9515
|
+
if (!isRecord(value) || value["blocking"] !== true) return [];
|
|
9516
|
+
const { rule, route, viewport, detail } = value;
|
|
9517
|
+
return [
|
|
9518
|
+
note("gate.look.finding", {
|
|
9519
|
+
rule: typeof rule === "string" ? rule : "",
|
|
9520
|
+
route: typeof route === "string" ? route : "",
|
|
9521
|
+
viewport: typeof viewport === "number" ? viewport : 0,
|
|
9522
|
+
detail: typeof detail === "string" ? detail : ""
|
|
9523
|
+
})
|
|
9524
|
+
];
|
|
9525
|
+
};
|
|
9526
|
+
var readSummary = (ctx) => {
|
|
9527
|
+
const text7 = ctx.runtime.readText(summaryPath(ctx.paths, ctx.treeHash));
|
|
9528
|
+
const parsed = text7.ok ? parseJsonObject(text7.value) : void 0;
|
|
9529
|
+
if (!parsed?.ok) return void 0;
|
|
9530
|
+
const { at, captures, findings, ignoredAllows } = parsed.value;
|
|
9531
|
+
if (typeof at !== "string" || !Array.isArray(captures) || !Array.isArray(findings)) return void 0;
|
|
9532
|
+
return {
|
|
9533
|
+
at,
|
|
9534
|
+
captures: captures.flatMap(
|
|
9535
|
+
(item2) => isRecord(item2) && typeof item2["path"] === "string" ? [item2["path"]] : []
|
|
9536
|
+
),
|
|
9537
|
+
blocking: findings.flatMap(findingNote),
|
|
9538
|
+
ignored: Array.isArray(ignoredAllows) ? ignoredAllows.filter((item2) => typeof item2 === "string") : []
|
|
9539
|
+
};
|
|
9540
|
+
};
|
|
9541
|
+
var lastScreenEdit = (ctx) => {
|
|
9542
|
+
const screens = loadLookConfig(ctx.runtime, ctx.paths.root)?.screens ?? DEFAULT_SCREENS;
|
|
9543
|
+
return ctx.changes.filter((change) => change.status !== "D" && matchesAny(change.path, screens)).reduce((latest, change) => {
|
|
9544
|
+
const stat = ctx.runtime.stat(toAbsolutePath(join19(ctx.paths.root, change.path)));
|
|
9545
|
+
return Math.max(latest, stat?.mtimeMs ?? 0);
|
|
9546
|
+
}, 0);
|
|
9547
|
+
};
|
|
9548
|
+
var unopened = (ctx, captures) => {
|
|
9549
|
+
const reads = readLookReads(ctx.runtime, ctx.paths);
|
|
9550
|
+
const edited = lastScreenEdit(ctx);
|
|
9551
|
+
return captures.filter((path) => {
|
|
9552
|
+
const at = reads[path];
|
|
9553
|
+
return at === void 0 || Date.parse(at) < edited;
|
|
9554
|
+
});
|
|
9555
|
+
};
|
|
9556
|
+
var lookCheck = (_check, ctx) => {
|
|
9557
|
+
const summary = readSummary(ctx);
|
|
9558
|
+
if (summary === void 0) return failed([note("gate.look.not_run")]);
|
|
9559
|
+
const ignored = summary.ignored.slice(0, MAX_LISTED2).map((entry2) => note("gate.look.allow_ignored", { entry: entry2 }));
|
|
9560
|
+
if (summary.blocking.length > 0) return failed([...summary.blocking.slice(0, MAX_LISTED2), ...ignored]);
|
|
9561
|
+
if (summary.captures.length === 0) return failed([note("gate.look.no_captures"), ...ignored]);
|
|
9562
|
+
const closed = unopened(ctx, summary.captures);
|
|
9563
|
+
if (closed.length > 0) {
|
|
9564
|
+
return failed([
|
|
9565
|
+
note("gate.look.unopened", { count: closed.length }),
|
|
9566
|
+
...closed.slice(0, MAX_LISTED2).map((file) => note("gate.look.open", { file })),
|
|
9567
|
+
...ignored
|
|
9568
|
+
]);
|
|
9569
|
+
}
|
|
9570
|
+
return passed([note("gate.look.ok", { count: summary.captures.length }), ...ignored]);
|
|
9571
|
+
};
|
|
9572
|
+
|
|
9324
9573
|
// src/gate/checks/manual.ts
|
|
9325
9574
|
var manualCheck = (check2, ctx) => {
|
|
9326
9575
|
const approval = readApprovals(ctx.runtime, ctx.paths).findLast(
|
|
@@ -9339,14 +9588,14 @@ var manualCheck = (check2, ctx) => {
|
|
|
9339
9588
|
};
|
|
9340
9589
|
|
|
9341
9590
|
// src/gate/checks/placeholders.ts
|
|
9342
|
-
import { join as
|
|
9343
|
-
var
|
|
9591
|
+
import { join as join20 } from "node:path";
|
|
9592
|
+
var MAX_LISTED3 = 10;
|
|
9344
9593
|
var SKIP_FILES = /\.(md|mdx|txt|rst|lock|snap|svg|png|jpg|gif|json)$|(^|\/)(CHANGELOG|LICENSE)/i;
|
|
9345
9594
|
var PLACEHOLDER = /\b(TODO|FIXME|XXX)\b|not implemented|NotImplementedError|unimplemented!|todo!\(|lorem ipsum/i;
|
|
9346
9595
|
var findPlaceholders = (lines3) => lines3.filter((line2) => !isTestFile(line2.path) && !SKIP_FILES.test(line2.path) && PLACEHOLDER.test(line2.text));
|
|
9347
9596
|
var untrackedAdded = (ctx) => ctx.changes.filter((change) => change.status === "A" && !SKIP_FILES.test(change.path)).flatMap((change) => {
|
|
9348
|
-
const
|
|
9349
|
-
return
|
|
9597
|
+
const text7 = ctx.runtime.readText(toAbsolutePath(join20(ctx.paths.root, change.path)));
|
|
9598
|
+
return text7.ok ? text7.value.split("\n").map((line2, index) => ({ path: change.path, line: index + 1, text: line2 })) : [];
|
|
9350
9599
|
});
|
|
9351
9600
|
var placeholdersCheck = (_check, ctx) => {
|
|
9352
9601
|
const tracked = addedLines(ctx.git, ctx.base);
|
|
@@ -9354,7 +9603,7 @@ var placeholdersCheck = (_check, ctx) => {
|
|
|
9354
9603
|
const found = findPlaceholders([...tracked, ...untrackedAdded(ctx).filter((line2) => !seen.has(line2.path))]);
|
|
9355
9604
|
if (found.length === 0) return passed([note("gate.placeholders.clean")]);
|
|
9356
9605
|
return failed(
|
|
9357
|
-
found.slice(0,
|
|
9606
|
+
found.slice(0, MAX_LISTED3).map((line2) => note("gate.placeholders.found", { file: line2.path, line: line2.line }))
|
|
9358
9607
|
);
|
|
9359
9608
|
};
|
|
9360
9609
|
|
|
@@ -9405,7 +9654,7 @@ var prOutcome = (check2, ctx) => {
|
|
|
9405
9654
|
|
|
9406
9655
|
// src/prose/preset.ts
|
|
9407
9656
|
var PRESET_LINE = /^preset:\s*([A-Za-z0-9_-]+)\s*$/m;
|
|
9408
|
-
var presetFromConfig = (
|
|
9657
|
+
var presetFromConfig = (text7) => parsePreset(PRESET_LINE.exec(text7)?.[1]) ?? "neutral";
|
|
9409
9658
|
var projectPreset = (runtime, root) => {
|
|
9410
9659
|
const config = runtime.readText(toAbsolutePath(CONFIG_FILE, root));
|
|
9411
9660
|
return config.ok ? presetFromConfig(config.value) : "neutral";
|
|
@@ -9582,16 +9831,16 @@ var ANY_MARKER = /<!--\s*finitemonkey:prose-allow(?:-file)?\s*-->/g;
|
|
|
9582
9831
|
var INLINE_CODE = /`[^`]*`/g;
|
|
9583
9832
|
var FENCE2 = /^\s*(```|~~~)/;
|
|
9584
9833
|
var outsideCode = (line2) => line2.replace(INLINE_CODE, "");
|
|
9585
|
-
var unfencedLines = (
|
|
9834
|
+
var unfencedLines = (text7) => {
|
|
9586
9835
|
let fenced = false;
|
|
9587
|
-
return
|
|
9836
|
+
return text7.split("\n").filter((line2) => {
|
|
9588
9837
|
if (FENCE2.test(line2)) fenced = !fenced;
|
|
9589
9838
|
return !fenced && !FENCE2.test(line2);
|
|
9590
9839
|
});
|
|
9591
9840
|
};
|
|
9592
|
-
var allowsWholeFile = (
|
|
9841
|
+
var allowsWholeFile = (text7) => unfencedLines(text7).some((line2) => FILE_MARKER.test(outsideCode(line2)));
|
|
9593
9842
|
var allowsLine = (line2) => LINE_MARKER.test(outsideCode(line2));
|
|
9594
|
-
var markerCount = (
|
|
9843
|
+
var markerCount = (text7) => text7.match(ANY_MARKER)?.length ?? 0;
|
|
9595
9844
|
var isMarkerOnlyLine = (line2) => allowsLine(line2) && outsideCode(line2).replace(LINE_MARKER, "").trim() === "";
|
|
9596
9845
|
|
|
9597
9846
|
// src/prose/extract.ts
|
|
@@ -9611,7 +9860,7 @@ var MASKS = [
|
|
|
9611
9860
|
[/<[^>]+>/g, ""],
|
|
9612
9861
|
[/\*\*|__/g, ""]
|
|
9613
9862
|
];
|
|
9614
|
-
var maskInline = (line2) => MASKS.reduce((
|
|
9863
|
+
var maskInline = (line2) => MASKS.reduce((text7, [pattern, replacement]) => text7.replace(pattern, replacement), line2);
|
|
9615
9864
|
var BlockReader = class {
|
|
9616
9865
|
blocks = [];
|
|
9617
9866
|
current;
|
|
@@ -9702,8 +9951,8 @@ var splitBlock = (block4) => {
|
|
|
9702
9951
|
return joined.split(SENTENCE_END).flatMap((sentence) => {
|
|
9703
9952
|
const at = joined.indexOf(sentence, offset);
|
|
9704
9953
|
offset = at + sentence.length;
|
|
9705
|
-
const
|
|
9706
|
-
return
|
|
9954
|
+
const text7 = sentence.trim();
|
|
9955
|
+
return text7 === "" ? [] : [{ text: text7, line: lineAt(at) }];
|
|
9707
9956
|
});
|
|
9708
9957
|
};
|
|
9709
9958
|
var unitsOf = (blocks) => {
|
|
@@ -9724,9 +9973,9 @@ var unitsOf = (blocks) => {
|
|
|
9724
9973
|
}));
|
|
9725
9974
|
});
|
|
9726
9975
|
};
|
|
9727
|
-
var extractProse = (
|
|
9728
|
-
if (allowsWholeFile(
|
|
9729
|
-
const lines3 =
|
|
9976
|
+
var extractProse = (text7) => {
|
|
9977
|
+
if (allowsWholeFile(text7)) return { units: [], allowed: true };
|
|
9978
|
+
const lines3 = text7.split("\n").map(withoutCarriageReturn);
|
|
9730
9979
|
return { units: unitsOf(readBlocks(lines3)), allowed: false };
|
|
9731
9980
|
};
|
|
9732
9981
|
|
|
@@ -9851,7 +10100,7 @@ var CONNECTIVES = new Set(
|
|
|
9851
10100
|
" "
|
|
9852
10101
|
)
|
|
9853
10102
|
);
|
|
9854
|
-
var wordsOf = (
|
|
10103
|
+
var wordsOf = (text7) => text7.split(/\s+/).filter((word2) => WORD.test(word2));
|
|
9855
10104
|
var hit = (rule, units, index, weight) => {
|
|
9856
10105
|
const unit = units[index];
|
|
9857
10106
|
return { rule, unit: index, line: unit?.line ?? 0, weight, excerpt: unit?.text ?? "" };
|
|
@@ -9979,8 +10228,8 @@ var mergeSpans = (hits, windows) => {
|
|
|
9979
10228
|
return ranges.map(([from, to]) => spanOf2(hits, from, to)).filter((span) => span.hits.length > 0);
|
|
9980
10229
|
};
|
|
9981
10230
|
var byPosition = (left, right) => left.unit - right.unit || left.line - right.line;
|
|
9982
|
-
var scoreProse = (
|
|
9983
|
-
const { units, allowed: allowed2 } = extractProse(
|
|
10231
|
+
var scoreProse = (text7, options = {}) => {
|
|
10232
|
+
const { units, allowed: allowed2 } = extractProse(text7);
|
|
9984
10233
|
const size = options.window ?? DENSITY_WINDOW;
|
|
9985
10234
|
const threshold2 = options.threshold ?? DENSITY_THRESHOLD;
|
|
9986
10235
|
const catalog = options.context === "record" ? [...phraseRules(), ...recordRules()] : phraseRules();
|
|
@@ -10012,9 +10261,9 @@ var changedMarkdown = (runtime, root) => {
|
|
|
10012
10261
|
var scoreFiles = (runtime, root, files, forced) => {
|
|
10013
10262
|
const reports = [];
|
|
10014
10263
|
for (const file of files) {
|
|
10015
|
-
const
|
|
10016
|
-
if (!
|
|
10017
|
-
reports.push({ file: toPosixPath(file), report: scoreProse(
|
|
10264
|
+
const text7 = runtime.readText(toAbsolutePath(file, root));
|
|
10265
|
+
if (!text7.ok) return reject(`${file}: ${text7.error}`);
|
|
10266
|
+
reports.push({ file: toPosixPath(file), report: scoreProse(text7.value, { context: forced ?? contextOf(file) }) });
|
|
10018
10267
|
}
|
|
10019
10268
|
return accept(reports);
|
|
10020
10269
|
};
|
|
@@ -10031,13 +10280,13 @@ var proseGateStatus = (runtime, root) => {
|
|
|
10031
10280
|
};
|
|
10032
10281
|
|
|
10033
10282
|
// src/gate/checks/prose.ts
|
|
10034
|
-
var
|
|
10283
|
+
var MAX_LISTED4 = 10;
|
|
10035
10284
|
var rulesOf = (span) => [...new Set(span.hits.map((hit3) => hit3.rule))].join(", ");
|
|
10036
10285
|
var proseOutcome = (status) => {
|
|
10037
10286
|
if (!status.ok) return unverified("tool_missing", [note("gate.prose.unavailable")]);
|
|
10038
10287
|
const { files, flagged, blocking: blocking2 } = status.value;
|
|
10039
10288
|
const key = blocking2 ? "gate.prose.finding" : "gate.prose.warning";
|
|
10040
|
-
const notes2 = flagged.flatMap((item2) => item2.report.spans.map((span) => ({ file: item2.file, span }))).slice(0,
|
|
10289
|
+
const notes2 = flagged.flatMap((item2) => item2.report.spans.map((span) => ({ file: item2.file, span }))).slice(0, MAX_LISTED4).map(({ file, span }) => note(key, { file, line: span.startLine, rules: rulesOf(span) }));
|
|
10041
10290
|
if (notes2.length === 0) return passed([note("gate.prose.clean", { count: files.length })]);
|
|
10042
10291
|
return blocking2 ? failed(notes2) : passed(notes2);
|
|
10043
10292
|
};
|
|
@@ -10113,8 +10362,8 @@ var parseAppCheck = (stdout) => {
|
|
|
10113
10362
|
var PROBE_MARGIN_MS = 15e3;
|
|
10114
10363
|
var MIN_REMAINING_MS2 = 5e3;
|
|
10115
10364
|
var TAIL2 = 160;
|
|
10116
|
-
var lastLine = (
|
|
10117
|
-
const rows =
|
|
10365
|
+
var lastLine = (text7) => {
|
|
10366
|
+
const rows = text7.split(/\r?\n/).filter((row) => row.trim() !== "" && !/^\s*at\s|^Node\.js v\d/.test(row));
|
|
10118
10367
|
const line2 = (rows.findLast((row) => /error/i.test(row)) ?? rows.at(-1))?.trim() ?? "";
|
|
10119
10368
|
return line2.length > TAIL2 ? `${line2.slice(0, TAIL2)}…` : line2;
|
|
10120
10369
|
};
|
|
@@ -10192,14 +10441,14 @@ ${run4.stderr}`)) })]);
|
|
|
10192
10441
|
};
|
|
10193
10442
|
|
|
10194
10443
|
// src/secrets/baseline.ts
|
|
10195
|
-
import { join as
|
|
10444
|
+
import { join as join21 } from "node:path";
|
|
10196
10445
|
var BASELINE_FILE2 = ".finitemonkey/findings/baseline.json";
|
|
10197
10446
|
var BASELINE_SCHEMA = "finitemonkey/secrets-baseline@1";
|
|
10198
10447
|
var BASELINE_STATES = ["aceito", "falso-positivo"];
|
|
10199
10448
|
var EXCEPTION_MAX_DAYS = 90;
|
|
10200
10449
|
var EMPTY_BASELINE = { schema: BASELINE_SCHEMA, entries: [] };
|
|
10201
|
-
var baselinePath = (root) => toAbsolutePath(
|
|
10202
|
-
var
|
|
10450
|
+
var baselinePath = (root) => toAbsolutePath(join21(root, BASELINE_FILE2));
|
|
10451
|
+
var text6 = (record3, key) => {
|
|
10203
10452
|
const value = record3[key];
|
|
10204
10453
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
10205
10454
|
};
|
|
@@ -10207,7 +10456,7 @@ var stateOf = (value) => BASELINE_STATES.find((state) => state === value);
|
|
|
10207
10456
|
var parseEntry2 = (value) => {
|
|
10208
10457
|
if (!isRecord(value)) return void 0;
|
|
10209
10458
|
const fields = ["id", "rule", "file", "fingerprint", "owner", "reason", "acceptedAt", "expiresAt"];
|
|
10210
|
-
const [id, rule, file, fingerprint, owner, reason, acceptedAt, expiresAt] = fields.map((key) =>
|
|
10459
|
+
const [id, rule, file, fingerprint, owner, reason, acceptedAt, expiresAt] = fields.map((key) => text6(value, key));
|
|
10211
10460
|
const state = stateOf(value["state"]);
|
|
10212
10461
|
const line2 = value["line"];
|
|
10213
10462
|
if (id === void 0 || rule === void 0 || file === void 0 || fingerprint === void 0) return void 0;
|
|
@@ -10239,13 +10488,13 @@ var serializeBaseline = (baseline) => `${JSON.stringify({ schema: baseline.schem
|
|
|
10239
10488
|
var saveBaseline = (runtime, root, baseline) => runtime.writeText(baselinePath(root), serializeBaseline(baseline));
|
|
10240
10489
|
var ratchet = (findings, baseline, now, inScope) => {
|
|
10241
10490
|
const byId = new Map(baseline.entries.map((entry2) => [entry2.id, entry2]));
|
|
10242
|
-
const seen = new Set(findings.map((
|
|
10243
|
-
const entryOf4 = (
|
|
10491
|
+
const seen = new Set(findings.map((finding4) => finding4.id));
|
|
10492
|
+
const entryOf4 = (finding4) => byId.get(finding4.id);
|
|
10244
10493
|
return {
|
|
10245
|
-
fresh: findings.filter((
|
|
10246
|
-
known: findings.filter((
|
|
10247
|
-
expired: findings.filter((
|
|
10248
|
-
const entry2 = entryOf4(
|
|
10494
|
+
fresh: findings.filter((finding4) => entryOf4(finding4) === void 0),
|
|
10495
|
+
known: findings.filter((finding4) => (entryOf4(finding4)?.expiresAt ?? "") >= now),
|
|
10496
|
+
expired: findings.filter((finding4) => {
|
|
10497
|
+
const entry2 = entryOf4(finding4);
|
|
10249
10498
|
return entry2 !== void 0 && entry2.expiresAt < now;
|
|
10250
10499
|
}),
|
|
10251
10500
|
fixed: inScope === void 0 ? [] : baseline.entries.filter((entry2) => !seen.has(entry2.id) && inScope(entry2.file))
|
|
@@ -10258,14 +10507,14 @@ var withoutFixed = (baseline, fixed) => {
|
|
|
10258
10507
|
var acceptedFingerprints = (baseline, now) => new Set(baseline.entries.filter((entry2) => entry2.expiresAt >= now).map((entry2) => entry2.fingerprint));
|
|
10259
10508
|
|
|
10260
10509
|
// src/secrets/incidents.ts
|
|
10261
|
-
import { join as
|
|
10510
|
+
import { join as join22 } from "node:path";
|
|
10262
10511
|
var INCIDENTS_DIR = ".finitemonkey/incidents";
|
|
10263
10512
|
var INCIDENT_STATES = ["aberto", "exposicao-local", "rotacionado", "fechado"];
|
|
10264
10513
|
var INCIDENT_SOURCES = ["tool-output", "prompt", "history", "scan"];
|
|
10265
10514
|
var ID = /^inc-[0-9a-f]{12}$/;
|
|
10266
10515
|
var incidentId = (rule, fingerprint) => `inc-${sha256Hex(`${rule}\0${fingerprint}`).slice(0, 12)}`;
|
|
10267
|
-
var incidentsDir = (root) => toAbsolutePath(
|
|
10268
|
-
var incidentFile = (root, id) => toAbsolutePath(
|
|
10516
|
+
var incidentsDir = (root) => toAbsolutePath(join22(root, INCIDENTS_DIR));
|
|
10517
|
+
var incidentFile = (root, id) => toAbsolutePath(join22(incidentsDir(root), `${id}.json`));
|
|
10269
10518
|
var pick3 = (options, value) => options.find((option) => option === value);
|
|
10270
10519
|
var str2 = (record3, key) => {
|
|
10271
10520
|
const value = record3[key];
|
|
@@ -10320,7 +10569,7 @@ var listIncidents = (runtime, root) => {
|
|
|
10320
10569
|
return names.value.filter((name) => name.endsWith(".json")).map((name) => readIncident(runtime, root, name.slice(0, -".json".length))).flatMap((parsed) => parsed.ok ? [parsed.value] : []).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
10321
10570
|
};
|
|
10322
10571
|
var saveIncident = (runtime, root, incident) => {
|
|
10323
|
-
const ignore = runtime.writeText(toAbsolutePath(
|
|
10572
|
+
const ignore = runtime.writeText(toAbsolutePath(join22(incidentsDir(root), ".gitignore")), "*\n");
|
|
10324
10573
|
if (!ignore.ok) return ignore;
|
|
10325
10574
|
return runtime.writeText(incidentFile(root, incident.id), `${JSON.stringify(incident, null, 2)}
|
|
10326
10575
|
`);
|
|
@@ -10367,25 +10616,25 @@ var moveIncident = (incident, to, now, by) => ({
|
|
|
10367
10616
|
var isRotated = (incident) => incident.state === "rotacionado" || incident.state === "fechado";
|
|
10368
10617
|
|
|
10369
10618
|
// src/secrets/scan.ts
|
|
10370
|
-
import { join as
|
|
10619
|
+
import { join as join23, relative as relative6 } from "node:path";
|
|
10371
10620
|
|
|
10372
10621
|
// src/secrets/located.ts
|
|
10373
10622
|
var findingId = (ruleId, file, fingerprint) => `fm-${sha256Hex(`${ruleId}\0${file}\0${fingerprint}`).slice(0, 16)}`;
|
|
10374
|
-
var locate = (
|
|
10623
|
+
var locate = (finding4, rawFile, line2, commit2) => {
|
|
10375
10624
|
const file = toPosixPath(rawFile);
|
|
10376
10625
|
return {
|
|
10377
|
-
id: findingId(
|
|
10378
|
-
ruleId:
|
|
10379
|
-
severity:
|
|
10626
|
+
id: findingId(finding4.ruleId, file, finding4.fingerprint),
|
|
10627
|
+
ruleId: finding4.ruleId,
|
|
10628
|
+
severity: finding4.severity,
|
|
10380
10629
|
file,
|
|
10381
10630
|
line: line2,
|
|
10382
|
-
column:
|
|
10383
|
-
fingerprint:
|
|
10384
|
-
tail:
|
|
10631
|
+
column: finding4.column,
|
|
10632
|
+
fingerprint: finding4.fingerprint,
|
|
10633
|
+
tail: finding4.tail,
|
|
10385
10634
|
...commit2 === void 0 ? {} : { commit: commit2 }
|
|
10386
10635
|
};
|
|
10387
10636
|
};
|
|
10388
|
-
var whereOf = (
|
|
10637
|
+
var whereOf = (finding4) => `${finding4.file}:${String(finding4.line)}`;
|
|
10389
10638
|
|
|
10390
10639
|
// src/secrets/diff.ts
|
|
10391
10640
|
var COMMIT = /^commit ([0-9a-f]{7,64})\b/;
|
|
@@ -10400,8 +10649,8 @@ var targetOf = (line2) => {
|
|
|
10400
10649
|
var collect2 = (chunk, commit2, found) => {
|
|
10401
10650
|
const { file } = chunk;
|
|
10402
10651
|
if (file === void 0 || chunk.text.length === 0) return;
|
|
10403
|
-
for (const
|
|
10404
|
-
const located = locate(
|
|
10652
|
+
for (const finding4 of scanText(chunk.text.join("\n"), { path: file })) {
|
|
10653
|
+
const located = locate(finding4, file, chunk.lines[finding4.line - 1] ?? finding4.line, commit2);
|
|
10405
10654
|
found.set(located.id, located);
|
|
10406
10655
|
}
|
|
10407
10656
|
};
|
|
@@ -10457,7 +10706,7 @@ var diffScanner = () => {
|
|
|
10457
10706
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
10458
10707
|
var SKIPPED = /(?:^|\/)(?:\.git|node_modules|\.finitemonkey)\//;
|
|
10459
10708
|
var DIFF_FLAGS = ["-U0", "--no-color", "--no-ext-diff", "--no-renames"];
|
|
10460
|
-
var binary = (
|
|
10709
|
+
var binary = (text7) => text7.slice(0, 8e3).includes("\0");
|
|
10461
10710
|
var listed2 = (runtime, root, target2) => {
|
|
10462
10711
|
const git = runtime.git(["ls-files", "-z", "--cached", "--others", "--exclude-standard", "--", target2], root);
|
|
10463
10712
|
if (git.ok)
|
|
@@ -10467,12 +10716,12 @@ var listed2 = (runtime, root, target2) => {
|
|
|
10467
10716
|
const base = toAbsolutePath(target2, root);
|
|
10468
10717
|
const walked = runtime.walkFiles(base);
|
|
10469
10718
|
if (!walked.ok) return walked;
|
|
10470
|
-
return accept(walked.value.map((name) => toPosixPath(
|
|
10719
|
+
return accept(walked.value.map((name) => toPosixPath(relative6(root, join23(base, toPosixPath(name))))));
|
|
10471
10720
|
};
|
|
10472
10721
|
var scanFile = (runtime, root, file) => {
|
|
10473
|
-
const
|
|
10474
|
-
if (!
|
|
10475
|
-
return scanText(
|
|
10722
|
+
const text7 = runtime.readText(toAbsolutePath(file, root));
|
|
10723
|
+
if (!text7.ok || text7.value.length > MAX_FILE_BYTES || binary(text7.value)) return [];
|
|
10724
|
+
return scanText(text7.value, { path: file }).map((finding4) => locate(finding4, file, finding4.line));
|
|
10476
10725
|
};
|
|
10477
10726
|
var scanPath = (runtime, root, target2) => {
|
|
10478
10727
|
const files = listed2(runtime, root, target2);
|
|
@@ -10482,7 +10731,7 @@ var scanPath = (runtime, root, target2) => {
|
|
|
10482
10731
|
var diffFindings = (diff) => {
|
|
10483
10732
|
const scanner = diffScanner();
|
|
10484
10733
|
for (const line2 of diff.split("\n")) scanner.push(line2);
|
|
10485
|
-
return scanner.end().filter((
|
|
10734
|
+
return scanner.end().filter((finding4) => !SKIPPED.test(finding4.file));
|
|
10486
10735
|
};
|
|
10487
10736
|
var scanStaged = (runtime, root, includeUnstaged = false) => {
|
|
10488
10737
|
const args2 = includeUnstaged ? ["diff", "HEAD", ...DIFF_FLAGS] : ["diff", "--cached", ...DIFF_FLAGS];
|
|
@@ -10495,7 +10744,7 @@ var scanHistory = async (runtime, root) => {
|
|
|
10495
10744
|
const args2 = ["log", "-p", "--all", "--format=commit %H", ...DIFF_FLAGS];
|
|
10496
10745
|
const git = await runtime.gitLines(args2, root, scanner.push);
|
|
10497
10746
|
if (!git.ok) return reject(git.found ? "git log failed" : "git not found");
|
|
10498
|
-
return accept(scanner.end().filter((
|
|
10747
|
+
return accept(scanner.end().filter((finding4) => !SKIPPED.test(finding4.file)));
|
|
10499
10748
|
};
|
|
10500
10749
|
|
|
10501
10750
|
// src/secrets/gate.ts
|
|
@@ -10517,7 +10766,7 @@ var secretsGateStatus = (runtime, root, since) => {
|
|
|
10517
10766
|
};
|
|
10518
10767
|
|
|
10519
10768
|
// src/gate/checks/secrets.ts
|
|
10520
|
-
var
|
|
10769
|
+
var MAX_LISTED5 = 10;
|
|
10521
10770
|
var EPOCH = "1970-01-01T00:00:00.000Z";
|
|
10522
10771
|
var sinceOf = (ctx) => {
|
|
10523
10772
|
const session = readSession(ctx.runtime, ctx.paths);
|
|
@@ -10527,17 +10776,17 @@ var secretsCheck = (_check, ctx) => {
|
|
|
10527
10776
|
const status = secretsGateStatus(ctx.runtime, ctx.paths.root, sinceOf(ctx));
|
|
10528
10777
|
if (!status.ok) return unverified("tool_missing", [note("gate.secrets.unavailable")]);
|
|
10529
10778
|
const { findings, openIncidents } = status.value;
|
|
10530
|
-
const warnings = findings.filter(isWarning).slice(0,
|
|
10779
|
+
const warnings = findings.filter(isWarning).slice(0, MAX_LISTED5).map((finding4) => note("gate.secrets.warning", { file: finding4.file, line: finding4.line, rule: finding4.ruleId }));
|
|
10531
10780
|
const notes2 = [
|
|
10532
|
-
...blocking(findings).slice(0,
|
|
10533
|
-
...openIncidents.slice(0,
|
|
10781
|
+
...blocking(findings).slice(0, MAX_LISTED5).map((finding4) => note("gate.secrets.finding", { file: finding4.file, line: finding4.line, rule: finding4.ruleId })),
|
|
10782
|
+
...openIncidents.slice(0, MAX_LISTED5).map((incident) => note("gate.secrets.incident", { id: incident.id, state: incident.state }))
|
|
10534
10783
|
];
|
|
10535
10784
|
if (notes2.length > 0) return failed([...notes2, ...warnings]);
|
|
10536
10785
|
return passed(warnings.length > 0 ? warnings : [note("gate.secrets.clean")]);
|
|
10537
10786
|
};
|
|
10538
10787
|
|
|
10539
10788
|
// src/gate/checks/tests.ts
|
|
10540
|
-
import { join as
|
|
10789
|
+
import { join as join24 } from "node:path";
|
|
10541
10790
|
|
|
10542
10791
|
// src/guards/tests/tree.ts
|
|
10543
10792
|
var outOfScope = (input, change) => input.testsProtected && input.scopePaths.length > 0 && change.status !== "A" && isTestFile(change.path) && !matchesAny(change.path, input.scopePaths) ? [{ kind: "out_of_scope", path: change.path }] : [];
|
|
@@ -10555,7 +10804,7 @@ var forChange = (input, change) => {
|
|
|
10555
10804
|
var analyzeTree = (input) => input.changes.flatMap((change) => forChange(input, change));
|
|
10556
10805
|
|
|
10557
10806
|
// src/gate/checks/tests.ts
|
|
10558
|
-
var
|
|
10807
|
+
var MAX_LISTED6 = 10;
|
|
10559
10808
|
var violationNote = (violation) => note(`gate.tests.${violation.kind}`, {
|
|
10560
10809
|
file: violation.path,
|
|
10561
10810
|
before: violation.before ?? 0,
|
|
@@ -10569,8 +10818,8 @@ var treeViolations = (ctx) => {
|
|
|
10569
10818
|
changes: relevant,
|
|
10570
10819
|
before: (path) => fileAt(ctx.git, ctx.base, path),
|
|
10571
10820
|
after: (path) => {
|
|
10572
|
-
const
|
|
10573
|
-
return
|
|
10821
|
+
const text7 = ctx.runtime.readText(toAbsolutePath(join24(ctx.paths.root, path)));
|
|
10822
|
+
return text7.ok ? text7.value : void 0;
|
|
10574
10823
|
},
|
|
10575
10824
|
acceptedConfigs: ctx.trust.configs,
|
|
10576
10825
|
scopePaths: ctx.contract.scope.paths,
|
|
@@ -10580,7 +10829,7 @@ var treeViolations = (ctx) => {
|
|
|
10580
10829
|
var testsCheck = (check2, ctx) => {
|
|
10581
10830
|
const violations = treeViolations(ctx);
|
|
10582
10831
|
const grant = violations.length === 0 ? void 0 : activeGrant(ctx.runtime, ctx.paths, "tests", nowMs(ctx.runtime), ctx.sessionId);
|
|
10583
|
-
const notes2 = violations.slice(0,
|
|
10832
|
+
const notes2 = violations.slice(0, MAX_LISTED6).map(violationNote);
|
|
10584
10833
|
if (violations.length > 0 && grant === void 0) return failed([...notes2, note("gate.tests.fix")]);
|
|
10585
10834
|
const released2 = grant === void 0 ? {} : { releasedBy: `person@${grant.grantedAt}` };
|
|
10586
10835
|
const releaseNotes = grant === void 0 ? [] : [...notes2, note("gate.note.released", { guard: "tests", reason: grant.reason })];
|
|
@@ -10590,7 +10839,7 @@ var testsCheck = (check2, ctx) => {
|
|
|
10590
10839
|
};
|
|
10591
10840
|
|
|
10592
10841
|
// src/git/tree.ts
|
|
10593
|
-
import { join as
|
|
10842
|
+
import { join as join25 } from "node:path";
|
|
10594
10843
|
|
|
10595
10844
|
// src/secrets/guards/paths.ts
|
|
10596
10845
|
var SAFE_ENV = /^\.env\.(?:example|sample|template|dist|defaults|schema)$/i;
|
|
@@ -10629,7 +10878,7 @@ var dropSensitive = (runtime, repo, env, options) => {
|
|
|
10629
10878
|
return removed.ok ? accept(void 0) : removed;
|
|
10630
10879
|
};
|
|
10631
10880
|
var withTempIndex = (runtime, repo, options, work) => {
|
|
10632
|
-
const path = toAbsolutePath(
|
|
10881
|
+
const path = toAbsolutePath(join25(repo.gitDir, `finitemonkey-${options.stamp}.index`));
|
|
10633
10882
|
const env = { GIT_INDEX_FILE: path, GIT_OPTIONAL_LOCKS: "0" };
|
|
10634
10883
|
try {
|
|
10635
10884
|
return work(env);
|
|
@@ -10734,10 +10983,10 @@ var bodyLine = (draft, line2) => {
|
|
|
10734
10983
|
else if (line2.startsWith("+")) draft.current.added.push(line2.slice(1));
|
|
10735
10984
|
};
|
|
10736
10985
|
var DIFF_GIT = /^diff --git a\/(.+) b\/(.+)$/;
|
|
10737
|
-
var parseUnifiedDiff = (
|
|
10986
|
+
var parseUnifiedDiff = (text7) => {
|
|
10738
10987
|
const out = [];
|
|
10739
10988
|
let draft;
|
|
10740
|
-
for (const raw of
|
|
10989
|
+
for (const raw of text7.split("\n")) {
|
|
10741
10990
|
const line2 = withoutCarriageReturn(raw);
|
|
10742
10991
|
const start = DIFF_GIT.exec(line2);
|
|
10743
10992
|
if (start !== null) {
|
|
@@ -10881,14 +11130,14 @@ var parsePorcelainV2 = (stdout) => {
|
|
|
10881
11130
|
};
|
|
10882
11131
|
|
|
10883
11132
|
// src/git/merge/dropped.ts
|
|
10884
|
-
import { join as
|
|
11133
|
+
import { join as join26 } from "node:path";
|
|
10885
11134
|
|
|
10886
11135
|
// src/git/merge/lines.ts
|
|
10887
11136
|
var normalizeLine = (line2) => line2.replace(/\r$/, "").trim().replace(/\s*,$/, "");
|
|
10888
11137
|
var WEAK = /^[\p{P}\p{S}\s]+$/u;
|
|
10889
11138
|
var isWeak = (line2) => WEAK.test(line2);
|
|
10890
11139
|
var meaningful = (lines3) => lines3.map(normalizeLine).filter((line2) => line2 !== "");
|
|
10891
|
-
var contentLines = (
|
|
11140
|
+
var contentLines = (text7) => text7 === void 0 ? [] : meaningful(text7.split("\n"));
|
|
10892
11141
|
var countLines = (lines3) => {
|
|
10893
11142
|
const counts = /* @__PURE__ */ new Map();
|
|
10894
11143
|
for (const line2 of lines3) counts.set(line2, (counts.get(line2) ?? 0) + 1);
|
|
@@ -10937,16 +11186,16 @@ var itemOf = (value) => {
|
|
|
10937
11186
|
approvedBy: optionalString(value, "approved_by")
|
|
10938
11187
|
};
|
|
10939
11188
|
};
|
|
10940
|
-
var invalidFinding = (file,
|
|
11189
|
+
var invalidFinding = (file, text7) => ({
|
|
10941
11190
|
kind: "invalid-dropped",
|
|
10942
11191
|
file,
|
|
10943
11192
|
side: void 0,
|
|
10944
11193
|
line: void 0,
|
|
10945
|
-
text:
|
|
11194
|
+
text: text7,
|
|
10946
11195
|
weak: false
|
|
10947
11196
|
});
|
|
10948
|
-
var parseDropped = (
|
|
10949
|
-
const parsed = parseJson(
|
|
11197
|
+
var parseDropped = (text7) => {
|
|
11198
|
+
const parsed = parseJson(text7);
|
|
10950
11199
|
if (!parsed.ok) return parsed;
|
|
10951
11200
|
const list4 = isRecord(parsed.value) ? parsed.value["dropped"] : parsed.value;
|
|
10952
11201
|
if (!Array.isArray(list4)) return reject("expected a list of dropped items");
|
|
@@ -10961,17 +11210,17 @@ var parseDropped = (text6) => {
|
|
|
10961
11210
|
return accept({ items, invalid });
|
|
10962
11211
|
};
|
|
10963
11212
|
var loadDropped = (runtime, root, file) => {
|
|
10964
|
-
const path = toAbsolutePath(file ??
|
|
10965
|
-
const
|
|
10966
|
-
if (!
|
|
10967
|
-
return parseDropped(
|
|
11213
|
+
const path = toAbsolutePath(file ?? join26(root, DROPPED_FILE), root);
|
|
11214
|
+
const text7 = runtime.readText(path);
|
|
11215
|
+
if (!text7.ok) return file === void 0 ? accept({ items: [], invalid: [] }) : reject(`${file}: ${text7.error}`);
|
|
11216
|
+
return parseDropped(text7.value);
|
|
10968
11217
|
};
|
|
10969
|
-
var isDropped = (
|
|
10970
|
-
(item2) => item2.file ===
|
|
11218
|
+
var isDropped = (finding4, items) => items.some(
|
|
11219
|
+
(item2) => item2.file === finding4.file && (item2.side === void 0 || finding4.side === void 0 || item2.side === finding4.side) && (item2.text === void 0 || normalizeLine(item2.text) === finding4.text)
|
|
10971
11220
|
);
|
|
10972
11221
|
|
|
10973
11222
|
// src/git/merge/generated.ts
|
|
10974
|
-
import { join as
|
|
11223
|
+
import { join as join27 } from "node:path";
|
|
10975
11224
|
var LOCKFILES3 = /* @__PURE__ */ new Set([
|
|
10976
11225
|
"package-lock.json",
|
|
10977
11226
|
"npm-shrinkwrap.json",
|
|
@@ -11034,7 +11283,7 @@ var verifyGenerated = (runtime, repo, inputs, paths, generators) => {
|
|
|
11034
11283
|
if (declared.length === 0) return accept([]);
|
|
11035
11284
|
const commit2 = resultCommit(runtime, repo, inputs);
|
|
11036
11285
|
if (!commit2.ok) return commit2;
|
|
11037
|
-
const dir =
|
|
11286
|
+
const dir = join27(repo.gitDir, `finitemonkey-generate-${runtime.now().replace(/[:.]/g, "-")}`);
|
|
11038
11287
|
if (!runtime.git(["worktree", "add", "--detach", dir, commit2.value], repo.root).ok)
|
|
11039
11288
|
return reject("could not check out the merge result");
|
|
11040
11289
|
try {
|
|
@@ -11044,7 +11293,7 @@ var verifyGenerated = (runtime, repo, inputs, paths, generators) => {
|
|
|
11044
11293
|
}
|
|
11045
11294
|
return accept(
|
|
11046
11295
|
declared.flatMap(({ path }) => {
|
|
11047
|
-
const fresh3 = runtime.readText(toAbsolutePath(
|
|
11296
|
+
const fresh3 = runtime.readText(toAbsolutePath(join27(dir, path)));
|
|
11048
11297
|
const merged2 = blobText(runtime, repo, commit2.value, path);
|
|
11049
11298
|
if (fresh3.ok && fresh3.value === merged2) return [];
|
|
11050
11299
|
return [{ kind: "generated-differs", file: path, side: void 0, line: void 0, text: path, weak: false }];
|
|
@@ -11087,13 +11336,13 @@ var removalOf = (ours, theirs, paired) => {
|
|
|
11087
11336
|
var missingAdditions = (hunks, file, elsewhere) => {
|
|
11088
11337
|
const present2 = new Set(file.result);
|
|
11089
11338
|
return hunks.flatMap(({ side, hunk, added }) => {
|
|
11090
|
-
const
|
|
11091
|
-
if (
|
|
11339
|
+
const text7 = added.find((line2) => !isWeak(line2) && !present2.has(line2));
|
|
11340
|
+
if (text7 === void 0 || containsSequence(file.result, added) || elsewhere(added)) return [];
|
|
11092
11341
|
const offset = Math.max(
|
|
11093
11342
|
0,
|
|
11094
|
-
hunk.added.findIndex((line2) => normalizeLine(line2) ===
|
|
11343
|
+
hunk.added.findIndex((line2) => normalizeLine(line2) === text7)
|
|
11095
11344
|
);
|
|
11096
|
-
return [{ kind: "missing-addition", file: file.path, side, line: hunk.newStart + offset, text:
|
|
11345
|
+
return [{ kind: "missing-addition", file: file.path, side, line: hunk.newStart + offset, text: text7, weak: false }];
|
|
11097
11346
|
});
|
|
11098
11347
|
};
|
|
11099
11348
|
var expectations = (file, hunks, removal) => {
|
|
@@ -11166,8 +11415,8 @@ var symbolsOf = (line2) => {
|
|
|
11166
11415
|
...title === void 0 ? [] : [{ name: title, test: true }]
|
|
11167
11416
|
];
|
|
11168
11417
|
};
|
|
11169
|
-
var escapeRegex = (
|
|
11170
|
-
var containsSymbol = (
|
|
11418
|
+
var escapeRegex = (text7) => text7.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11419
|
+
var containsSymbol = (text7, symbol) => symbol.test ? text7.includes(symbol.name) : new RegExp(`(?<![\\w$])${escapeRegex(symbol.name)}(?![\\w$])`).test(text7);
|
|
11171
11420
|
var missingSymbols = (file, side, hunks, present2) => {
|
|
11172
11421
|
const seen = /* @__PURE__ */ new Set();
|
|
11173
11422
|
return hunks.flatMap(
|
|
@@ -11186,12 +11435,12 @@ var created2 = (diff) => diff?.status === "added" || diff?.status === "copied";
|
|
|
11186
11435
|
var resultPathOf = (context, pair) => [pair.ours?.newPath, pair.theirs?.newPath, pair.key].find(
|
|
11187
11436
|
(path) => path !== void 0 && context.resultText(path) !== void 0
|
|
11188
11437
|
);
|
|
11189
|
-
var finding2 = (kind, file, side,
|
|
11438
|
+
var finding2 = (kind, file, side, text7) => ({
|
|
11190
11439
|
kind,
|
|
11191
11440
|
file,
|
|
11192
11441
|
side,
|
|
11193
11442
|
line: void 0,
|
|
11194
|
-
text:
|
|
11443
|
+
text: text7,
|
|
11195
11444
|
weak: false
|
|
11196
11445
|
});
|
|
11197
11446
|
var binaryFindings = (context, pair, path) => {
|
|
@@ -11210,8 +11459,8 @@ var binaryFindings = (context, pair, path) => {
|
|
|
11210
11459
|
return result === expected ? [] : [finding2("binary", file, side, file)];
|
|
11211
11460
|
};
|
|
11212
11461
|
var elsewhereOf = (context) => (lines3) => context.resultFiles().some((path) => containsSequence(contentLines(context.resultText(path)), lines3));
|
|
11213
|
-
var symbolFindings = (context, pair, path,
|
|
11214
|
-
const present2 = (symbol) =>
|
|
11462
|
+
var symbolFindings = (context, pair, path, text7) => {
|
|
11463
|
+
const present2 = (symbol) => text7 !== void 0 && containsSymbol(text7, symbol) || context.resultFiles().some((other) => containsSymbol(context.resultText(other) ?? "", symbol));
|
|
11215
11464
|
return [
|
|
11216
11465
|
...missingSymbols(path, "ours", pair.ours?.hunks ?? [], present2),
|
|
11217
11466
|
...missingSymbols(path, "theirs", pair.theirs?.hunks ?? [], present2)
|
|
@@ -11227,19 +11476,19 @@ var verifyFile = (context, pair) => {
|
|
|
11227
11476
|
const copied = [pair.ours, pair.theirs].find((diff) => diff?.status === "copied");
|
|
11228
11477
|
const baseSource = copied?.oldPath ?? (created2(pair.ours) || created2(pair.theirs) ? void 0 : pair.key);
|
|
11229
11478
|
const baseText = baseSource === void 0 ? void 0 : blobText(context.runtime, context.repo, context.inputs.base, baseSource);
|
|
11230
|
-
const
|
|
11479
|
+
const text7 = path === void 0 ? void 0 : context.resultText(path);
|
|
11231
11480
|
const file = path ?? pair.key;
|
|
11232
11481
|
const lines3 = checkFile(
|
|
11233
11482
|
{
|
|
11234
11483
|
path: file,
|
|
11235
11484
|
base: contentLines(baseText),
|
|
11236
|
-
result: contentLines(
|
|
11485
|
+
result: contentLines(text7),
|
|
11237
11486
|
ours: pair.ours?.hunks ?? [],
|
|
11238
11487
|
theirs: pair.theirs?.hunks ?? []
|
|
11239
11488
|
},
|
|
11240
11489
|
elsewhereOf(context)
|
|
11241
11490
|
);
|
|
11242
|
-
return [...lines3, ...symbolFindings(context, pair, file,
|
|
11491
|
+
return [...lines3, ...symbolFindings(context, pair, file, text7)];
|
|
11243
11492
|
};
|
|
11244
11493
|
|
|
11245
11494
|
// src/git/merge/verify.ts
|
|
@@ -11330,19 +11579,19 @@ var verifyMerge = (runtime, repo, options = {}) => {
|
|
|
11330
11579
|
};
|
|
11331
11580
|
|
|
11332
11581
|
// src/git/ownership.ts
|
|
11333
|
-
import { join as
|
|
11582
|
+
import { join as join28 } from "node:path";
|
|
11334
11583
|
var OWNERSHIP_DIR = ".finitemonkey/git";
|
|
11335
11584
|
var ownerKey = (raw) => {
|
|
11336
11585
|
const cleaned = (raw ?? "").replace(/[^A-Za-z0-9._-]/g, "_").slice(0, 80);
|
|
11337
11586
|
return cleaned === "" ? "unknown" : cleaned;
|
|
11338
11587
|
};
|
|
11339
|
-
var fileOf = (repo, kind, owner) => toAbsolutePath(
|
|
11588
|
+
var fileOf = (repo, kind, owner) => toAbsolutePath(join28(repo.root, OWNERSHIP_DIR, kind, `${ownerKey(owner)}.json`));
|
|
11340
11589
|
var readDirty = (runtime, root) => {
|
|
11341
11590
|
const status = runtime.git(["status", "--porcelain=v2", "-z", "--untracked-files=all"], root);
|
|
11342
11591
|
return status.ok ? parsePorcelainV2(status.stdout) : void 0;
|
|
11343
11592
|
};
|
|
11344
11593
|
var hashDirty = (runtime, repo, entries) => {
|
|
11345
|
-
const present2 = entries.filter((entry2) => runtime.stat(toAbsolutePath(
|
|
11594
|
+
const present2 = entries.filter((entry2) => runtime.stat(toAbsolutePath(join28(repo.root, entry2.path)))?.kind === "file");
|
|
11346
11595
|
const hashed = present2.length === 0 ? void 0 : runtime.gitWith(["hash-object", "--stdin-paths"], repo.root, {
|
|
11347
11596
|
input: `${present2.map((entry2) => entry2.path).join("\n")}
|
|
11348
11597
|
`
|
|
@@ -11359,8 +11608,8 @@ var hashesOf = (value) => isRecord(value) ? Object.fromEntries(
|
|
|
11359
11608
|
Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
|
|
11360
11609
|
) : {};
|
|
11361
11610
|
var readObject = (runtime, path) => {
|
|
11362
|
-
const
|
|
11363
|
-
return
|
|
11611
|
+
const text7 = runtime.readText(path);
|
|
11612
|
+
return text7.ok ? parseJsonObject(text7.value) : text7;
|
|
11364
11613
|
};
|
|
11365
11614
|
var readBaseline2 = (runtime, repo, owner) => {
|
|
11366
11615
|
const parsed = readObject(runtime, fileOf(repo, "baseline", owner));
|
|
@@ -11426,7 +11675,7 @@ var foreignFiles = (runtime, repo, owner, targets2) => {
|
|
|
11426
11675
|
};
|
|
11427
11676
|
|
|
11428
11677
|
// src/git/gate-status.ts
|
|
11429
|
-
var lines = (
|
|
11678
|
+
var lines = (text7) => text7.split("\n").filter((line2) => line2.trim() !== "");
|
|
11430
11679
|
var unpushedSince = (runtime, repo, since) => {
|
|
11431
11680
|
if (lines(runtime.git(["remote"], repo.root).stdout).length === 0) return [];
|
|
11432
11681
|
const result = runtime.git(["rev-list", `${since}..HEAD`, "--not", "--remotes"], repo.root);
|
|
@@ -11449,9 +11698,9 @@ var mergeGateStatus = (runtime, repo, owner) => {
|
|
|
11449
11698
|
const commits = since === void 0 ? [] : mergeCommitsSince(runtime, repo, since);
|
|
11450
11699
|
return [...pending, ...commits.map((commit2) => ({ commit: commit2, report: verifyMerge(runtime, repo, { rev: commit2 }) }))];
|
|
11451
11700
|
};
|
|
11452
|
-
var findingLabel = (
|
|
11453
|
-
const where =
|
|
11454
|
-
return `${where} ${
|
|
11701
|
+
var findingLabel = (finding4) => {
|
|
11702
|
+
const where = finding4.line === void 0 ? finding4.file : `${finding4.file}:${String(finding4.line)}`;
|
|
11703
|
+
return `${where} ${finding4.text === "" ? finding4.kind : `"${finding4.text}"`}`;
|
|
11455
11704
|
};
|
|
11456
11705
|
|
|
11457
11706
|
// src/git/repo.ts
|
|
@@ -11535,10 +11784,10 @@ var repoFacts = (runtime, repo, config = loadProjectConfig(runtime, repo.root))
|
|
|
11535
11784
|
};
|
|
11536
11785
|
|
|
11537
11786
|
// src/gate/checks/worktree.ts
|
|
11538
|
-
var
|
|
11787
|
+
var MAX_LISTED7 = 8;
|
|
11539
11788
|
var listed3 = (items) => {
|
|
11540
|
-
const head = items.slice(0,
|
|
11541
|
-
return items.length >
|
|
11789
|
+
const head = items.slice(0, MAX_LISTED7).join(", ");
|
|
11790
|
+
return items.length > MAX_LISTED7 ? `${head} (+${String(items.length - MAX_LISTED7)})` : head;
|
|
11542
11791
|
};
|
|
11543
11792
|
var repoOf = (ctx) => openRepo(ctx.runtime, ctx.paths.root);
|
|
11544
11793
|
var ownerOf = (ctx) => ownerKey(ctx.sessionId);
|
|
@@ -11597,7 +11846,8 @@ var DEFAULT_CHECKS = {
|
|
|
11597
11846
|
merge: mergeCheck,
|
|
11598
11847
|
review: reviewCheck,
|
|
11599
11848
|
callers: callersCheck,
|
|
11600
|
-
runtime: runtimeCheck
|
|
11849
|
+
runtime: runtimeCheck,
|
|
11850
|
+
look: lookCheck
|
|
11601
11851
|
};
|
|
11602
11852
|
|
|
11603
11853
|
// src/gate/run.ts
|
|
@@ -11791,7 +12041,7 @@ var subagentStopGate = (input) => {
|
|
|
11791
12041
|
};
|
|
11792
12042
|
|
|
11793
12043
|
// src/gate/hooks/subagent.ts
|
|
11794
|
-
import { join as
|
|
12044
|
+
import { join as join29 } from "node:path";
|
|
11795
12045
|
var NO_CONTRACT_AGENTS = /(^|:)(reviewer|verifier|explore|plan|claude-code-guide|statusline-setup)$/i;
|
|
11796
12046
|
var KNOWN_KINDS = /* @__PURE__ */ new Set([
|
|
11797
12047
|
"command",
|
|
@@ -11807,7 +12057,8 @@ var KNOWN_KINDS = /* @__PURE__ */ new Set([
|
|
|
11807
12057
|
"merge",
|
|
11808
12058
|
"review",
|
|
11809
12059
|
"callers",
|
|
11810
|
-
"runtime"
|
|
12060
|
+
"runtime",
|
|
12061
|
+
"look"
|
|
11811
12062
|
]);
|
|
11812
12063
|
var requirementText = (t, check2) => {
|
|
11813
12064
|
const key = `gate.require.${check2.kind}`;
|
|
@@ -11827,13 +12078,13 @@ var subagentStart = (input) => {
|
|
|
11827
12078
|
const { runtime, t } = input;
|
|
11828
12079
|
const active = readState(runtime, paths).active;
|
|
11829
12080
|
if (active === void 0) return { kind: "allow" };
|
|
11830
|
-
const disk = (
|
|
11831
|
-
const
|
|
11832
|
-
return
|
|
12081
|
+
const disk = (relative11) => {
|
|
12082
|
+
const text7 = runtime.readText(toAbsolutePath(join29(paths.root, relative11)));
|
|
12083
|
+
return text7.ok ? text7.value : void 0;
|
|
11833
12084
|
};
|
|
11834
12085
|
const loaded = loadTask(runtime, paths, { disk, base: () => void 0 }, active);
|
|
11835
12086
|
if (!loaded.ok) return { kind: "allow" };
|
|
11836
|
-
const briefing = runtime.stat(toAbsolutePath(
|
|
12087
|
+
const briefing = runtime.stat(toAbsolutePath(join29(paths.dir, "briefing.md"))) !== void 0;
|
|
11837
12088
|
return { kind: "allow", output: contextOutput("SubagentStart", contractFacts(t, loaded.value.contract, briefing)) };
|
|
11838
12089
|
};
|
|
11839
12090
|
|
|
@@ -11846,16 +12097,17 @@ var PORTAO_HANDLERS = {
|
|
|
11846
12097
|
SubagentStart: [subagentStart],
|
|
11847
12098
|
PreCompact: [preCompact],
|
|
11848
12099
|
SessionStart: [sessionStart],
|
|
11849
|
-
ConfigChange: [configChange]
|
|
12100
|
+
ConfigChange: [configChange],
|
|
12101
|
+
PostToolUse: [lookReadTracker]
|
|
11850
12102
|
};
|
|
11851
12103
|
|
|
11852
12104
|
// src/prod/guard.ts
|
|
11853
|
-
import { join as
|
|
12105
|
+
import { join as join31 } from "node:path";
|
|
11854
12106
|
|
|
11855
12107
|
// src/git/command-text.ts
|
|
11856
12108
|
var MAX_SHOWN = 160;
|
|
11857
|
-
var shown = (
|
|
11858
|
-
const flat =
|
|
12109
|
+
var shown = (text7) => {
|
|
12110
|
+
const flat = text7.replace(/\s+/g, " ").trim();
|
|
11859
12111
|
return flat.length > MAX_SHOWN ? `${flat.slice(0, MAX_SHOWN - 3)}...` : flat;
|
|
11860
12112
|
};
|
|
11861
12113
|
var commandText = (command) => shown([command.program, ...command.args].join(" "));
|
|
@@ -12192,7 +12444,7 @@ var remoteHostsOf = (command) => {
|
|
|
12192
12444
|
};
|
|
12193
12445
|
|
|
12194
12446
|
// src/prod/targets.ts
|
|
12195
|
-
import { join as
|
|
12447
|
+
import { join as join30 } from "node:path";
|
|
12196
12448
|
var DEPLOY_FILES = [
|
|
12197
12449
|
"deploy.sh",
|
|
12198
12450
|
"scripts/deploy.sh",
|
|
@@ -12205,9 +12457,9 @@ var SSH_URL = /ssh:\/\/(?:[^@\s/]+@)?([A-Za-z0-9.-]+)/g;
|
|
|
12205
12457
|
var HOST_KEY = /^\s*-?\s*(?:host|hostname|server|ssh_host|deploy_host|remote_host)\s*:\s*["']?([A-Za-z0-9][A-Za-z0-9.-]*)["']?\s*$/i;
|
|
12206
12458
|
var hostKey = (raw) => raw.trim().toLowerCase().replace(/^[^@]*@/, "").replace(/^\[([^\]]+)\].*$/, "$1").replace(/:\d+$/, "").replace(/\.$/, "");
|
|
12207
12459
|
var shellLine = (line2) => line2.replace(/^\s*(?:-\s*)?(?:run:\s*\|?)?/, "");
|
|
12208
|
-
var hostsInText = (
|
|
12209
|
-
const urls = [...
|
|
12210
|
-
const lines3 =
|
|
12460
|
+
var hostsInText = (text7) => {
|
|
12461
|
+
const urls = [...text7.matchAll(SSH_URL)].map((match) => match[1] ?? "");
|
|
12462
|
+
const lines3 = text7.split(/\r?\n/);
|
|
12211
12463
|
const keyed = lines3.flatMap((line2) => {
|
|
12212
12464
|
const match = HOST_KEY.exec(line2);
|
|
12213
12465
|
return match?.[1] === void 0 ? [] : [match[1]];
|
|
@@ -12216,13 +12468,13 @@ var hostsInText = (text6) => {
|
|
|
12216
12468
|
return [...urls, ...keyed, ...commands].filter((host) => host !== "" && !host.includes("$"));
|
|
12217
12469
|
};
|
|
12218
12470
|
var workflowFiles = (runtime, root) => {
|
|
12219
|
-
const listed4 = runtime.listDir(toAbsolutePath(
|
|
12471
|
+
const listed4 = runtime.listDir(toAbsolutePath(join30(root, ".github", "workflows")));
|
|
12220
12472
|
if (!listed4.ok) return [];
|
|
12221
12473
|
return listed4.value.filter((name) => /deploy/i.test(name) && /\.ya?ml$/i.test(name)).map((name) => `.github/workflows/${name}`);
|
|
12222
12474
|
};
|
|
12223
12475
|
var detectedHosts = (runtime, root, files) => files.flatMap((file) => {
|
|
12224
|
-
const
|
|
12225
|
-
return
|
|
12476
|
+
const text7 = runtime.readText(toAbsolutePath(join30(root, file)));
|
|
12477
|
+
return text7.ok ? hostsInText(text7.value) : [];
|
|
12226
12478
|
});
|
|
12227
12479
|
var loadProdTargets = (runtime, root, config) => {
|
|
12228
12480
|
const workflows = workflowFiles(runtime, root);
|
|
@@ -12233,7 +12485,7 @@ var loadProdTargets = (runtime, root, config) => {
|
|
|
12233
12485
|
...detectedHosts(runtime, root, deployFiles)
|
|
12234
12486
|
];
|
|
12235
12487
|
const declaredDeploy = configAt(config, "prod", "deploy");
|
|
12236
|
-
const existing = deployFiles.find((file) => runtime.stat(toAbsolutePath(
|
|
12488
|
+
const existing = deployFiles.find((file) => runtime.stat(toAbsolutePath(join30(root, file))) !== void 0);
|
|
12237
12489
|
return {
|
|
12238
12490
|
hosts: new Set(hosts.map(hostKey).filter((host) => host !== "")),
|
|
12239
12491
|
kubeContexts: configList(config, "prod", "kube_contexts"),
|
|
@@ -12392,8 +12644,8 @@ var prodGuard = (runtime, root, t) => {
|
|
|
12392
12644
|
if (!PROD_PROGRAMS.has(command.program)) return void 0;
|
|
12393
12645
|
const hit3 = prodHit(command, targets2(), isProd);
|
|
12394
12646
|
if (hit3 === void 0) return void 0;
|
|
12395
|
-
const
|
|
12396
|
-
return hit3.effect === "read" ? { kind: "read", hit: hit3, command:
|
|
12647
|
+
const text7 = commandText(command);
|
|
12648
|
+
return hit3.effect === "read" ? { kind: "read", hit: hit3, command: text7 } : blockOf(t, hit3, text7, targets2().deployPath);
|
|
12397
12649
|
}
|
|
12398
12650
|
};
|
|
12399
12651
|
};
|
|
@@ -12410,11 +12662,11 @@ var recordEvidence = (runtime, root, owner, reads) => {
|
|
|
12410
12662
|
] : []
|
|
12411
12663
|
);
|
|
12412
12664
|
if (lines3.length === 0) return;
|
|
12413
|
-
const path = toAbsolutePath(
|
|
12414
|
-
runtime.mkdir(toAbsolutePath(
|
|
12665
|
+
const path = toAbsolutePath(join31(root, EVIDENCE_FILE));
|
|
12666
|
+
runtime.mkdir(toAbsolutePath(join31(root, ".finitemonkey")));
|
|
12415
12667
|
const before = runtime.readText(path);
|
|
12416
|
-
const
|
|
12417
|
-
runtime.writeAtomic(path, `${
|
|
12668
|
+
const text7 = before.ok ? before.value : "";
|
|
12669
|
+
runtime.writeAtomic(path, `${text7}${text7 === "" || text7.endsWith("\n") ? "" : "\n"}${lines3.join("\n")}
|
|
12418
12670
|
`);
|
|
12419
12671
|
};
|
|
12420
12672
|
|
|
@@ -12429,15 +12681,15 @@ var editTexts = (input, file) => {
|
|
|
12429
12681
|
if (!Array.isArray(edits)) return [];
|
|
12430
12682
|
return edits.flatMap((edit) => {
|
|
12431
12683
|
if (!isRecord(edit)) return [];
|
|
12432
|
-
const
|
|
12433
|
-
return
|
|
12684
|
+
const text7 = optionalString(edit, "new_string");
|
|
12685
|
+
return text7 === void 0 ? [] : [{ file, text: text7, whole: false }];
|
|
12434
12686
|
});
|
|
12435
12687
|
};
|
|
12436
12688
|
var writtenTexts = (tool, input) => {
|
|
12437
12689
|
const file = optionalString(input, "file_path") ?? optionalString(input, "notebook_path") ?? "";
|
|
12438
12690
|
const single2 = (key, whole) => {
|
|
12439
|
-
const
|
|
12440
|
-
return
|
|
12691
|
+
const text7 = optionalString(input, key);
|
|
12692
|
+
return text7 === void 0 ? [] : [{ file, text: text7, whole }];
|
|
12441
12693
|
};
|
|
12442
12694
|
switch (tool) {
|
|
12443
12695
|
case "Write":
|
|
@@ -13119,24 +13371,24 @@ var withChain = (facts, chain) => {
|
|
|
13119
13371
|
const branch = chain?.branch;
|
|
13120
13372
|
return branch === void 0 ? facts : { ...facts, currentBranch: () => branch };
|
|
13121
13373
|
};
|
|
13122
|
-
var decide = (context, repo, verdict, facts,
|
|
13374
|
+
var decide = (context, repo, verdict, facts, text7) => {
|
|
13123
13375
|
switch (verdict.kind) {
|
|
13124
13376
|
case "pass":
|
|
13125
13377
|
return { kind: "pass" };
|
|
13126
13378
|
case "block":
|
|
13127
|
-
return { kind: "block", rule: verdict.rule, subject: verdict.subject, command:
|
|
13379
|
+
return { kind: "block", rule: verdict.rule, subject: verdict.subject, command: text7 };
|
|
13128
13380
|
case "snapshot":
|
|
13129
|
-
return { kind: "snapshot", repo, op: verdict.op, command:
|
|
13381
|
+
return { kind: "snapshot", repo, op: verdict.op, command: text7 };
|
|
13130
13382
|
case "discard": {
|
|
13131
13383
|
const entries = facts.dirty();
|
|
13132
|
-
if (entries === void 0) return { kind: "unknown", command:
|
|
13384
|
+
if (entries === void 0) return { kind: "unknown", command: text7 };
|
|
13133
13385
|
const foreign = foreignFiles(
|
|
13134
13386
|
context.runtime,
|
|
13135
13387
|
repo,
|
|
13136
13388
|
context.owner,
|
|
13137
13389
|
targetsOf(entries, verdict.discard, facts.prefix)
|
|
13138
13390
|
);
|
|
13139
|
-
return foreign.length > 0 ? { kind: "foreign", files: foreign, command:
|
|
13391
|
+
return foreign.length > 0 ? { kind: "foreign", files: foreign, command: text7 } : { kind: "snapshot", repo, op: verdict.op, command: text7 };
|
|
13140
13392
|
}
|
|
13141
13393
|
}
|
|
13142
13394
|
};
|
|
@@ -13223,10 +13475,10 @@ var listWorktrees = (runtime, cwd) => {
|
|
|
13223
13475
|
};
|
|
13224
13476
|
|
|
13225
13477
|
// src/git/journal.ts
|
|
13226
|
-
import { join as
|
|
13478
|
+
import { join as join32 } from "node:path";
|
|
13227
13479
|
var JOURNAL_FILE = ".finitemonkey/git-journal.jsonl";
|
|
13228
13480
|
var JOURNAL_SCHEMA = "finitemonkey/git-journal@1";
|
|
13229
|
-
var journalPath = (root) => toAbsolutePath(
|
|
13481
|
+
var journalPath = (root) => toAbsolutePath(join32(root, JOURNAL_FILE));
|
|
13230
13482
|
var headsOf = (value) => isRecord(value) ? Object.fromEntries(
|
|
13231
13483
|
Object.entries(value).filter((entry2) => typeof entry2[1] === "string")
|
|
13232
13484
|
) : {};
|
|
@@ -13253,16 +13505,16 @@ var parseJournalLine = (line2) => {
|
|
|
13253
13505
|
};
|
|
13254
13506
|
};
|
|
13255
13507
|
var readJournal = (runtime, root) => {
|
|
13256
|
-
const
|
|
13257
|
-
if (!
|
|
13258
|
-
return
|
|
13508
|
+
const text7 = runtime.readText(journalPath(root));
|
|
13509
|
+
if (!text7.ok) return [];
|
|
13510
|
+
return text7.value.split("\n").flatMap((line2) => {
|
|
13259
13511
|
const entry2 = line2.trim() === "" ? void 0 : parseJournalLine(line2);
|
|
13260
13512
|
return entry2 === void 0 ? [] : [entry2];
|
|
13261
13513
|
});
|
|
13262
13514
|
};
|
|
13263
13515
|
var appendJournal = (runtime, root, entry2) => {
|
|
13264
13516
|
const path = journalPath(root);
|
|
13265
|
-
const made = runtime.mkdir(toAbsolutePath(
|
|
13517
|
+
const made = runtime.mkdir(toAbsolutePath(join32(root, ".finitemonkey")));
|
|
13266
13518
|
if (!made.ok) return made;
|
|
13267
13519
|
const previous = runtime.readText(path);
|
|
13268
13520
|
const before = previous.ok ? previous.value : "";
|
|
@@ -13446,9 +13698,9 @@ var review = (input, commands, owner) => {
|
|
|
13446
13698
|
return { blocked: void 0, snapshots, reads };
|
|
13447
13699
|
};
|
|
13448
13700
|
var gitPreToolUse = (input) => {
|
|
13449
|
-
const
|
|
13450
|
-
if (
|
|
13451
|
-
const commands = normalizeCommand(
|
|
13701
|
+
const text7 = shellText(input.payload);
|
|
13702
|
+
if (text7 === void 0) return ALLOW;
|
|
13703
|
+
const commands = normalizeCommand(text7, dialectOf(toolName2(input.payload))).commands;
|
|
13452
13704
|
if (commands.length === 0) return ALLOW;
|
|
13453
13705
|
const owner = ownerOf2(input.payload);
|
|
13454
13706
|
const result = review(input, commands, owner);
|
|
@@ -13465,7 +13717,7 @@ var gitPreToolUse = (input) => {
|
|
|
13465
13717
|
};
|
|
13466
13718
|
|
|
13467
13719
|
// src/git/hooks/session.ts
|
|
13468
|
-
import { dirname as dirname6, relative as
|
|
13720
|
+
import { dirname as dirname6, relative as relative7 } from "node:path";
|
|
13469
13721
|
var ALLOW2 = { kind: "allow" };
|
|
13470
13722
|
var EDIT_TOOLS = /* @__PURE__ */ new Set(["Edit", "Write", "MultiEdit", "NotebookEdit"]);
|
|
13471
13723
|
var gitSessionStart = (input) => {
|
|
@@ -13480,7 +13732,7 @@ var editedPath = (input) => {
|
|
|
13480
13732
|
const file = toAbsolutePath(raw, input.cwd);
|
|
13481
13733
|
const repo = openRepo(input.runtime, toAbsolutePath(dirname6(file))) ?? openRepo(input.runtime, input.cwd);
|
|
13482
13734
|
if (repo === void 0) return void 0;
|
|
13483
|
-
const path = toPosixPath(
|
|
13735
|
+
const path = toPosixPath(relative7(input.runtime.realpath(repo.root), input.runtime.realpath(file)));
|
|
13484
13736
|
return path === "" || path.startsWith("..") || path.startsWith(STATE_PREFIX) ? void 0 : { repo, path };
|
|
13485
13737
|
};
|
|
13486
13738
|
var gitPostToolUse = (input) => {
|
|
@@ -13508,8 +13760,8 @@ var GIT_HANDLERS = {
|
|
|
13508
13760
|
var SHADOW_FILE = "shadow.json";
|
|
13509
13761
|
var numberOf = (value) => typeof value === "number" && Number.isInteger(value) ? value : 0;
|
|
13510
13762
|
var readShadow = (runtime, paths) => {
|
|
13511
|
-
const
|
|
13512
|
-
const parsed =
|
|
13763
|
+
const text7 = runtime.readText(paths.cache(SHADOW_FILE));
|
|
13764
|
+
const parsed = text7.ok ? parseJsonObject(text7.value) : void 0;
|
|
13513
13765
|
if (parsed?.ok !== true) return {};
|
|
13514
13766
|
return Object.fromEntries(
|
|
13515
13767
|
Object.entries(parsed.value).flatMap(
|
|
@@ -13583,7 +13835,7 @@ var withPolicy = (set) => {
|
|
|
13583
13835
|
};
|
|
13584
13836
|
|
|
13585
13837
|
// src/prose/marker-guard.ts
|
|
13586
|
-
import { relative as
|
|
13838
|
+
import { relative as relative8 } from "node:path";
|
|
13587
13839
|
|
|
13588
13840
|
// src/prose/shell-words.ts
|
|
13589
13841
|
var HEREDOC2 = /<<-?[ \t]*(['"]?)([A-Za-z_][A-Za-z0-9_]*)\1[^\n]*\n([\s\S]*?)\n[ \t]*\2[ \t]*(?=\n|$)/g;
|
|
@@ -13593,11 +13845,11 @@ var ASSIGNMENT3 = /^[A-Za-z_][A-Za-z0-9_]*=/;
|
|
|
13593
13845
|
var WRAPPERS2 = /* @__PURE__ */ new Set(["sudo", "command", "exec", "time", "nice", "nohup", "builtin", "env"]);
|
|
13594
13846
|
var liftHeredocs = (command) => {
|
|
13595
13847
|
const heredocs = [];
|
|
13596
|
-
const
|
|
13848
|
+
const text7 = command.replace(HEREDOC2, (_match, _quote, _tag, body2) => {
|
|
13597
13849
|
heredocs.push(body2);
|
|
13598
13850
|
return `<<__FM_HEREDOC_${String(heredocs.length - 1)}__`;
|
|
13599
13851
|
});
|
|
13600
|
-
return { text:
|
|
13852
|
+
return { text: text7, heredocs };
|
|
13601
13853
|
};
|
|
13602
13854
|
var endWord = (scan) => {
|
|
13603
13855
|
if (scan.word !== void 0) scan.words.push(scan.word);
|
|
@@ -13608,23 +13860,23 @@ var endSegment = (scan) => {
|
|
|
13608
13860
|
if (scan.words.length > 0) scan.segments.push(scan.words);
|
|
13609
13861
|
scan.words = [];
|
|
13610
13862
|
};
|
|
13611
|
-
var quotedEnd2 = (
|
|
13863
|
+
var quotedEnd2 = (text7, from, quote) => {
|
|
13612
13864
|
let index = from;
|
|
13613
|
-
while (index <
|
|
13865
|
+
while (index < text7.length && text7[index] !== quote) index += quote === '"' && text7[index] === "\\" ? 2 : 1;
|
|
13614
13866
|
return index;
|
|
13615
13867
|
};
|
|
13616
13868
|
var unescape = (value) => value.replace(/\\(["\\$`])/g, "$1");
|
|
13617
|
-
var splitWords = (
|
|
13869
|
+
var splitWords = (text7) => {
|
|
13618
13870
|
const scan = { words: [], segments: [], word: void 0 };
|
|
13619
|
-
for (let index = 0; index <
|
|
13620
|
-
const char =
|
|
13871
|
+
for (let index = 0; index < text7.length; index += 1) {
|
|
13872
|
+
const char = text7[index] ?? "";
|
|
13621
13873
|
if (char === '"' || char === "'") {
|
|
13622
|
-
const end = quotedEnd2(
|
|
13623
|
-
const inner =
|
|
13874
|
+
const end = quotedEnd2(text7, index + 1, char);
|
|
13875
|
+
const inner = text7.slice(index + 1, end);
|
|
13624
13876
|
scan.word = (scan.word ?? "") + (char === '"' ? unescape(inner) : inner);
|
|
13625
13877
|
index = end;
|
|
13626
|
-
} else if (char === "\\" && index + 1 <
|
|
13627
|
-
scan.word = (scan.word ?? "") + (
|
|
13878
|
+
} else if (char === "\\" && index + 1 < text7.length) {
|
|
13879
|
+
scan.word = (scan.word ?? "") + (text7[index + 1] === "\n" ? "" : text7[index + 1] ?? "");
|
|
13628
13880
|
index += 1;
|
|
13629
13881
|
} else if (SEPARATOR.test(char)) {
|
|
13630
13882
|
endSegment(scan);
|
|
@@ -13645,8 +13897,8 @@ var segmentOf = (words) => {
|
|
|
13645
13897
|
return { program: program.split(/[\\/]/).pop() ?? program, args: words.slice(index + 1) };
|
|
13646
13898
|
};
|
|
13647
13899
|
var shellWords = (command) => {
|
|
13648
|
-
const { text:
|
|
13649
|
-
const segments = splitWords(
|
|
13900
|
+
const { text: text7, heredocs } = liftHeredocs(command);
|
|
13901
|
+
const segments = splitWords(text7).flatMap((words) => {
|
|
13650
13902
|
const segment = segmentOf(words);
|
|
13651
13903
|
return segment === void 0 ? [] : [segment];
|
|
13652
13904
|
});
|
|
@@ -13690,7 +13942,7 @@ var addsMarker = (input, tool, inputs, file) => {
|
|
|
13690
13942
|
var isReadOnly = (program, args2) => READ_ONLY.has(program) || program === "git" && GIT_READ_ONLY.has(args2[0] ?? "");
|
|
13691
13943
|
var shellAddsMarker = (command) => markerCount(command) > 0 && shellWords(command).segments.some((segment) => !isReadOnly(segment.program, segment.args));
|
|
13692
13944
|
var shown2 = (cwd, file) => {
|
|
13693
|
-
const rel = toPosixPath(
|
|
13945
|
+
const rel = toPosixPath(relative8(cwd, file));
|
|
13694
13946
|
return file === "" || rel.startsWith("..") ? toPosixPath(file) : rel;
|
|
13695
13947
|
};
|
|
13696
13948
|
var blocked = (input, file) => ({
|
|
@@ -13746,7 +13998,7 @@ var sourceOf = (value, isFile, segment, heredocs) => {
|
|
|
13746
13998
|
if (inline3 !== void 0) return { kind: "inline", text: inline3 };
|
|
13747
13999
|
if (!isFile) return { kind: "inline", text: value };
|
|
13748
14000
|
if (value !== "-") return { kind: "file", path: value };
|
|
13749
|
-
const piped = segment.args.map((arg) => heredocIn(arg, heredocs)).find((
|
|
14001
|
+
const piped = segment.args.map((arg) => heredocIn(arg, heredocs)).find((text7) => text7 !== void 0);
|
|
13750
14002
|
return { kind: "inline", text: piped ?? "" };
|
|
13751
14003
|
};
|
|
13752
14004
|
var collect3 = (target2, segment, args2, spec2, heredocs) => {
|
|
@@ -13843,11 +14095,11 @@ var textOf2 = (input, found) => {
|
|
|
13843
14095
|
return accept(parts.join("\n\n"));
|
|
13844
14096
|
};
|
|
13845
14097
|
var decisionFor = (input, found, blocking2) => {
|
|
13846
|
-
const
|
|
13847
|
-
if (!
|
|
13848
|
-
return blocking2 ? { kind: "block", reason: input.t("prose.block.unreadable", { ...
|
|
14098
|
+
const text7 = textOf2(input, found);
|
|
14099
|
+
if (!text7.ok) {
|
|
14100
|
+
return blocking2 ? { kind: "block", reason: input.t("prose.block.unreadable", { ...text7.error }) } : void 0;
|
|
13849
14101
|
}
|
|
13850
|
-
const report2 = scoreProse(
|
|
14102
|
+
const report2 = scoreProse(text7.value, { context: "record" });
|
|
13851
14103
|
if (report2.allowed || report2.spans.length === 0) return void 0;
|
|
13852
14104
|
const params2 = {
|
|
13853
14105
|
target: input.t(TARGET_KEYS[found.target]),
|
|
@@ -13876,27 +14128,27 @@ var PROSE_HANDLERS = {
|
|
|
13876
14128
|
};
|
|
13877
14129
|
|
|
13878
14130
|
// src/secrets/guards/describe.ts
|
|
13879
|
-
var
|
|
14131
|
+
var MAX_LISTED8 = 5;
|
|
13880
14132
|
var describeFindings = (t, items) => {
|
|
13881
|
-
const listed4 = items.slice(0,
|
|
14133
|
+
const listed4 = items.slice(0, MAX_LISTED8).map(({ ruleId, where }) => t("secrets.found_item", { rule: ruleId, where }));
|
|
13882
14134
|
const rest = items.length - listed4.length;
|
|
13883
14135
|
return rest > 0 ? `${listed4.join("; ")}; ${t("secrets.found_more", { count: rest })}` : listed4.join("; ");
|
|
13884
14136
|
};
|
|
13885
|
-
var lineWhere = (t,
|
|
14137
|
+
var lineWhere = (t, finding4) => t("secrets.where.line", { line: finding4.line, column: finding4.column });
|
|
13886
14138
|
|
|
13887
14139
|
// src/secrets/guards/exposure.ts
|
|
13888
14140
|
var HOOK_ACTOR = "finitemonkey";
|
|
13889
|
-
var liveFindings = (input,
|
|
14141
|
+
var liveFindings = (input, texts2) => {
|
|
13890
14142
|
const baseline = loadBaseline(input.runtime, input.cwd);
|
|
13891
14143
|
const accepted2 = acceptedFingerprints(baseline.ok ? baseline.value : EMPTY_BASELINE, input.runtime.now());
|
|
13892
|
-
return
|
|
14144
|
+
return texts2.flatMap((text7) => scanText(text7, { rules: CREDENTIAL_RULES })).filter((finding4) => !accepted2.has(finding4.fingerprint));
|
|
13893
14145
|
};
|
|
13894
14146
|
var record2 = (input, findings, draft, where) => {
|
|
13895
|
-
const ids2 = findings.map((
|
|
14147
|
+
const ids2 = findings.map((finding4) => {
|
|
13896
14148
|
const saved = recordIncident(
|
|
13897
14149
|
input.runtime,
|
|
13898
14150
|
input.cwd,
|
|
13899
|
-
{ ...draft, rule:
|
|
14151
|
+
{ ...draft, rule: finding4.ruleId, fingerprint: finding4.fingerprint, where: `${where}:${String(finding4.line)}` },
|
|
13900
14152
|
HOOK_ACTOR
|
|
13901
14153
|
);
|
|
13902
14154
|
return saved.ok ? saved.value.id : input.t("secrets.incident.unsaved", { detail: saved.error });
|
|
@@ -13908,7 +14160,7 @@ var secretsUserPromptSubmit = (input) => {
|
|
|
13908
14160
|
const findings = liveFindings(input, [prompt]);
|
|
13909
14161
|
if (findings.length === 0) return { kind: "allow" };
|
|
13910
14162
|
const incident = record2(input, findings, { source: "prompt", state: "exposicao-local" }, "prompt");
|
|
13911
|
-
const items = findings.map((
|
|
14163
|
+
const items = findings.map((finding4) => ({ ruleId: finding4.ruleId, where: lineWhere(input.t, finding4) }));
|
|
13912
14164
|
return {
|
|
13913
14165
|
kind: "block",
|
|
13914
14166
|
reason: input.t("secrets.block.prompt", { found: describeFindings(input.t, items), incident })
|
|
@@ -13919,7 +14171,7 @@ var secretsPostToolUse = (input) => {
|
|
|
13919
14171
|
if (findings.length === 0) return { kind: "allow" };
|
|
13920
14172
|
const tool = toolName2(input.payload) || "tool";
|
|
13921
14173
|
const incident = record2(input, findings, { source: "tool-output", state: "aberto" }, `tool-output:${tool}`);
|
|
13922
|
-
const items = findings.map((
|
|
14174
|
+
const items = findings.map((finding4) => ({ ruleId: finding4.ruleId, where: lineWhere(input.t, finding4) }));
|
|
13923
14175
|
const context = input.t("secrets.post.context", { tool, found: describeFindings(input.t, items), incident });
|
|
13924
14176
|
return {
|
|
13925
14177
|
kind: "allow",
|
|
@@ -13928,7 +14180,7 @@ var secretsPostToolUse = (input) => {
|
|
|
13928
14180
|
};
|
|
13929
14181
|
|
|
13930
14182
|
// src/secrets/guards/pre-tool.ts
|
|
13931
|
-
import { relative as
|
|
14183
|
+
import { relative as relative9 } from "node:path";
|
|
13932
14184
|
|
|
13933
14185
|
// src/secrets/guards/bash-rules.ts
|
|
13934
14186
|
var READERS = /* @__PURE__ */ new Set([
|
|
@@ -14025,7 +14277,7 @@ var commitIntent = (segments) => {
|
|
|
14025
14277
|
var ALLOW5 = { kind: "allow" };
|
|
14026
14278
|
var relativeFile = (cwd, file) => {
|
|
14027
14279
|
if (file === "") return file;
|
|
14028
|
-
const rel =
|
|
14280
|
+
const rel = relative9(cwd, file).split("\\").join("/");
|
|
14029
14281
|
return rel.startsWith("..") || rel === "" ? file : rel;
|
|
14030
14282
|
};
|
|
14031
14283
|
var warningDecision = (t, warnings) => {
|
|
@@ -14040,38 +14292,38 @@ var writeDecision = (input, tool) => {
|
|
|
14040
14292
|
const file = relativeFile(input.cwd, written.file);
|
|
14041
14293
|
const all = scanText(written.text, { path: file });
|
|
14042
14294
|
warnings.push(
|
|
14043
|
-
...all.filter(isWarning).map((
|
|
14044
|
-
ruleId:
|
|
14045
|
-
where: t("secrets.where.file", { file, line:
|
|
14295
|
+
...all.filter(isWarning).map((finding4) => ({
|
|
14296
|
+
ruleId: finding4.ruleId,
|
|
14297
|
+
where: t("secrets.where.file", { file, line: finding4.line, column: finding4.column })
|
|
14046
14298
|
}))
|
|
14047
14299
|
);
|
|
14048
14300
|
const found = blocking(all);
|
|
14049
14301
|
if (found.length === 0) continue;
|
|
14050
|
-
const items = found.map((
|
|
14051
|
-
ruleId:
|
|
14052
|
-
where: written.whole ? t("secrets.where.file", { file, line:
|
|
14302
|
+
const items = found.map((finding4) => ({
|
|
14303
|
+
ruleId: finding4.ruleId,
|
|
14304
|
+
where: written.whole ? t("secrets.where.file", { file, line: finding4.line, column: finding4.column }) : t("secrets.where.new_text", { file, line: finding4.line })
|
|
14053
14305
|
}));
|
|
14054
14306
|
return { kind: "block", reason: t("secrets.block.write", { file, found: describeFindings(t, items) }) };
|
|
14055
14307
|
}
|
|
14056
14308
|
return warningDecision(t, warnings);
|
|
14057
14309
|
};
|
|
14058
|
-
var bashMessage = (t,
|
|
14059
|
-
switch (
|
|
14310
|
+
var bashMessage = (t, finding4) => {
|
|
14311
|
+
switch (finding4.kind) {
|
|
14060
14312
|
case "read":
|
|
14061
|
-
return t("secrets.block.read", { target:
|
|
14313
|
+
return t("secrets.block.read", { target: finding4.target });
|
|
14062
14314
|
case "env-dump":
|
|
14063
|
-
return t("secrets.block.env_dump", { command:
|
|
14315
|
+
return t("secrets.block.env_dump", { command: finding4.command });
|
|
14064
14316
|
case "person-only":
|
|
14065
|
-
return t("secrets.block.person_only", { command:
|
|
14317
|
+
return t("secrets.block.person_only", { command: finding4.command });
|
|
14066
14318
|
}
|
|
14067
14319
|
};
|
|
14068
14320
|
var commitDecision = (input, includeUnstaged) => {
|
|
14069
14321
|
const staged = scanStaged(input.runtime, input.cwd, includeUnstaged);
|
|
14070
14322
|
const found = staged.ok ? blocking(staged.value) : [];
|
|
14071
14323
|
if (found.length === 0) return ALLOW5;
|
|
14072
|
-
const items = found.map((
|
|
14073
|
-
ruleId:
|
|
14074
|
-
where: input.t("secrets.where.file", { file:
|
|
14324
|
+
const items = found.map((finding4) => ({
|
|
14325
|
+
ruleId: finding4.ruleId,
|
|
14326
|
+
where: input.t("secrets.where.file", { file: finding4.file, line: finding4.line, column: finding4.column })
|
|
14075
14327
|
}));
|
|
14076
14328
|
return { kind: "block", reason: input.t("secrets.block.commit", { found: describeFindings(input.t, items) }) };
|
|
14077
14329
|
};
|
|
@@ -14083,7 +14335,7 @@ var bashDecision = (input) => {
|
|
|
14083
14335
|
if (first !== void 0) return { kind: "block", reason: bashMessage(input.t, first) };
|
|
14084
14336
|
const found = blocking(scanText(command));
|
|
14085
14337
|
if (found.length > 0) {
|
|
14086
|
-
const items = found.map((
|
|
14338
|
+
const items = found.map((finding4) => ({ ruleId: finding4.ruleId, where: lineWhere(input.t, finding4) }));
|
|
14087
14339
|
return { kind: "block", reason: input.t("secrets.block.bash", { found: describeFindings(input.t, items) }) };
|
|
14088
14340
|
}
|
|
14089
14341
|
const commit2 = commitIntent(segments);
|
|
@@ -14134,12 +14386,12 @@ var secretsSessionStart = (input) => {
|
|
|
14134
14386
|
const remotes = remotesWithCredentials(input.runtime, input.cwd);
|
|
14135
14387
|
if (remotes.length === 0) return { kind: "allow" };
|
|
14136
14388
|
const params2 = { remotes: remotes.join(", "), remote: remotes[0] ?? "origin" };
|
|
14137
|
-
const
|
|
14389
|
+
const text7 = input.t("secrets.session.remote_credential", params2);
|
|
14138
14390
|
return {
|
|
14139
14391
|
kind: "allow",
|
|
14140
14392
|
output: {
|
|
14141
|
-
systemMessage: `finitemonkey: ${
|
|
14142
|
-
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext:
|
|
14393
|
+
systemMessage: `finitemonkey: ${text7}`,
|
|
14394
|
+
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: text7 }
|
|
14143
14395
|
}
|
|
14144
14396
|
};
|
|
14145
14397
|
};
|
|
@@ -14216,9 +14468,9 @@ var EMPTY_PAYLOAD = {
|
|
|
14216
14468
|
stopHookActive: false,
|
|
14217
14469
|
raw: {}
|
|
14218
14470
|
};
|
|
14219
|
-
var parseHookPayload = (event,
|
|
14220
|
-
if (interactive &&
|
|
14221
|
-
const parsed = parseJsonObject(
|
|
14471
|
+
var parseHookPayload = (event, text7, interactive) => {
|
|
14472
|
+
if (interactive && text7.trim() === "") return accept(EMPTY_PAYLOAD);
|
|
14473
|
+
const parsed = parseJsonObject(text7);
|
|
14222
14474
|
if (!parsed.ok) return reject(`stdin: ${parsed.error}`);
|
|
14223
14475
|
const payload = {
|
|
14224
14476
|
hookEventName: optionalString(parsed.value, "hook_event_name"),
|
|
@@ -14278,8 +14530,8 @@ var HOOK_PROBE_ENV = "FINITEMONKEY_HOOK_PROBE";
|
|
|
14278
14530
|
var messageOf2 = (error) => error instanceof Error ? error.message : String(error);
|
|
14279
14531
|
var decide2 = (input, handlers) => composeDecisions(input, handlers[input.event] ?? [allow]);
|
|
14280
14532
|
var evaluate = async (event, options) => {
|
|
14281
|
-
const
|
|
14282
|
-
const parsed = parseHookPayload(event,
|
|
14533
|
+
const text7 = await readAll(options.stdin);
|
|
14534
|
+
const parsed = parseHookPayload(event, text7, options.stdin.isTTY === true);
|
|
14283
14535
|
if (!parsed.ok) return { kind: "failed", payload: void 0, detail: parsed.error };
|
|
14284
14536
|
const payload = parsed.value;
|
|
14285
14537
|
if (options.env?.[HOOK_PROBE_ENV] === "1") return { kind: "decided", payload, decision: { kind: "allow" } };
|
|
@@ -14356,7 +14608,7 @@ var runHook = async (event, options) => {
|
|
|
14356
14608
|
var PROBE_TIMEOUT_MS2 = 1e4;
|
|
14357
14609
|
var SLOW_HOOK_MS = 1500;
|
|
14358
14610
|
var DEFAULT_TIMEOUT_S = 60;
|
|
14359
|
-
var expand = (
|
|
14611
|
+
var expand = (text7, vars) => text7.replace(/\$\{(\w+)\}|\$(\w+)/g, (match, braced, bare) => {
|
|
14360
14612
|
const name = braced ?? bare ?? "";
|
|
14361
14613
|
return vars[name] ?? match;
|
|
14362
14614
|
});
|
|
@@ -14405,7 +14657,7 @@ var run2 = (exec, invocation, cwd, env, input) => exec({
|
|
|
14405
14657
|
env: { ...env, [HOOK_PROBE_ENV]: "1" },
|
|
14406
14658
|
...input === void 0 ? {} : { input }
|
|
14407
14659
|
});
|
|
14408
|
-
var firstLine = (
|
|
14660
|
+
var firstLine = (text7) => maskText(text7.trim().split("\n")[0] ?? "").slice(0, 160);
|
|
14409
14661
|
var classify2 = (result) => {
|
|
14410
14662
|
if (!result.found) return { kind: "failed", problem: "missing", detail: "" };
|
|
14411
14663
|
if (result.timedOut) return { kind: "failed", problem: "timeout", detail: "" };
|
|
@@ -14617,7 +14869,7 @@ var hookSchemaProblems = (hooks, claude) => {
|
|
|
14617
14869
|
};
|
|
14618
14870
|
|
|
14619
14871
|
// src/doctor/hook-sources.ts
|
|
14620
|
-
import { join as
|
|
14872
|
+
import { join as join33 } from "node:path";
|
|
14621
14873
|
var entriesOf = (source, hooks, own) => Object.entries(asRecord(hooks)).flatMap(
|
|
14622
14874
|
([event, groups]) => asList(groups).flatMap((group) => {
|
|
14623
14875
|
const record3 = asRecord(group);
|
|
@@ -14630,31 +14882,31 @@ var projectHookFiles = (env) => readSettings(env).flatMap(({ scope: scope2, path
|
|
|
14630
14882
|
const source = PROJECT_SCOPES[scope2];
|
|
14631
14883
|
return source === void 0 || !("hooks" in settings) ? [] : [{ source, path, hooks: settings["hooks"] }];
|
|
14632
14884
|
});
|
|
14633
|
-
var pluginsDir = ({ env, runtime }) =>
|
|
14885
|
+
var pluginsDir = ({ env, runtime }) => join33(env["CLAUDE_CONFIG_DIR"] ?? join33(runtime.homeDir(), ".claude"), "plugins");
|
|
14634
14886
|
var installRecord = (value, root) => {
|
|
14635
14887
|
const records = Array.isArray(value) ? value.filter(isRecord) : isRecord(value) ? [value] : [];
|
|
14636
14888
|
return records.find((record3) => record3["projectPath"] === root) ?? records[0];
|
|
14637
14889
|
};
|
|
14638
14890
|
var readJson = (env, path) => {
|
|
14639
|
-
const
|
|
14640
|
-
const parsed =
|
|
14891
|
+
const text7 = env.runtime.readText(path);
|
|
14892
|
+
const parsed = text7.ok ? parseJsonObject(text7.value) : void 0;
|
|
14641
14893
|
return parsed?.ok === true ? parsed.value : void 0;
|
|
14642
14894
|
};
|
|
14643
14895
|
var installedPlugin = (env) => {
|
|
14644
|
-
const index = readJson(env, toAbsolutePath(
|
|
14896
|
+
const index = readJson(env, toAbsolutePath(join33(pluginsDir(env), "installed_plugins.json")));
|
|
14645
14897
|
const record3 = installRecord(asRecord(index?.["plugins"])[PLUGIN_ID], env.root);
|
|
14646
14898
|
const installPath = record3 === void 0 ? void 0 : optionalString(record3, "installPath");
|
|
14647
14899
|
if (record3 === void 0 || installPath === void 0) return void 0;
|
|
14648
14900
|
const root = toAbsolutePath(installPath);
|
|
14649
|
-
const manifest = readJson(env, toAbsolutePath(
|
|
14650
|
-
const hooksPath = toAbsolutePath(
|
|
14651
|
-
const
|
|
14652
|
-
const parsed =
|
|
14901
|
+
const manifest = readJson(env, toAbsolutePath(join33(root, ".claude-plugin", "plugin.json")));
|
|
14902
|
+
const hooksPath = toAbsolutePath(join33(root, "hooks", "hooks.json"));
|
|
14903
|
+
const text7 = env.runtime.readText(hooksPath);
|
|
14904
|
+
const parsed = text7.ok ? parseJsonObject(text7.value) : void 0;
|
|
14653
14905
|
return {
|
|
14654
14906
|
root,
|
|
14655
14907
|
version: (manifest === void 0 ? void 0 : optionalString(manifest, "version")) ?? optionalString(record3, "version"),
|
|
14656
14908
|
hooksFile: parsed?.ok === true ? { source: "plugin", path: hooksPath, hooks: parsed.value["hooks"] } : void 0,
|
|
14657
|
-
hooksText:
|
|
14909
|
+
hooksText: text7.ok ? text7.value : void 0
|
|
14658
14910
|
};
|
|
14659
14911
|
};
|
|
14660
14912
|
var PRECEDENCE2 = ["managed", "local", "project", "user"];
|
|
@@ -14684,15 +14936,15 @@ var schemaFinding = (file, problems, claude) => problems.length === 0 ? [] : [
|
|
|
14684
14936
|
)
|
|
14685
14937
|
];
|
|
14686
14938
|
var unreadableProjectFiles = (env) => settingsPaths(env).filter(({ scope: scope2 }) => scope2 === "project" || scope2 === "local").flatMap(({ path }) => {
|
|
14687
|
-
const
|
|
14688
|
-
return
|
|
14939
|
+
const text7 = env.runtime.readText(path);
|
|
14940
|
+
return text7.ok && text7.value.trim() !== "" && !parseJsonObject(text7.value).ok ? [path] : [];
|
|
14689
14941
|
});
|
|
14690
14942
|
var schemaFindings = (env, plugin, claude) => {
|
|
14691
14943
|
const label2 = claude === void 0 ? "?" : claude.join(".");
|
|
14692
14944
|
const files = [...projectHookFiles(env), ...plugin?.hooksFile === void 0 ? [] : [plugin.hooksFile]];
|
|
14693
14945
|
const invalidJson = [
|
|
14694
14946
|
...unreadableProjectFiles(env),
|
|
14695
|
-
...plugin?.hooksText !== void 0 && plugin.hooksFile === void 0 ? [toAbsolutePath(
|
|
14947
|
+
...plugin?.hooksText !== void 0 && plugin.hooksFile === void 0 ? [toAbsolutePath(join34(plugin.root, "hooks", "hooks.json"))] : []
|
|
14696
14948
|
];
|
|
14697
14949
|
return [
|
|
14698
14950
|
...invalidJson.flatMap((file) => schemaFinding(file, ["not valid JSON"], label2)),
|
|
@@ -14713,7 +14965,7 @@ var pluginFindings = (env, projectOwn) => {
|
|
|
14713
14965
|
];
|
|
14714
14966
|
};
|
|
14715
14967
|
var coverageFindings = (env, entries) => {
|
|
14716
|
-
const installed = env.runtime.stat(toAbsolutePath(
|
|
14968
|
+
const installed = env.runtime.stat(toAbsolutePath(join34(env.root, ...LOCK_FILE.split("/")))) !== void 0;
|
|
14717
14969
|
if (entries.length === 0 && !installed) {
|
|
14718
14970
|
return [finding("hooks.registered", "warn", "doctor.hooks.none", {}, "doctor.hooks.fix_none")];
|
|
14719
14971
|
}
|
|
@@ -14765,21 +15017,21 @@ var checkHooks = (env) => {
|
|
|
14765
15017
|
};
|
|
14766
15018
|
|
|
14767
15019
|
// src/doctor/instructions.ts
|
|
14768
|
-
import { join as
|
|
15020
|
+
import { join as join35 } from "node:path";
|
|
14769
15021
|
var INSTRUCTION_LINE_CEILING = 200;
|
|
14770
15022
|
var TOP_INSTRUCTIONS = ["AGENTS.md", "CLAUDE.md", ".claude/CLAUDE.md", "CLAUDE.local.md"];
|
|
14771
15023
|
var RULES_DIR = ".claude/rules";
|
|
14772
15024
|
var SHOWN = 3;
|
|
14773
|
-
var readProjectFile = (env,
|
|
14774
|
-
const
|
|
14775
|
-
return
|
|
15025
|
+
var readProjectFile = (env, relative11) => {
|
|
15026
|
+
const text7 = env.runtime.readText(toAbsolutePath(join35(env.root, ...relative11.split("/"))));
|
|
15027
|
+
return text7.ok ? text7.value : void 0;
|
|
14776
15028
|
};
|
|
14777
15029
|
var instructionFiles = (env) => {
|
|
14778
|
-
const listed4 = env.runtime.listDir(toAbsolutePath(
|
|
15030
|
+
const listed4 = env.runtime.listDir(toAbsolutePath(join35(env.root, ...RULES_DIR.split("/"))));
|
|
14779
15031
|
const rules = listed4.ok ? listed4.value.filter((name) => name.endsWith(".md")).map((name) => `${RULES_DIR}/${name}`) : [];
|
|
14780
15032
|
return [...TOP_INSTRUCTIONS, ...rules].flatMap((path) => {
|
|
14781
|
-
const
|
|
14782
|
-
return
|
|
15033
|
+
const text7 = readProjectFile(env, path);
|
|
15034
|
+
return text7 === void 0 ? [] : [{ path, text: text7 }];
|
|
14783
15035
|
});
|
|
14784
15036
|
};
|
|
14785
15037
|
var shortList = (items) => items.length <= SHOWN ? items.join(", ") : `${items.slice(0, SHOWN).join(", ")} (+${String(items.length - SHOWN)})`;
|
|
@@ -14827,10 +15079,10 @@ var hiddenInstructions = (env, deny2) => {
|
|
|
14827
15079
|
];
|
|
14828
15080
|
};
|
|
14829
15081
|
var localFile = (env) => readProjectFile(env, "CLAUDE.local.md") === void 0 ? [] : [finding("instructions.local", "warn", "doctor.instructions.local", {}, "doctor.instructions.fix_local")];
|
|
14830
|
-
var lineCount = (
|
|
15082
|
+
var lineCount = (text7) => text7.split("\n").length - (text7.endsWith("\n") ? 1 : 0);
|
|
14831
15083
|
var oversized = (env) => ["AGENTS.md", "CLAUDE.md"].flatMap((file) => {
|
|
14832
|
-
const
|
|
14833
|
-
const lines3 =
|
|
15084
|
+
const text7 = readProjectFile(env, file);
|
|
15085
|
+
const lines3 = text7 === void 0 ? 0 : lineCount(text7);
|
|
14834
15086
|
if (lines3 <= INSTRUCTION_LINE_CEILING) return [];
|
|
14835
15087
|
return [
|
|
14836
15088
|
finding(
|
|
@@ -14843,13 +15095,13 @@ var oversized = (env) => ["AGENTS.md", "CLAUDE.md"].flatMap((file) => {
|
|
|
14843
15095
|
];
|
|
14844
15096
|
});
|
|
14845
15097
|
var MIN_RULE_WORDS = 6;
|
|
14846
|
-
var ruleLines = (
|
|
15098
|
+
var ruleLines = (text7) => text7.split("\n").map((raw) => withoutCarriageReturn(raw).trim()).filter((line2) => !/^(#|\||```|@|>)/.test(line2)).map(
|
|
14847
15099
|
(line2) => line2.replace(/^([-*+]|\d+[.)])\s+/, "").replace(/\s+/g, " ").toLowerCase()
|
|
14848
15100
|
).filter((line2) => line2.split(" ").length >= MIN_RULE_WORDS);
|
|
14849
15101
|
var duplicatedRules = (files) => {
|
|
14850
15102
|
const seen = /* @__PURE__ */ new Map();
|
|
14851
|
-
for (const { path, text:
|
|
14852
|
-
for (const line2 of ruleLines(
|
|
15103
|
+
for (const { path, text: text7 } of files) {
|
|
15104
|
+
for (const line2 of ruleLines(text7)) seen.set(line2, (seen.get(line2) ?? /* @__PURE__ */ new Set()).add(path));
|
|
14853
15105
|
}
|
|
14854
15106
|
return [...seen].filter(([, paths]) => paths.size > 1).map(([line2]) => line2);
|
|
14855
15107
|
};
|
|
@@ -14880,12 +15132,12 @@ var checkInstructions = (env) => {
|
|
|
14880
15132
|
};
|
|
14881
15133
|
|
|
14882
15134
|
// src/doctor/line-endings.ts
|
|
14883
|
-
import { join as
|
|
15135
|
+
import { join as join36 } from "node:path";
|
|
14884
15136
|
var LF_RULE = /^\*\s.*\beol=lf\b/m;
|
|
14885
15137
|
var checkLineEndings = ({ root, runtime }) => {
|
|
14886
15138
|
const autocrlf = runtime.git(["config", "--get", "core.autocrlf"], root);
|
|
14887
15139
|
if (!autocrlf.ok || autocrlf.stdout.trim().toLowerCase() !== "true") return [];
|
|
14888
|
-
const attributes = runtime.readText(toAbsolutePath(
|
|
15140
|
+
const attributes = runtime.readText(toAbsolutePath(join36(root, ".gitattributes")));
|
|
14889
15141
|
if (attributes.ok && LF_RULE.test(attributes.value)) return [];
|
|
14890
15142
|
return [
|
|
14891
15143
|
finding("git.autocrlf", "warn", "doctor.git.autocrlf", { file: ".gitattributes" }, "doctor.git.fix_autocrlf")
|
|
@@ -14893,25 +15145,25 @@ var checkLineEndings = ({ root, runtime }) => {
|
|
|
14893
15145
|
};
|
|
14894
15146
|
|
|
14895
15147
|
// src/doctor/memory.ts
|
|
14896
|
-
import { join as
|
|
15148
|
+
import { join as join37 } from "node:path";
|
|
14897
15149
|
var ceilingOf = (config) => {
|
|
14898
15150
|
const value = configAt(parseConfigYaml(config ?? ""), "memory", "maxLines");
|
|
14899
15151
|
const parsed = typeof value === "string" ? Number.parseInt(value, 10) : Number.NaN;
|
|
14900
15152
|
return Number.isInteger(parsed) && parsed > 0 ? parsed : AGENTS_MAX_LINES;
|
|
14901
15153
|
};
|
|
14902
|
-
var lineCount2 = (
|
|
15154
|
+
var lineCount2 = (text7) => text7.replace(/\r?\n$/, "").split(/\r?\n/).length;
|
|
14903
15155
|
var checkAgentsFile = ({ root, runtime }) => {
|
|
14904
|
-
const
|
|
14905
|
-
if (!
|
|
14906
|
-
const config = runtime.readText(toAbsolutePath(
|
|
15156
|
+
const text7 = runtime.readText(toAbsolutePath(join37(root, AGENTS_FILE)));
|
|
15157
|
+
if (!text7.ok) return [];
|
|
15158
|
+
const config = runtime.readText(toAbsolutePath(join37(root, CONFIG_FILE)));
|
|
14907
15159
|
const max = ceilingOf(config.ok ? config.value : void 0);
|
|
14908
|
-
const lines3 = lineCount2(
|
|
15160
|
+
const lines3 = lineCount2(text7.value);
|
|
14909
15161
|
const params2 = { file: AGENTS_FILE, lines: lines3, max };
|
|
14910
15162
|
return lines3 > max ? [finding("memory.agents_lines", "fail", "doctor.memory.too_long", params2, "doctor.memory.fix_too_long")] : [finding("memory.agents_lines", "ok", "doctor.memory.lines", params2)];
|
|
14911
15163
|
};
|
|
14912
15164
|
|
|
14913
15165
|
// src/doctor/project-files.ts
|
|
14914
|
-
import { join as
|
|
15166
|
+
import { join as join38 } from "node:path";
|
|
14915
15167
|
var variants = (base, extensions) => extensions.map((extension) => `${base}${extension}`);
|
|
14916
15168
|
var SCRIPT = [".js", ".cjs", ".mjs", ".ts", ".cts", ".mts"];
|
|
14917
15169
|
var DATA = ["", ".json", ".yml", ".yaml"];
|
|
@@ -14942,7 +15194,7 @@ var CONFIG_GROUPS = [
|
|
|
14942
15194
|
files: [...variants("commitlint.config", SCRIPT), ...variants(".commitlintrc", [...DATA, ".js"])]
|
|
14943
15195
|
}
|
|
14944
15196
|
];
|
|
14945
|
-
var present = (env, files) => files.filter((file) => env.runtime.stat(toAbsolutePath(
|
|
15197
|
+
var present = (env, files) => files.filter((file) => env.runtime.stat(toAbsolutePath(join38(env.root, file)))?.kind === "file");
|
|
14946
15198
|
var crowded = (env, groups, build) => groups.flatMap(({ name, files }) => {
|
|
14947
15199
|
const found = present(env, files);
|
|
14948
15200
|
return found.length > 1 ? [build(name, found.join(", "))] : [];
|
|
@@ -15019,24 +15271,24 @@ var doctorEnvFrom = (ctx) => ({
|
|
|
15019
15271
|
runtime: { ...ctx.runtime, probe: probeOnce(ctx.runtime.probe) }
|
|
15020
15272
|
});
|
|
15021
15273
|
var runChecks2 = (env, checks = CHECKS) => checks.flatMap((check2) => check2(env));
|
|
15022
|
-
var toItem = (ctx,
|
|
15023
|
-
check:
|
|
15024
|
-
status:
|
|
15025
|
-
detail: ctx.t(
|
|
15026
|
-
...
|
|
15274
|
+
var toItem = (ctx, finding4) => ({
|
|
15275
|
+
check: finding4.check,
|
|
15276
|
+
status: finding4.status,
|
|
15277
|
+
detail: ctx.t(finding4.messageKey, finding4.params),
|
|
15278
|
+
...finding4.fixKey === void 0 ? {} : { fix: ctx.t(finding4.fixKey, finding4.params) }
|
|
15027
15279
|
});
|
|
15028
15280
|
var count3 = (items, status) => items.filter((item2) => item2.status === status).length;
|
|
15029
15281
|
var hookItems = (ctx, findings) => findings.flatMap(
|
|
15030
|
-
(
|
|
15282
|
+
(finding4) => finding4.hook === void 0 ? [] : [
|
|
15031
15283
|
{
|
|
15032
|
-
check:
|
|
15033
|
-
status:
|
|
15034
|
-
fix:
|
|
15284
|
+
check: finding4.check,
|
|
15285
|
+
status: finding4.hook,
|
|
15286
|
+
fix: finding4.fixKey === void 0 ? null : ctx.t(finding4.fixKey, finding4.params)
|
|
15035
15287
|
}
|
|
15036
15288
|
]
|
|
15037
15289
|
);
|
|
15038
15290
|
var buildReport2 = (ctx, findings) => {
|
|
15039
|
-
const checks = findings.map((
|
|
15291
|
+
const checks = findings.map((finding4) => toItem(ctx, finding4));
|
|
15040
15292
|
return {
|
|
15041
15293
|
schema: DOCTOR_SCHEMA,
|
|
15042
15294
|
checks,
|
|
@@ -15086,9 +15338,9 @@ var manualLines = (ctx, report2) => report2.checks.some((item2) => item2.status
|
|
|
15086
15338
|
var writeReport = (ctx, data, result) => {
|
|
15087
15339
|
const env = doctorEnvFrom(ctx);
|
|
15088
15340
|
const path = reportPath(env);
|
|
15089
|
-
const
|
|
15341
|
+
const text7 = reportMarkdown(env, ctx, toEnvelope("doctor", result));
|
|
15090
15342
|
const dir = ctx.runtime.mkdir(toAbsolutePath(dirname7(path)));
|
|
15091
|
-
const written = dir.ok ? ctx.runtime.writeAtomic(path,
|
|
15343
|
+
const written = dir.ok ? ctx.runtime.writeAtomic(path, text7) : dir;
|
|
15092
15344
|
if (!written.ok) return failureFrom(ctx.t, applyProblem({ path, detail: written.error }));
|
|
15093
15345
|
const human2 = [...result.human ?? [], ctx.t("doctor.report.written", { path })];
|
|
15094
15346
|
return { ...result, data: { ...data, reportPath: path }, human: human2 };
|
|
@@ -15357,10 +15609,10 @@ var prePushChecks = (runtime, repo, remote, updates) => {
|
|
|
15357
15609
|
};
|
|
15358
15610
|
|
|
15359
15611
|
// src/githooks/render.ts
|
|
15360
|
-
var
|
|
15612
|
+
var MAX_LISTED9 = 5;
|
|
15361
15613
|
var detailOf = (check2) => {
|
|
15362
|
-
const listed4 = check2.failures.slice(0,
|
|
15363
|
-
const rest = check2.failures.length -
|
|
15614
|
+
const listed4 = check2.failures.slice(0, MAX_LISTED9).join("; ");
|
|
15615
|
+
const rest = check2.failures.length - MAX_LISTED9;
|
|
15364
15616
|
return rest > 0 ? `${listed4}; +${String(rest)}` : listed4;
|
|
15365
15617
|
};
|
|
15366
15618
|
var checkLine = (t, check2) => check2.failures.length === 0 ? t("githooks.pass", { check: check2.id }) : t("githooks.fail", { check: check2.id, detail: detailOf(check2), fix: check2.fix });
|
|
@@ -15414,15 +15666,15 @@ var commitMsgCommand = {
|
|
|
15414
15666
|
usageProblem("invalid_arguments", "error.invalid_arguments", COMMIT_MSG_USAGE, { detail: "<message-file>" })
|
|
15415
15667
|
)
|
|
15416
15668
|
);
|
|
15417
|
-
const
|
|
15418
|
-
if (!
|
|
15669
|
+
const text7 = ctx.runtime.readText(toAbsolutePath(file, ctx.cwd));
|
|
15670
|
+
if (!text7.ok)
|
|
15419
15671
|
return Promise.resolve(
|
|
15420
15672
|
failureFrom(
|
|
15421
15673
|
ctx.t,
|
|
15422
15674
|
usageProblem("invalid_arguments", "error.invalid_arguments", COMMIT_MSG_USAGE, { detail: file })
|
|
15423
15675
|
)
|
|
15424
15676
|
);
|
|
15425
|
-
const failures = commitMessageProblems(
|
|
15677
|
+
const failures = commitMessageProblems(text7.value);
|
|
15426
15678
|
const fix = "git commit --amend, using type(scope): subject and no AI attribution lines";
|
|
15427
15679
|
return Promise.resolve(
|
|
15428
15680
|
checksResult(ctx.t, COMMIT_MSG_SCHEMA, "githook_failed", [{ id: "commit-messages", failures, fix }])
|
|
@@ -15486,11 +15738,11 @@ var humanOf = (ctx, item2) => {
|
|
|
15486
15738
|
return [
|
|
15487
15739
|
ctx.t("git.merge.lost", { ...range, count: report2.findings.length }),
|
|
15488
15740
|
...report2.findings.map(
|
|
15489
|
-
(
|
|
15490
|
-
where: findingLabel(
|
|
15491
|
-
kind:
|
|
15492
|
-
side:
|
|
15493
|
-
text:
|
|
15741
|
+
(finding4) => ctx.t("git.merge.finding", {
|
|
15742
|
+
where: findingLabel(finding4),
|
|
15743
|
+
kind: finding4.kind,
|
|
15744
|
+
side: finding4.side ?? "-",
|
|
15745
|
+
text: finding4.text
|
|
15494
15746
|
})
|
|
15495
15747
|
)
|
|
15496
15748
|
];
|
|
@@ -15560,7 +15812,7 @@ var mergeVerifyCommand = {
|
|
|
15560
15812
|
var REFLOG_LIMIT = 300;
|
|
15561
15813
|
var LIST_LIMIT = 50;
|
|
15562
15814
|
var SHA = /^[0-9a-f]{40,64}$/;
|
|
15563
|
-
var linesOf = (
|
|
15815
|
+
var linesOf = (text7) => text7.split("\n").filter((line2) => line2.trim() !== "");
|
|
15564
15816
|
var reflogLines = (runtime, repo) => {
|
|
15565
15817
|
const result = runtime.git(["reflog", "show", "--all", `-n${String(REFLOG_LIMIT)}`, "--format=%H %gD"], repo.root);
|
|
15566
15818
|
if (!result.ok) return [];
|
|
@@ -15681,7 +15933,7 @@ var rescueCommand = {
|
|
|
15681
15933
|
};
|
|
15682
15934
|
|
|
15683
15935
|
// src/git/undo.ts
|
|
15684
|
-
import { basename as basename3, dirname as dirname8, join as
|
|
15936
|
+
import { basename as basename3, dirname as dirname8, join as join39 } from "node:path";
|
|
15685
15937
|
var INDEX_SUBJECT = "finitemonkey index:";
|
|
15686
15938
|
var gitLine = (runtime, repo, args2) => {
|
|
15687
15939
|
const result = runtime.git(args2, repo.root);
|
|
@@ -15724,7 +15976,7 @@ var findSnapshot = (views, wanted) => {
|
|
|
15724
15976
|
};
|
|
15725
15977
|
var stampOf2 = (iso) => iso.replace(/[:.]/g, "-");
|
|
15726
15978
|
var worktreePath = (runtime, repo) => {
|
|
15727
|
-
const base =
|
|
15979
|
+
const base = join39(dirname8(repo.root), `${basename3(repo.root)}-undo-${stampOf2(runtime.now())}`);
|
|
15728
15980
|
return toAbsolutePath(runtime.stat(toAbsolutePath(base)) === void 0 ? base : `${base}-2`);
|
|
15729
15981
|
};
|
|
15730
15982
|
var intoWorktree = (runtime, repo, view2) => {
|
|
@@ -15849,7 +16101,7 @@ var GLOBAL_FLAGS = [
|
|
|
15849
16101
|
{ flag: "--help, -h", key: "flag.help" }
|
|
15850
16102
|
];
|
|
15851
16103
|
var exitCodeHelp = (ctx) => EXIT_CODE_NAMES.map((name) => ({ code: ExitCode[name], name, summary: ctx.t(exitCodeDocs[ExitCode[name]]) }));
|
|
15852
|
-
var pad = (
|
|
16104
|
+
var pad = (text7, width) => text7.padEnd(Math.max(width, text7.length + 1));
|
|
15853
16105
|
var describe2 = (ctx, spec2) => ({
|
|
15854
16106
|
name: commandName(spec2),
|
|
15855
16107
|
usage: spec2.usage,
|
|
@@ -15959,7 +16211,7 @@ var hookCommand = {
|
|
|
15959
16211
|
};
|
|
15960
16212
|
|
|
15961
16213
|
// src/commands/i18n-check.ts
|
|
15962
|
-
import { join as
|
|
16214
|
+
import { join as join40 } from "node:path";
|
|
15963
16215
|
|
|
15964
16216
|
// src/i18n/message.ts
|
|
15965
16217
|
var ARG = /^\s*([a-zA-Z][a-zA-Z0-9_]*)\s*(?:,\s*(plural|select)\s*,)?/;
|
|
@@ -15991,10 +16243,10 @@ var parseBranches = (body2, inPlural) => {
|
|
|
15991
16243
|
};
|
|
15992
16244
|
var parseNodes = (source, inPlural) => {
|
|
15993
16245
|
const nodes = [];
|
|
15994
|
-
let
|
|
16246
|
+
let text7 = "";
|
|
15995
16247
|
const flush2 = () => {
|
|
15996
|
-
if (
|
|
15997
|
-
|
|
16248
|
+
if (text7 !== "") nodes.push({ kind: "text", text: text7 });
|
|
16249
|
+
text7 = "";
|
|
15998
16250
|
};
|
|
15999
16251
|
for (let index = 0; index < source.length; index += 1) {
|
|
16000
16252
|
const char = source.charAt(index);
|
|
@@ -16004,7 +16256,7 @@ var parseNodes = (source, inPlural) => {
|
|
|
16004
16256
|
continue;
|
|
16005
16257
|
}
|
|
16006
16258
|
if (char !== "{") {
|
|
16007
|
-
|
|
16259
|
+
text7 += char;
|
|
16008
16260
|
continue;
|
|
16009
16261
|
}
|
|
16010
16262
|
const cursor = { source, index: index + 1 };
|
|
@@ -16012,18 +16264,18 @@ var parseNodes = (source, inPlural) => {
|
|
|
16012
16264
|
const inner = end < 0 ? void 0 : source.slice(index + 1, end);
|
|
16013
16265
|
const match = inner === void 0 ? null : ARG.exec(inner);
|
|
16014
16266
|
if (inner === void 0 || match === null) {
|
|
16015
|
-
|
|
16267
|
+
text7 += char;
|
|
16016
16268
|
continue;
|
|
16017
16269
|
}
|
|
16018
16270
|
const name = match[1] ?? "";
|
|
16019
16271
|
const type = match[2] === "plural" || match[2] === "select" ? match[2] : void 0;
|
|
16020
16272
|
if (type === void 0 && match[0].trim().length !== inner.trim().length) {
|
|
16021
|
-
|
|
16273
|
+
text7 += char;
|
|
16022
16274
|
continue;
|
|
16023
16275
|
}
|
|
16024
16276
|
const branches = type === void 0 ? void 0 : parseBranches(inner.slice(match[0].length), type === "plural");
|
|
16025
16277
|
if (type !== void 0 && branches === void 0) {
|
|
16026
|
-
|
|
16278
|
+
text7 += char;
|
|
16027
16279
|
continue;
|
|
16028
16280
|
}
|
|
16029
16281
|
flush2();
|
|
@@ -16161,10 +16413,10 @@ var unreadable = (dir, file, detail) => ({
|
|
|
16161
16413
|
exitCode: ExitCode.NotConfigured
|
|
16162
16414
|
});
|
|
16163
16415
|
var readCatalog = (ctx, dir, lang) => {
|
|
16164
|
-
const file = toAbsolutePath(
|
|
16165
|
-
const
|
|
16166
|
-
if (!
|
|
16167
|
-
const parsed = parseJsonObject(
|
|
16416
|
+
const file = toAbsolutePath(join40(dir, `${lang}.json`));
|
|
16417
|
+
const text7 = ctx.runtime.readText(file);
|
|
16418
|
+
if (!text7.ok) return reject(unreadable(dir, file, text7.error));
|
|
16419
|
+
const parsed = parseJsonObject(text7.value);
|
|
16168
16420
|
return parsed.ok ? parsed : reject(unreadable(dir, file, parsed.error));
|
|
16169
16421
|
};
|
|
16170
16422
|
var loadCatalogs = (ctx, dir) => {
|
|
@@ -16184,8 +16436,8 @@ var describeProblem = (ctx, problem4) => {
|
|
|
16184
16436
|
return ` ${problem4.lang} ${problem4.key} ${kind}`;
|
|
16185
16437
|
};
|
|
16186
16438
|
var loadReview = (ctx, dir) => {
|
|
16187
|
-
const
|
|
16188
|
-
const parsed =
|
|
16439
|
+
const text7 = ctx.runtime.readText(toAbsolutePath(join40(dir, "review.json")));
|
|
16440
|
+
const parsed = text7.ok ? parseJsonObject(text7.value) : void 0;
|
|
16189
16441
|
if (parsed?.ok !== true) return void 0;
|
|
16190
16442
|
const statuses = (lang) => {
|
|
16191
16443
|
const value = parsed.value[lang];
|
|
@@ -16225,7 +16477,7 @@ var i18nCheckCommand = {
|
|
|
16225
16477
|
};
|
|
16226
16478
|
|
|
16227
16479
|
// src/history/look-back.ts
|
|
16228
|
-
import { join as
|
|
16480
|
+
import { join as join41 } from "node:path";
|
|
16229
16481
|
|
|
16230
16482
|
// src/history/patterns.ts
|
|
16231
16483
|
import { posix as posix5 } from "node:path";
|
|
@@ -16330,8 +16582,8 @@ var addResults = (draft, blocks) => {
|
|
|
16330
16582
|
let any = false;
|
|
16331
16583
|
for (const block4 of blocks) {
|
|
16332
16584
|
if (!isRecord(block4) || block4["type"] !== "tool_result" || typeof block4["tool_use_id"] !== "string") continue;
|
|
16333
|
-
const
|
|
16334
|
-
draft.results.set(block4["tool_use_id"], { failed: block4["is_error"] === true || FAILED_EXIT.test(
|
|
16585
|
+
const text7 = textOf3(block4["content"]);
|
|
16586
|
+
draft.results.set(block4["tool_use_id"], { failed: block4["is_error"] === true || FAILED_EXIT.test(text7), text: text7 });
|
|
16335
16587
|
any = true;
|
|
16336
16588
|
}
|
|
16337
16589
|
return any;
|
|
@@ -16361,7 +16613,7 @@ var parseTranscript = (jsonl) => {
|
|
|
16361
16613
|
// src/history/look-back.ts
|
|
16362
16614
|
var SAMPLES = 3;
|
|
16363
16615
|
var projectLogKey = (root) => root.replace(/[^A-Za-z0-9-]/g, "-");
|
|
16364
|
-
var projectLogDir = (runtime, root, env) =>
|
|
16616
|
+
var projectLogDir = (runtime, root, env) => join41(env["CLAUDE_CONFIG_DIR"] ?? join41(runtime.homeDir(), ".claude"), "projects", projectLogKey(root));
|
|
16365
16617
|
var lookBack = (runtime, root, env) => {
|
|
16366
16618
|
const dir = projectLogDir(runtime, root, env);
|
|
16367
16619
|
const listed4 = runtime.listDir(toAbsolutePath(dir));
|
|
@@ -16382,9 +16634,9 @@ var lookBack = (runtime, root, env) => {
|
|
|
16382
16634
|
};
|
|
16383
16635
|
let turns = 0;
|
|
16384
16636
|
for (const name of files) {
|
|
16385
|
-
const
|
|
16386
|
-
if (!
|
|
16387
|
-
for (const turn of parseTranscript(
|
|
16637
|
+
const text7 = runtime.readText(toAbsolutePath(join41(dir, name)));
|
|
16638
|
+
if (!text7.ok) continue;
|
|
16639
|
+
for (const turn of parseTranscript(text7.value)) {
|
|
16388
16640
|
turns += 1;
|
|
16389
16641
|
for (const hit3 of hitsIn(turn)) {
|
|
16390
16642
|
counts[hit3.pattern] += 1;
|
|
@@ -16432,7 +16684,7 @@ var walk2 = (a, b) => {
|
|
|
16432
16684
|
}
|
|
16433
16685
|
return ops;
|
|
16434
16686
|
};
|
|
16435
|
-
var splitLines = (
|
|
16687
|
+
var splitLines = (text7) => text7 === "" ? [] : text7.replace(/\r?\n$/, "").split(/\r?\n/);
|
|
16436
16688
|
var sharedPrefix = (a, b) => {
|
|
16437
16689
|
let count4 = 0;
|
|
16438
16690
|
while (count4 < a.length && count4 < b.length && a[count4] === b[count4]) count4 += 1;
|
|
@@ -16509,7 +16761,7 @@ var planData = (t, plan, mode, applied, backup) => ({
|
|
|
16509
16761
|
diff: planDiff(plan),
|
|
16510
16762
|
...backup === void 0 ? {} : { backup }
|
|
16511
16763
|
});
|
|
16512
|
-
var pad2 = (
|
|
16764
|
+
var pad2 = (text7, width) => text7.padEnd(width);
|
|
16513
16765
|
var planLines = (t, data, withDiff) => [
|
|
16514
16766
|
t("install.plan.title", { root: data.root, preset: data.preset }),
|
|
16515
16767
|
...data.actions.map(({ path, action }) => ` ${pad2(action, 13)}${path}`),
|
|
@@ -16598,6 +16850,346 @@ var initCommand = {
|
|
|
16598
16850
|
}
|
|
16599
16851
|
};
|
|
16600
16852
|
|
|
16853
|
+
// src/look/findings.ts
|
|
16854
|
+
var MAX_PER_RULE = 5;
|
|
16855
|
+
var finding3 = (result, rule, detail) => ({
|
|
16856
|
+
rule,
|
|
16857
|
+
route: result.route,
|
|
16858
|
+
viewport: result.viewport,
|
|
16859
|
+
detail,
|
|
16860
|
+
blocking: BLOCKING_RULES.includes(rule)
|
|
16861
|
+
});
|
|
16862
|
+
var many = (result, rule, items) => items.slice(0, MAX_PER_RULE).map((item2) => finding3(result, rule, item2));
|
|
16863
|
+
var pageFindings = (result) => {
|
|
16864
|
+
if (result.loadError !== void 0) return [finding3(result, "look/request-failed", result.loadError)];
|
|
16865
|
+
const measure = result.measure;
|
|
16866
|
+
return [
|
|
16867
|
+
...measure !== void 0 && measure.overflow > 0 ? [finding3(result, "look/overflow", `${String(measure.overflow)}px wider than the viewport`)] : [],
|
|
16868
|
+
...many(result, "look/console-error", result.consoleErrors),
|
|
16869
|
+
...many(result, "look/request-failed", result.failedRequests),
|
|
16870
|
+
...many(result, "look/i18n-key", measure?.keys ?? []),
|
|
16871
|
+
...many(result, "look/broken-image", measure?.broken ?? []),
|
|
16872
|
+
...many(result, "look/touch-target", measure?.small ?? [])
|
|
16873
|
+
];
|
|
16874
|
+
};
|
|
16875
|
+
var applyAllows = (findings, allows, decided) => {
|
|
16876
|
+
const accepted2 = allows.filter((allow2) => decided(allow2.decision));
|
|
16877
|
+
const ignored = allows.filter((allow2) => !decided(allow2.decision)).map((allow2) => `${allow2.rule} ${allow2.route} ${allow2.decision}`);
|
|
16878
|
+
return {
|
|
16879
|
+
ignored,
|
|
16880
|
+
findings: findings.map((item2) => {
|
|
16881
|
+
const allow2 = accepted2.find((entry2) => entry2.rule === item2.rule && entry2.route === item2.route);
|
|
16882
|
+
return allow2 === void 0 ? item2 : { ...item2, blocking: false, allowedBy: allow2.decision };
|
|
16883
|
+
})
|
|
16884
|
+
};
|
|
16885
|
+
};
|
|
16886
|
+
|
|
16887
|
+
// src/look/node-capture.ts
|
|
16888
|
+
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
16889
|
+
import { join as join42 } from "node:path";
|
|
16890
|
+
|
|
16891
|
+
// src/look/measure.ts
|
|
16892
|
+
var measurePage = (doc) => {
|
|
16893
|
+
const KEY2 = /^[a-z][A-Za-z0-9_]*(?:\.[A-Za-z0-9_-]+)+$/;
|
|
16894
|
+
const PLACEHOLDER2 = /\{\{\s*[\w.]+\s*\}\}|\$t\(/;
|
|
16895
|
+
const NOT_KEY = /\.(?:com|net|org|io|dev|br|app|js|ts|json|png|svg|jpg|md)$/i;
|
|
16896
|
+
const SHOW_TEXT = 4;
|
|
16897
|
+
const walker = doc.createTreeWalker(doc.body, SHOW_TEXT);
|
|
16898
|
+
const keys = /* @__PURE__ */ new Set();
|
|
16899
|
+
for (let node = walker.nextNode(); node !== null; node = walker.nextNode()) {
|
|
16900
|
+
const text7 = (node.textContent ?? "").trim();
|
|
16901
|
+
if (KEY2.test(text7) && !NOT_KEY.test(text7) || PLACEHOLDER2.test(text7)) keys.add(text7.slice(0, 80));
|
|
16902
|
+
}
|
|
16903
|
+
const small = [...doc.querySelectorAll("a, button, input, select, textarea, [role=button], [role=link]")].filter((element) => {
|
|
16904
|
+
const box = element.getBoundingClientRect();
|
|
16905
|
+
return box.width > 0 && box.height > 0 && (box.width < 40 || box.height < 40);
|
|
16906
|
+
}).map((element) => `${element.tagName.toLowerCase()} "${(element.textContent ?? "").trim().slice(0, 40)}"`);
|
|
16907
|
+
const broken = [...doc.images].filter((image) => image.complete && image.naturalWidth === 0 && image.getAttribute("src") !== null).map((image) => image.getAttribute("src") ?? "");
|
|
16908
|
+
const root = doc.documentElement;
|
|
16909
|
+
return { overflow: Math.max(0, root.scrollWidth - root.clientWidth), keys: [...keys], small, broken };
|
|
16910
|
+
};
|
|
16911
|
+
var MEASURE_SCRIPT = `(${measurePage.toString()})(document)`;
|
|
16912
|
+
var texts = (value) => Array.isArray(value) ? value.filter((item2) => typeof item2 === "string") : [];
|
|
16913
|
+
var parseMeasure = (value) => {
|
|
16914
|
+
if (!isRecord(value) || typeof value["overflow"] !== "number") return void 0;
|
|
16915
|
+
return {
|
|
16916
|
+
overflow: value["overflow"],
|
|
16917
|
+
keys: texts(value["keys"]),
|
|
16918
|
+
small: texts(value["small"]),
|
|
16919
|
+
broken: texts(value["broken"])
|
|
16920
|
+
};
|
|
16921
|
+
};
|
|
16922
|
+
|
|
16923
|
+
// src/look/node-capture.ts
|
|
16924
|
+
var HEIGHT = 900;
|
|
16925
|
+
var MAX_EVENTS = 20;
|
|
16926
|
+
var RESOURCE_ERROR = /^Failed to load resource\b/;
|
|
16927
|
+
var slugOf = (route) => {
|
|
16928
|
+
const slug2 = route.replace(/^\//, "").replace(/[^A-Za-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
16929
|
+
return slug2 === "" ? "root" : slug2.slice(0, 80);
|
|
16930
|
+
};
|
|
16931
|
+
var watch = (page) => {
|
|
16932
|
+
const errors = [];
|
|
16933
|
+
const failed4 = [];
|
|
16934
|
+
const push = (list4, text7) => {
|
|
16935
|
+
if (list4.length < MAX_EVENTS) list4.push(text7.slice(0, 200));
|
|
16936
|
+
};
|
|
16937
|
+
page.on("console", (message) => {
|
|
16938
|
+
if (message.type() === "error" && !RESOURCE_ERROR.test(message.text())) push(errors, message.text());
|
|
16939
|
+
});
|
|
16940
|
+
page.on("pageerror", (error) => {
|
|
16941
|
+
push(errors, error.message);
|
|
16942
|
+
});
|
|
16943
|
+
page.on("requestfailed", (request) => {
|
|
16944
|
+
push(failed4, `${request.method()} ${request.url()}`);
|
|
16945
|
+
});
|
|
16946
|
+
page.on("response", (response) => {
|
|
16947
|
+
if (response.status() >= 400 && !response.url().endsWith("/favicon.ico"))
|
|
16948
|
+
push(failed4, `${String(response.status())} ${response.url()}`);
|
|
16949
|
+
});
|
|
16950
|
+
return { errors, failed: failed4 };
|
|
16951
|
+
};
|
|
16952
|
+
var loadAndShoot = async (page, url2, path, timeoutMs) => {
|
|
16953
|
+
try {
|
|
16954
|
+
await page.goto(url2, { waitUntil: "networkidle", timeout: timeoutMs });
|
|
16955
|
+
} catch (error) {
|
|
16956
|
+
return error instanceof Error ? error.message.split("\n")[0] : String(error);
|
|
16957
|
+
}
|
|
16958
|
+
await page.screenshot({ path, fullPage: true, animations: "disabled" });
|
|
16959
|
+
return void 0;
|
|
16960
|
+
};
|
|
16961
|
+
var captureOne = async (browser, request, route, viewport) => {
|
|
16962
|
+
const context = await browser.newContext({ viewport: { width: viewport, height: HEIGHT }, reducedMotion: "reduce" });
|
|
16963
|
+
const page = await context.newPage();
|
|
16964
|
+
const events = watch(page);
|
|
16965
|
+
const path = join42(request.dir, `${slugOf(route)}-${String(viewport)}.png`);
|
|
16966
|
+
const loadError = await loadAndShoot(page, `${request.baseUrl}${route}`, path, request.timeoutMs);
|
|
16967
|
+
const measure = loadError === void 0 ? parseMeasure(await page.evaluate(MEASURE_SCRIPT)) : void 0;
|
|
16968
|
+
await context.close();
|
|
16969
|
+
return { route, viewport, path, consoleErrors: events.errors, failedRequests: events.failed, measure, loadError };
|
|
16970
|
+
};
|
|
16971
|
+
var captureAll = async (browser, request) => {
|
|
16972
|
+
mkdirSync3(request.dir, { recursive: true });
|
|
16973
|
+
const results = [];
|
|
16974
|
+
for (const route of request.routes) {
|
|
16975
|
+
for (const viewport of request.viewports) results.push(await captureOne(browser, request, route, viewport));
|
|
16976
|
+
}
|
|
16977
|
+
return results;
|
|
16978
|
+
};
|
|
16979
|
+
|
|
16980
|
+
// src/look/node-playwright.ts
|
|
16981
|
+
import { createRequire } from "node:module";
|
|
16982
|
+
import { join as join43 } from "node:path";
|
|
16983
|
+
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
16984
|
+
var NAMES = ["playwright", "playwright-core"];
|
|
16985
|
+
var isPlaywright = (value) => isRecord(value) && isRecord(value["chromium"]) && typeof value["chromium"]["launch"] === "function";
|
|
16986
|
+
var unwrap2 = (value) => isRecord(value) && !("chromium" in value) && isRecord(value["default"]) ? value["default"] : value;
|
|
16987
|
+
var tryLoad = async (base, name) => {
|
|
16988
|
+
try {
|
|
16989
|
+
const resolved = createRequire(base).resolve(name);
|
|
16990
|
+
const loaded = unwrap2(await import(pathToFileURL(resolved).href));
|
|
16991
|
+
return isPlaywright(loaded) ? loaded : void 0;
|
|
16992
|
+
} catch {
|
|
16993
|
+
return void 0;
|
|
16994
|
+
}
|
|
16995
|
+
};
|
|
16996
|
+
var loadPlaywright = async (root) => {
|
|
16997
|
+
for (const base of [join43(root, "package.json"), fileURLToPath3(import.meta.url)]) {
|
|
16998
|
+
for (const name of NAMES) {
|
|
16999
|
+
const found = await tryLoad(base, name);
|
|
17000
|
+
if (found !== void 0) return found;
|
|
17001
|
+
}
|
|
17002
|
+
}
|
|
17003
|
+
return void 0;
|
|
17004
|
+
};
|
|
17005
|
+
var launchBrowser = async (playwright) => {
|
|
17006
|
+
for (const options of [{}, { channel: "chrome" }, { channel: "msedge" }]) {
|
|
17007
|
+
try {
|
|
17008
|
+
return await playwright.chromium.launch({ headless: true, ...options });
|
|
17009
|
+
} catch {
|
|
17010
|
+
continue;
|
|
17011
|
+
}
|
|
17012
|
+
}
|
|
17013
|
+
return void 0;
|
|
17014
|
+
};
|
|
17015
|
+
|
|
17016
|
+
// src/look/node-sheet.ts
|
|
17017
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
17018
|
+
import { join as join44 } from "node:path";
|
|
17019
|
+
var escapeHtml = (text7) => text7.replace(/[&<>"]/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[char] ?? char);
|
|
17020
|
+
var card = (result) => {
|
|
17021
|
+
const image = (() => {
|
|
17022
|
+
try {
|
|
17023
|
+
return `<img src="data:image/png;base64,${readFileSync2(result.path).toString("base64")}">`;
|
|
17024
|
+
} catch {
|
|
17025
|
+
return "<p>no capture</p>";
|
|
17026
|
+
}
|
|
17027
|
+
})();
|
|
17028
|
+
return `<figure><figcaption>${escapeHtml(result.route)} @ ${String(result.viewport)}px</figcaption>${image}</figure>`;
|
|
17029
|
+
};
|
|
17030
|
+
var STYLE = [
|
|
17031
|
+
"body{margin:16px;font:14px sans-serif;background:#fff;display:grid;grid-template-columns:repeat(4,1fr);gap:12px}",
|
|
17032
|
+
"figure{margin:0;border:1px solid #ccc}",
|
|
17033
|
+
"figcaption{padding:4px 6px;background:#eee}",
|
|
17034
|
+
"img{display:block;width:100%;max-height:720px;object-fit:cover;object-position:top}"
|
|
17035
|
+
].join("");
|
|
17036
|
+
var contactSheet = async (browser, results, dir) => {
|
|
17037
|
+
const shown3 = results.filter((result) => result.loadError === void 0);
|
|
17038
|
+
if (shown3.length === 0) return void 0;
|
|
17039
|
+
const page = await browser.newPage({ viewport: { width: 1600, height: 900 } });
|
|
17040
|
+
await page.setContent(`<!doctype html><style>${STYLE}</style>${shown3.map(card).join("")}`);
|
|
17041
|
+
const path = join44(dir, "contact-sheet.png");
|
|
17042
|
+
await page.screenshot({ path, fullPage: true });
|
|
17043
|
+
await page.close();
|
|
17044
|
+
return path;
|
|
17045
|
+
};
|
|
17046
|
+
|
|
17047
|
+
// src/look/record.ts
|
|
17048
|
+
var LOOK_CHECK = "*look*";
|
|
17049
|
+
var recordLook = (runtime, paths, tree, shot, allowed2) => {
|
|
17050
|
+
const failures = allowed2.findings.filter((item2) => item2.blocking).length;
|
|
17051
|
+
const summary = {
|
|
17052
|
+
schema: LOOK_SCHEMA,
|
|
17053
|
+
treeHash: tree,
|
|
17054
|
+
at: runtime.now(),
|
|
17055
|
+
failures,
|
|
17056
|
+
notes: allowed2.findings.length - failures,
|
|
17057
|
+
captures: shot.results.filter((result) => result.loadError === void 0).map((result) => ({ route: result.route, viewport: result.viewport, path: projectRelative(paths, result.path) })),
|
|
17058
|
+
contactSheet: shot.sheet === void 0 ? null : projectRelative(paths, shot.sheet),
|
|
17059
|
+
findings: allowed2.findings,
|
|
17060
|
+
ignoredAllows: allowed2.ignored
|
|
17061
|
+
};
|
|
17062
|
+
const file = summaryPath(paths, tree);
|
|
17063
|
+
runtime.writeText(file, `${JSON.stringify(summary, null, 2)}
|
|
17064
|
+
`);
|
|
17065
|
+
const task = activeTaskId(runtime, paths) ?? IMPLICIT_ID;
|
|
17066
|
+
appendEvidence(runtime, paths.evidence(task), [
|
|
17067
|
+
{
|
|
17068
|
+
schema: EVIDENCE_SCHEMA,
|
|
17069
|
+
task,
|
|
17070
|
+
check: LOOK_CHECK,
|
|
17071
|
+
kind: "look",
|
|
17072
|
+
status: failures === 0 ? "passed" : "failed",
|
|
17073
|
+
treeHash: tree,
|
|
17074
|
+
at: summary.at,
|
|
17075
|
+
notes: [{ key: "look.recorded", params: { failures, captures: summary.captures.length } }]
|
|
17076
|
+
}
|
|
17077
|
+
]);
|
|
17078
|
+
return { kind: "done", summary, summaryPath: projectRelative(paths, file) };
|
|
17079
|
+
};
|
|
17080
|
+
|
|
17081
|
+
// src/look/run.ts
|
|
17082
|
+
var prepare = (runtime, cwd) => {
|
|
17083
|
+
const root = gitRoot(gitAt(runtime, cwd));
|
|
17084
|
+
if (root === void 0) return { kind: "not_a_repo" };
|
|
17085
|
+
const tree = treeHash(gitAt(runtime, root));
|
|
17086
|
+
const config = loadLookConfig(runtime, root);
|
|
17087
|
+
if (tree === void 0) return { kind: "not_a_repo" };
|
|
17088
|
+
if (config?.port === void 0) return { kind: "not_configured" };
|
|
17089
|
+
return { root, tree, config: { ...config, port: config.port } };
|
|
17090
|
+
};
|
|
17091
|
+
var bootApp = async (ready) => {
|
|
17092
|
+
const { config } = ready;
|
|
17093
|
+
if (await portOpen(config.host, config.port)) return void 0;
|
|
17094
|
+
if (config.start === void 0) return { detail: `nothing listens on ${config.host}:${String(config.port)}` };
|
|
17095
|
+
const app = startApp(config.start, ready.root, config.port);
|
|
17096
|
+
const state = await waitForPort(app, config.host, config.port, Date.now() + config.timeoutS * 1e3);
|
|
17097
|
+
if (state === "open") return app;
|
|
17098
|
+
await stopApp(app);
|
|
17099
|
+
return {
|
|
17100
|
+
detail: state === "exited" ? `the app exited with code ${String(app.exited()?.code ?? -1)}` : "port timeout"
|
|
17101
|
+
};
|
|
17102
|
+
};
|
|
17103
|
+
var capture = async (ready) => {
|
|
17104
|
+
const playwright = await loadPlaywright(ready.root);
|
|
17105
|
+
if (playwright === void 0) return { kind: "playwright_missing" };
|
|
17106
|
+
const browser = await launchBrowser(playwright);
|
|
17107
|
+
if (browser === void 0) return { kind: "browser_missing" };
|
|
17108
|
+
try {
|
|
17109
|
+
const dir = lookDir(fmPaths(ready.root), ready.tree);
|
|
17110
|
+
const results = await captureAll(browser, {
|
|
17111
|
+
baseUrl: `http://${ready.config.host}:${String(ready.config.port)}`,
|
|
17112
|
+
routes: lookRoutes(ready.config),
|
|
17113
|
+
viewports: VIEWPORTS,
|
|
17114
|
+
dir,
|
|
17115
|
+
timeoutMs: ready.config.timeoutS * 1e3
|
|
17116
|
+
});
|
|
17117
|
+
return { results, sheet: await contactSheet(browser, results, dir) };
|
|
17118
|
+
} finally {
|
|
17119
|
+
await browser.close();
|
|
17120
|
+
}
|
|
17121
|
+
};
|
|
17122
|
+
var runLook = async (runtime, cwd) => {
|
|
17123
|
+
const ready = prepare(runtime, cwd);
|
|
17124
|
+
if ("kind" in ready) return ready;
|
|
17125
|
+
const app = await bootApp(ready);
|
|
17126
|
+
if (app !== void 0 && "detail" in app) return { kind: "app_failed", detail: app.detail };
|
|
17127
|
+
try {
|
|
17128
|
+
const shot = await capture(ready);
|
|
17129
|
+
if ("kind" in shot) return shot;
|
|
17130
|
+
const paths = fmPaths(ready.root);
|
|
17131
|
+
const approvals = readApprovals(runtime, paths);
|
|
17132
|
+
const allowed2 = applyAllows(
|
|
17133
|
+
shot.results.flatMap(pageFindings),
|
|
17134
|
+
ready.config.allow,
|
|
17135
|
+
(decision) => approvals.some((approval) => approval.check === decision)
|
|
17136
|
+
);
|
|
17137
|
+
return recordLook(runtime, paths, ready.tree, shot, allowed2);
|
|
17138
|
+
} finally {
|
|
17139
|
+
if (app !== void 0) await stopApp(app);
|
|
17140
|
+
}
|
|
17141
|
+
};
|
|
17142
|
+
|
|
17143
|
+
// src/commands/look.ts
|
|
17144
|
+
var MAX_FINDINGS = 10;
|
|
17145
|
+
var setupError = (ctx, run4) => {
|
|
17146
|
+
const fix = run4.kind === "playwright_missing" || run4.kind === "browser_missing" ? "npm install --save-dev playwright && npx playwright install chromium" : run4.kind === "not_configured" ? `echo "port: 3000" > ${LOOK_CONFIG}` : "finitemonkey look";
|
|
17147
|
+
const message = ctx.t(`look.error.${run4.kind}`, { detail: run4.kind === "app_failed" ? run4.detail : "" });
|
|
17148
|
+
return {
|
|
17149
|
+
ok: false,
|
|
17150
|
+
exitCode: run4.kind === "app_failed" ? ExitCode.CheckFailed : ExitCode.NotConfigured,
|
|
17151
|
+
human: [message],
|
|
17152
|
+
error: { code: run4.kind === "app_failed" ? "look_failed" : "look_unavailable", message, fix }
|
|
17153
|
+
};
|
|
17154
|
+
};
|
|
17155
|
+
var dataOf = (run4) => ({
|
|
17156
|
+
schema: LOOK_SCHEMA,
|
|
17157
|
+
treeHash: run4.summary.treeHash,
|
|
17158
|
+
failures: run4.summary.failures,
|
|
17159
|
+
notes: run4.summary.notes,
|
|
17160
|
+
captures: run4.summary.captures.map((capture2) => capture2.path),
|
|
17161
|
+
contactSheet: run4.summary.contactSheet,
|
|
17162
|
+
summary: run4.summaryPath,
|
|
17163
|
+
findings: run4.summary.findings.slice(0, MAX_FINDINGS).map(({ rule, route, viewport, detail, blocking: blocking2 }) => ({ rule, route, viewport, detail, blocking: blocking2 }))
|
|
17164
|
+
});
|
|
17165
|
+
var doneResult = (ctx, data) => {
|
|
17166
|
+
const human2 = [
|
|
17167
|
+
...data.captures,
|
|
17168
|
+
...data.contactSheet === null ? [] : [data.contactSheet],
|
|
17169
|
+
...data.findings.map((item2) => `${item2.rule} ${item2.route} @${String(item2.viewport)}: ${item2.detail}`),
|
|
17170
|
+
ctx.t("look.done", { failures: data.failures, count: data.captures.length })
|
|
17171
|
+
];
|
|
17172
|
+
if (data.failures === 0) return success(data, human2);
|
|
17173
|
+
const message = ctx.t("look.done", { failures: data.failures, count: data.captures.length });
|
|
17174
|
+
return {
|
|
17175
|
+
ok: false,
|
|
17176
|
+
exitCode: ExitCode.CheckFailed,
|
|
17177
|
+
data,
|
|
17178
|
+
human: human2,
|
|
17179
|
+
error: { code: "look_failed", message, fix: "finitemonkey look" }
|
|
17180
|
+
};
|
|
17181
|
+
};
|
|
17182
|
+
var lookCommand = {
|
|
17183
|
+
path: ["look"],
|
|
17184
|
+
summaryKey: "command.look.summary",
|
|
17185
|
+
usage: "finitemonkey look",
|
|
17186
|
+
status: "ready",
|
|
17187
|
+
run: async (ctx) => {
|
|
17188
|
+
const run4 = await runLook(ctx.runtime, ctx.cwd);
|
|
17189
|
+
return run4.kind === "done" ? doneResult(ctx, dataOf(run4)) : setupError(ctx, run4);
|
|
17190
|
+
}
|
|
17191
|
+
};
|
|
17192
|
+
|
|
16601
17193
|
// src/commands/pause.ts
|
|
16602
17194
|
var toggle = (ctx, off) => {
|
|
16603
17195
|
const command = off ? "finitemonkey off" : "finitemonkey on";
|
|
@@ -16654,7 +17246,7 @@ var policyFalsePositiveCommand = {
|
|
|
16654
17246
|
};
|
|
16655
17247
|
|
|
16656
17248
|
// src/commands/prose-check.ts
|
|
16657
|
-
import { relative as
|
|
17249
|
+
import { relative as relative10 } from "node:path";
|
|
16658
17250
|
|
|
16659
17251
|
// src/commands/prose-view.ts
|
|
16660
17252
|
var PROSE_CHECK_SCHEMA = "finitemonkey/prose-check@1";
|
|
@@ -16714,7 +17306,7 @@ var problem2 = (code, messageKey, params2) => ({
|
|
|
16714
17306
|
fix: "finitemonkey help prose check",
|
|
16715
17307
|
exitCode: ExitCode.Usage
|
|
16716
17308
|
});
|
|
16717
|
-
var underRoot = (root, path) => toPosixPath(
|
|
17309
|
+
var underRoot = (root, path) => toPosixPath(relative10(root, path));
|
|
16718
17310
|
var expand2 = (ctx, root, target2) => {
|
|
16719
17311
|
const path = toAbsolutePath(target2, ctx.cwd);
|
|
16720
17312
|
const stat = ctx.runtime.stat(path);
|
|
@@ -16834,14 +17426,14 @@ var inputProblem = (ctx, id, reason, days) => {
|
|
|
16834
17426
|
}
|
|
16835
17427
|
return void 0;
|
|
16836
17428
|
};
|
|
16837
|
-
var entryOf3 = (ctx,
|
|
17429
|
+
var entryOf3 = (ctx, finding4, args2) => {
|
|
16838
17430
|
const now = ctx.runtime.now();
|
|
16839
17431
|
return {
|
|
16840
|
-
id:
|
|
16841
|
-
rule:
|
|
16842
|
-
file:
|
|
16843
|
-
line:
|
|
16844
|
-
fingerprint:
|
|
17432
|
+
id: finding4.id,
|
|
17433
|
+
rule: finding4.ruleId,
|
|
17434
|
+
file: finding4.file,
|
|
17435
|
+
line: finding4.line,
|
|
17436
|
+
fingerprint: finding4.fingerprint,
|
|
16845
17437
|
state: args2.falsePositive ? "falso-positivo" : "aceito",
|
|
16846
17438
|
owner: personName(ctx),
|
|
16847
17439
|
reason: args2.reason,
|
|
@@ -16856,8 +17448,8 @@ var acceptEntry = (ctx, args2) => {
|
|
|
16856
17448
|
return failure3(ctx, baselineInvalid(baseline.error));
|
|
16857
17449
|
}
|
|
16858
17450
|
const scanned = scanPath(ctx.runtime, root, ".");
|
|
16859
|
-
const
|
|
16860
|
-
if (
|
|
17451
|
+
const finding4 = scanned.ok ? scanned.value.find((item2) => item2.id === args2.id) : void 0;
|
|
17452
|
+
if (finding4 === void 0) {
|
|
16861
17453
|
return failure3(
|
|
16862
17454
|
ctx,
|
|
16863
17455
|
usageProblem("secrets_finding_not_found", "secrets.error.finding_not_found", "finitemonkey secrets scan", {
|
|
@@ -16865,7 +17457,7 @@ var acceptEntry = (ctx, args2) => {
|
|
|
16865
17457
|
})
|
|
16866
17458
|
);
|
|
16867
17459
|
}
|
|
16868
|
-
const entry2 = entryOf3(ctx,
|
|
17460
|
+
const entry2 = entryOf3(ctx, finding4, args2);
|
|
16869
17461
|
const entries = [...baseline.value.entries.filter((item2) => item2.id !== entry2.id), entry2];
|
|
16870
17462
|
const saved = saveBaseline(ctx.runtime, root, { schema: baseline.value.schema, entries });
|
|
16871
17463
|
if (!saved.ok) {
|
|
@@ -16874,7 +17466,7 @@ var acceptEntry = (ctx, args2) => {
|
|
|
16874
17466
|
const line2 = ctx.t("secrets.accept.done", {
|
|
16875
17467
|
id: entry2.id,
|
|
16876
17468
|
rule: entry2.rule,
|
|
16877
|
-
where: whereOf(
|
|
17469
|
+
where: whereOf(finding4),
|
|
16878
17470
|
state: entry2.state,
|
|
16879
17471
|
expires: entry2.expiresAt.slice(0, 10),
|
|
16880
17472
|
owner: entry2.owner
|
|
@@ -16908,7 +17500,7 @@ var secretsAcceptCommand = {
|
|
|
16908
17500
|
};
|
|
16909
17501
|
|
|
16910
17502
|
// src/secrets/runbooks.ts
|
|
16911
|
-
import { dirname as dirname9, join as
|
|
17503
|
+
import { dirname as dirname9, join as join45 } from "node:path";
|
|
16912
17504
|
var PROVIDER_OF_RULE = {
|
|
16913
17505
|
"aws-access-token": "aws",
|
|
16914
17506
|
"aws-secret-access-key": "aws",
|
|
@@ -16930,7 +17522,7 @@ var RUNBOOKS_URL = "https://github.com/gabrielvieiraf/finitemonkey/blob/main/run
|
|
|
16930
17522
|
var runbookProvider = (rule) => PROVIDER_OF_RULE[rule] ?? "generic";
|
|
16931
17523
|
var runbookLink = (runtime, rule) => {
|
|
16932
17524
|
const file = `${runbookProvider(rule)}.md`;
|
|
16933
|
-
const local = toAbsolutePath(
|
|
17525
|
+
const local = toAbsolutePath(join45(dirname9(dirname9(runtime.selfPath)), "runbooks", file));
|
|
16934
17526
|
return runtime.stat(local)?.kind === "file" ? local : `${RUNBOOKS_URL}/${file}`;
|
|
16935
17527
|
};
|
|
16936
17528
|
|
|
@@ -17167,17 +17759,17 @@ var secretsLiveCommand = {
|
|
|
17167
17759
|
|
|
17168
17760
|
// src/commands/secrets-scan.ts
|
|
17169
17761
|
var SECRETS_SCAN_SCHEMA = "finitemonkey/secrets-scan@1";
|
|
17170
|
-
var view = (
|
|
17171
|
-
id:
|
|
17172
|
-
rule:
|
|
17173
|
-
severity:
|
|
17174
|
-
file:
|
|
17175
|
-
line:
|
|
17176
|
-
column:
|
|
17177
|
-
fingerprint:
|
|
17178
|
-
mask: maskToken(
|
|
17762
|
+
var view = (finding4, status) => ({
|
|
17763
|
+
id: finding4.id,
|
|
17764
|
+
rule: finding4.ruleId,
|
|
17765
|
+
severity: finding4.severity,
|
|
17766
|
+
file: finding4.file,
|
|
17767
|
+
line: finding4.line,
|
|
17768
|
+
column: finding4.column,
|
|
17769
|
+
fingerprint: finding4.fingerprint,
|
|
17770
|
+
mask: maskToken(finding4),
|
|
17179
17771
|
status,
|
|
17180
|
-
...
|
|
17772
|
+
...finding4.commit === void 0 ? {} : { commit: finding4.commit }
|
|
17181
17773
|
});
|
|
17182
17774
|
var modeOf3 = (values) => {
|
|
17183
17775
|
const chosen = [values["staged"] === true, values["history"] === true, stringOption(values, "path") !== void 0];
|
|
@@ -17195,24 +17787,24 @@ var runScan = async (ctx, root, mode, target2) => {
|
|
|
17195
17787
|
if (mode === "history") return scanHistory(ctx.runtime, root);
|
|
17196
17788
|
return scanPath(ctx.runtime, root, normalizedTarget(target2) || ".");
|
|
17197
17789
|
};
|
|
17198
|
-
var historyIncidents = (ctx, root, fresh3) => fresh3.flatMap((
|
|
17199
|
-
if (ruleById(
|
|
17790
|
+
var historyIncidents = (ctx, root, fresh3) => fresh3.flatMap((finding4) => {
|
|
17791
|
+
if (ruleById(finding4.ruleId)?.kind !== "credential") return [];
|
|
17200
17792
|
const draft = {
|
|
17201
|
-
rule:
|
|
17202
|
-
fingerprint:
|
|
17203
|
-
where: whereOf(
|
|
17793
|
+
rule: finding4.ruleId,
|
|
17794
|
+
fingerprint: finding4.fingerprint,
|
|
17795
|
+
where: whereOf(finding4),
|
|
17204
17796
|
source: "history",
|
|
17205
17797
|
state: "aberto",
|
|
17206
|
-
...
|
|
17798
|
+
...finding4.commit === void 0 ? {} : { firstCommit: finding4.commit }
|
|
17207
17799
|
};
|
|
17208
17800
|
const saved = recordIncident(ctx.runtime, root, draft, HOOK_ACTOR);
|
|
17209
17801
|
return saved.ok ? [saved.value.id] : [];
|
|
17210
17802
|
});
|
|
17211
|
-
var
|
|
17803
|
+
var dataOf2 = (mode, outcome2, incidents) => {
|
|
17212
17804
|
const findings = [
|
|
17213
|
-
...outcome2.fresh.map((
|
|
17214
|
-
...outcome2.known.map((
|
|
17215
|
-
...outcome2.expired.map((
|
|
17805
|
+
...outcome2.fresh.map((finding4) => view(finding4, "new")),
|
|
17806
|
+
...outcome2.known.map((finding4) => view(finding4, "known")),
|
|
17807
|
+
...outcome2.expired.map((finding4) => view(finding4, "expired"))
|
|
17216
17808
|
];
|
|
17217
17809
|
return {
|
|
17218
17810
|
schema: SECRETS_SCAN_SCHEMA,
|
|
@@ -17287,12 +17879,12 @@ var secretsScanCommand = {
|
|
|
17287
17879
|
);
|
|
17288
17880
|
if (mode === "path") prune(ctx, root, baseline.value, outcome2);
|
|
17289
17881
|
const incidents = mode === "history" ? historyIncidents(ctx, root, outcome2.fresh) : [];
|
|
17290
|
-
return resultOf3(ctx,
|
|
17882
|
+
return resultOf3(ctx, dataOf2(mode, outcome2, incidents));
|
|
17291
17883
|
}
|
|
17292
17884
|
};
|
|
17293
17885
|
|
|
17294
17886
|
// src/task/create.ts
|
|
17295
|
-
import { join as
|
|
17887
|
+
import { join as join46 } from "node:path";
|
|
17296
17888
|
|
|
17297
17889
|
// src/core/yaml/write.ts
|
|
17298
17890
|
var PLAIN_SAFE = /^[A-Za-z_./][A-Za-z0-9_./ ()-]*$/;
|
|
@@ -17380,11 +17972,11 @@ var explicitChecks = (draft) => [
|
|
|
17380
17972
|
];
|
|
17381
17973
|
var draftChecks = (runtime, paths, draft) => {
|
|
17382
17974
|
const disk = {
|
|
17383
|
-
read: (
|
|
17384
|
-
const
|
|
17385
|
-
return
|
|
17975
|
+
read: (relative11) => {
|
|
17976
|
+
const text7 = runtime.readText(toAbsolutePath(join46(paths.root, relative11)));
|
|
17977
|
+
return text7.ok ? text7.value : void 0;
|
|
17386
17978
|
},
|
|
17387
|
-
exists: (
|
|
17979
|
+
exists: (relative11) => runtime.stat(toAbsolutePath(join46(paths.root, relative11))) !== void 0
|
|
17388
17980
|
};
|
|
17389
17981
|
const detected = detectChecks(disk);
|
|
17390
17982
|
const base = draft.runs.length > 0 || draft.tests !== void 0 ? explicitChecks(draft) : detected;
|
|
@@ -17399,6 +17991,7 @@ var draftChecks = (runtime, paths, draft) => {
|
|
|
17399
17991
|
{ id: "merge", kind: "merge" },
|
|
17400
17992
|
{ id: "callers", kind: "callers" },
|
|
17401
17993
|
...draft.paths.length > 0 || draft.budget !== void 0 ? [{ id: "diff", kind: "diff" }] : [],
|
|
17994
|
+
...runtime.stat(toAbsolutePath(join46(paths.root, LOOK_CONFIG))) === void 0 ? [] : [{ id: "look", kind: "look" }],
|
|
17402
17995
|
...draft.git.length > 0 ? [{ id: "git", kind: "git", require: draft.git }] : [],
|
|
17403
17996
|
{ id: "pr", kind: "pr", requireOpen: draft.git.includes("pr_open") },
|
|
17404
17997
|
...draft.manual.map((description, index) => ({
|
|
@@ -17424,19 +18017,19 @@ var createTask = (runtime, paths, draft) => {
|
|
|
17424
18017
|
decisionsNeeded: [],
|
|
17425
18018
|
implicit: false
|
|
17426
18019
|
};
|
|
17427
|
-
const
|
|
18020
|
+
const text7 = stringifyYaml(contractYaml(shape));
|
|
17428
18021
|
const valid = validateContract(contractYaml(shape));
|
|
17429
18022
|
if (!valid.ok) return reject({ kind: "invalid", issues: valid.error });
|
|
17430
|
-
const wrote = runtime.writeText(paths.task(draft.id),
|
|
18023
|
+
const wrote = runtime.writeText(paths.task(draft.id), text7);
|
|
17431
18024
|
if (!wrote.ok) return reject({ kind: "write", detail: wrote.error });
|
|
17432
18025
|
const lock = readTrust(runtime, paths);
|
|
17433
18026
|
writeTrust(runtime, paths, {
|
|
17434
18027
|
...lock,
|
|
17435
18028
|
contracts: { ...lock.contracts, [draft.id]: contractHash(valid.value) },
|
|
17436
|
-
approved: { ...lock.approved, [draft.id]:
|
|
18029
|
+
approved: { ...lock.approved, [draft.id]: text7 }
|
|
17437
18030
|
});
|
|
17438
18031
|
writeState(runtime, paths, { ...readState(runtime, paths), active: draft.id });
|
|
17439
|
-
return accept({ contract: valid.value, text:
|
|
18032
|
+
return accept({ contract: valid.value, text: text7 });
|
|
17440
18033
|
};
|
|
17441
18034
|
|
|
17442
18035
|
// src/commands/task.ts
|
|
@@ -17587,7 +18180,7 @@ var taskDoneCommand = {
|
|
|
17587
18180
|
};
|
|
17588
18181
|
|
|
17589
18182
|
// src/install/githook-block.ts
|
|
17590
|
-
var withoutHookBlock = (
|
|
18183
|
+
var withoutHookBlock = (text7) => withoutManagedBlock(text7, { begin: BLOCK_BEGIN, end: BLOCK_END });
|
|
17591
18184
|
|
|
17592
18185
|
// src/install/uninstall.ts
|
|
17593
18186
|
var blockless = (kind, current2) => {
|
|
@@ -17596,8 +18189,8 @@ var blockless = (kind, current2) => {
|
|
|
17596
18189
|
};
|
|
17597
18190
|
var structural = (entry2, current2) => {
|
|
17598
18191
|
if (entry2.kind === "gitignore" || entry2.kind === "markdown" || entry2.kind === "githook") {
|
|
17599
|
-
const
|
|
17600
|
-
return
|
|
18192
|
+
const text7 = blockless(entry2.kind, current2);
|
|
18193
|
+
return text7.trim() === "" && !entry2.existed ? void 0 : text7;
|
|
17601
18194
|
}
|
|
17602
18195
|
const parsed = parseSettings(current2);
|
|
17603
18196
|
if (!parsed.ok || entry2.added === void 0) return current2;
|
|
@@ -17636,8 +18229,8 @@ var applyItem = (project, item2) => {
|
|
|
17636
18229
|
if (item2.after === void 0 || item2.action === "gone" || item2.action.startsWith("keep")) return accept(void 0);
|
|
17637
18230
|
return writeText(project, item2.path, item2.after);
|
|
17638
18231
|
};
|
|
17639
|
-
var isEmptyDir = (project,
|
|
17640
|
-
const listed4 = project.runtime.listDir(project.abs(
|
|
18232
|
+
var isEmptyDir = (project, relative11) => {
|
|
18233
|
+
const listed4 = project.runtime.listDir(project.abs(relative11));
|
|
17641
18234
|
return listed4.ok && listed4.value.length === 0;
|
|
17642
18235
|
};
|
|
17643
18236
|
var finish3 = (project, plan) => {
|
|
@@ -17669,7 +18262,7 @@ var OUTSIDE = [
|
|
|
17669
18262
|
command: "git for-each-ref --format='delete %(refname)' refs/finitemonkey/ | git update-ref --stdin"
|
|
17670
18263
|
}
|
|
17671
18264
|
];
|
|
17672
|
-
var
|
|
18265
|
+
var dataOf3 = (root, plan, applied) => ({
|
|
17673
18266
|
schema: UNINSTALL_SCHEMA,
|
|
17674
18267
|
applied,
|
|
17675
18268
|
purge: plan.purge,
|
|
@@ -17698,7 +18291,7 @@ var runUninstall = (ctx, args2) => {
|
|
|
17698
18291
|
if (lock.value === void 0) return failureFrom(ctx.t, notInstalled());
|
|
17699
18292
|
const options = { purge: args2.values["purge"] === true, keepGenerated: args2.values["keep-generated"] === true };
|
|
17700
18293
|
const plan = planRemoval(project, lock.value, options);
|
|
17701
|
-
const preview =
|
|
18294
|
+
const preview = dataOf3(project.root, plan, false);
|
|
17702
18295
|
if (ctx.dryRun) return success(preview, [...linesOf2(ctx, preview), ctx.t("install.dry_run")]);
|
|
17703
18296
|
if (!ctx.yes) {
|
|
17704
18297
|
const next2 = fixFor(args2);
|
|
@@ -17707,7 +18300,7 @@ var runUninstall = (ctx, args2) => {
|
|
|
17707
18300
|
}
|
|
17708
18301
|
const removed = applyRemoval(project, plan);
|
|
17709
18302
|
if (!removed.ok) return failureFrom(ctx.t, applyProblem(removed.error));
|
|
17710
|
-
const data =
|
|
18303
|
+
const data = dataOf3(project.root, plan, true);
|
|
17711
18304
|
return success(data, [...linesOf2(ctx, data), ctx.t("install.uninstall.done")]);
|
|
17712
18305
|
};
|
|
17713
18306
|
var uninstallCommand = {
|
|
@@ -17799,34 +18392,35 @@ var REGISTRY = [
|
|
|
17799
18392
|
commitMsgCommand,
|
|
17800
18393
|
pushCheckCommand,
|
|
17801
18394
|
ciGateCommand,
|
|
18395
|
+
lookCommand,
|
|
17802
18396
|
versionCommand,
|
|
17803
18397
|
helpCommand
|
|
17804
18398
|
];
|
|
17805
18399
|
|
|
17806
18400
|
// src/i18n/lang-settings.ts
|
|
17807
|
-
import { join as
|
|
18401
|
+
import { join as join47 } from "node:path";
|
|
17808
18402
|
var read3 = (runtime, path) => {
|
|
17809
|
-
const
|
|
17810
|
-
return
|
|
18403
|
+
const text7 = runtime.readText(toAbsolutePath(path));
|
|
18404
|
+
return text7.ok ? text7.value : void 0;
|
|
17811
18405
|
};
|
|
17812
18406
|
var configText = (tree, ...path) => {
|
|
17813
18407
|
const value = configAt(tree, ...path);
|
|
17814
18408
|
return typeof value === "string" && value !== "" ? value : void 0;
|
|
17815
18409
|
};
|
|
17816
|
-
var languageIn = (
|
|
17817
|
-
if (
|
|
17818
|
-
const parsed = parseJsonObject(
|
|
18410
|
+
var languageIn = (text7) => {
|
|
18411
|
+
if (text7 === void 0) return void 0;
|
|
18412
|
+
const parsed = parseJsonObject(text7);
|
|
17819
18413
|
const value = parsed.ok && isRecord(parsed.value) ? parsed.value["language"] : void 0;
|
|
17820
18414
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
17821
18415
|
};
|
|
17822
|
-
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ??
|
|
18416
|
+
var claudeDir = (runtime, env) => env["CLAUDE_CONFIG_DIR"] ?? join47(runtime.homeDir(), ".claude");
|
|
17823
18417
|
var langSettings = (runtime, cwd, env, platform) => {
|
|
17824
18418
|
const root = findGitRoot(cwd, runtime) ?? cwd;
|
|
17825
|
-
const config = parseConfigYaml(read3(runtime,
|
|
18419
|
+
const config = parseConfigYaml(read3(runtime, join47(root, CONFIG_FILE)) ?? "");
|
|
17826
18420
|
const claudeFiles = [
|
|
17827
|
-
|
|
17828
|
-
|
|
17829
|
-
|
|
18421
|
+
join47(root, ".claude", "settings.local.json"),
|
|
18422
|
+
join47(root, ".claude", "settings.json"),
|
|
18423
|
+
join47(claudeDir(runtime, env), "settings.json")
|
|
17830
18424
|
];
|
|
17831
18425
|
return {
|
|
17832
18426
|
config: configText(config, "lang"),
|
|
@@ -17913,8 +18507,8 @@ var resultFromThrown = (thrown, t) => {
|
|
|
17913
18507
|
};
|
|
17914
18508
|
var block3 = (items) => items.length > 0 ? `${items.join("\n")}
|
|
17915
18509
|
` : "";
|
|
17916
|
-
var write2 = (sink,
|
|
17917
|
-
if (
|
|
18510
|
+
var write2 = (sink, text7) => {
|
|
18511
|
+
if (text7 !== "") sink.write(maskText(text7));
|
|
17918
18512
|
};
|
|
17919
18513
|
var render2 = (ctx, command, result) => {
|
|
17920
18514
|
if (result.raw !== void 0) {
|
|
@@ -17979,20 +18573,20 @@ var run3 = async (options) => {
|
|
|
17979
18573
|
};
|
|
17980
18574
|
|
|
17981
18575
|
// src/core/version.ts
|
|
17982
|
-
import { fileURLToPath as
|
|
18576
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
17983
18577
|
var UNKNOWN_VERSION = "0.0.0-unknown";
|
|
17984
18578
|
var VERSION_SOURCES = ["../package.json", "../.claude-plugin/plugin.json"];
|
|
17985
|
-
var versionAt = (readText, moduleUrl,
|
|
17986
|
-
const
|
|
17987
|
-
if (!
|
|
17988
|
-
const parsed = parseJson(
|
|
18579
|
+
var versionAt = (readText, moduleUrl, relative11) => {
|
|
18580
|
+
const text7 = readText(toAbsolutePath(fileURLToPath4(new URL(relative11, moduleUrl))));
|
|
18581
|
+
if (!text7.ok) return void 0;
|
|
18582
|
+
const parsed = parseJson(text7.value);
|
|
17989
18583
|
if (!parsed.ok || !isRecord(parsed.value)) return void 0;
|
|
17990
18584
|
const version = parsed.value["version"];
|
|
17991
18585
|
return typeof version === "string" ? version : void 0;
|
|
17992
18586
|
};
|
|
17993
18587
|
var readPackageVersion = (readText, moduleUrl) => {
|
|
17994
|
-
for (const
|
|
17995
|
-
const version = versionAt(readText, moduleUrl,
|
|
18588
|
+
for (const relative11 of VERSION_SOURCES) {
|
|
18589
|
+
const version = versionAt(readText, moduleUrl, relative11);
|
|
17996
18590
|
if (version !== void 0) return version;
|
|
17997
18591
|
}
|
|
17998
18592
|
return UNKNOWN_VERSION;
|