enigma-cli 1.63.5 → 1.64.0
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/assets/memory/AGENTS.md +1 -1
- package/assets/memory/CLAUDE.md +1 -1
- package/assets/skills/anti-overengineering-policy/skill.json +1 -1
- package/assets/skills/anti-overengineering-review/skill.json +1 -1
- package/assets/skills/backend-policy/SKILL.md +14 -1
- package/assets/skills/backend-policy/skill.json +5 -5
- package/assets/skills/ci-policy/skill.json +1 -1
- package/assets/skills/ciphera-style-policy/skill.json +1 -1
- package/assets/skills/code-review-policy/skill.json +1 -1
- package/assets/skills/core-engineering-policy/skill.json +1 -1
- package/assets/skills/database-expert/skill.json +1 -1
- package/assets/skills/debugging-policy/skill.json +1 -1
- package/assets/skills/dependency-policy/skill.json +1 -1
- package/assets/skills/email-policy/skill.json +1 -1
- package/assets/skills/frontend-design/skill.json +1 -1
- package/assets/skills/frontend-policy/SKILL.md +4 -1
- package/assets/skills/frontend-policy/skill.json +4 -4
- package/assets/skills/git-policy/skill.json +1 -1
- package/assets/skills/logo-sourcing-policy/skill.json +1 -1
- package/assets/skills/security-policy/SKILL.md +2 -0
- package/assets/skills/security-policy/skill.json +3 -3
- package/assets/skills/skill-creator/skill.json +1 -1
- package/assets/skills/task-completion-policy/skill.json +1 -1
- package/assets/skills/technical-writing-policy/skill.json +1 -1
- package/assets/skills/testing-policy/SKILL.md +1 -0
- package/assets/skills/testing-policy/skill.json +3 -3
- package/assets/skills/validation-policy/skill.json +1 -1
- package/bin/checksums.json +4 -4
- package/dist/guard.js +0 -0
- package/dist/guardrails.js +194 -24
- package/dist/post-edit.js +241 -68
- package/package.json +1 -1
package/dist/post-edit.js
CHANGED
|
@@ -573,6 +573,44 @@ var init_trim = __esm({
|
|
|
573
573
|
}
|
|
574
574
|
});
|
|
575
575
|
|
|
576
|
+
// src/private-terms.ts
|
|
577
|
+
import { join as join5 } from "path";
|
|
578
|
+
import { homedir as homedir2 } from "os";
|
|
579
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
580
|
+
function readPrivateTerms() {
|
|
581
|
+
try {
|
|
582
|
+
const raw = JSON.parse(readFileSync4(join5(process.env.ENIGMA_CONFIG_HOME || homedir2(), ".enigma-guard.json"), "utf8"));
|
|
583
|
+
if (!Array.isArray(raw.privateTerms)) return [];
|
|
584
|
+
return raw.privateTerms.filter((t) => typeof t === "string" && t.trim().length >= 3);
|
|
585
|
+
} catch {
|
|
586
|
+
return [];
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
function privateTermMatcher(terms) {
|
|
590
|
+
const anyCase = (ch) => {
|
|
591
|
+
const lo = ch.toLowerCase(), up = ch.toUpperCase();
|
|
592
|
+
return lo === up ? ch.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&") : `[${lo}${up}]`;
|
|
593
|
+
};
|
|
594
|
+
const parts = terms.map((t) => t.trim().split(/[\s_-]+/).filter(Boolean).map((w) => [...w].map(anyCase).join("")).join("[\\s_-]*")).filter(Boolean);
|
|
595
|
+
if (!parts.length) return null;
|
|
596
|
+
return new RegExp(`(?:(?<![\\p{L}\\p{N}])|(?<=\\p{Ll})(?=\\p{Lu}))(?:${parts.join("|")})(?!\\p{Ll})`, "u");
|
|
597
|
+
}
|
|
598
|
+
function hasPrivateTerm(line, re) {
|
|
599
|
+
return re.test(line.replace(/\b[a-z][a-z0-9+.-]*:\/\/\S+/gi, " "));
|
|
600
|
+
}
|
|
601
|
+
function privateTermLines(text, re) {
|
|
602
|
+
const out = [];
|
|
603
|
+
text.split("\n").forEach((line, i) => {
|
|
604
|
+
if (hasPrivateTerm(line, re)) out.push(i + 1);
|
|
605
|
+
});
|
|
606
|
+
return out;
|
|
607
|
+
}
|
|
608
|
+
var init_private_terms = __esm({
|
|
609
|
+
"src/private-terms.ts"() {
|
|
610
|
+
"use strict";
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
|
|
576
614
|
// src/guardrails.ts
|
|
577
615
|
var guardrails_exports = {};
|
|
578
616
|
__export(guardrails_exports, {
|
|
@@ -617,9 +655,11 @@ __export(guardrails_exports, {
|
|
|
617
655
|
pageAwaitWithoutBoundary: () => pageAwaitWithoutBoundary,
|
|
618
656
|
pathJoinedFromInput: () => pathJoinedFromInput,
|
|
619
657
|
poweredByBuzzword: () => poweredByBuzzword,
|
|
658
|
+
privateTermLeak: () => privateTermLeak,
|
|
620
659
|
purpleGradient: () => purpleGradient,
|
|
621
660
|
queryInLoop: () => queryInLoop,
|
|
622
661
|
radixContentWithoutPortal: () => radixContentWithoutPortal,
|
|
662
|
+
rawImgOverComponent: () => rawImgOverComponent,
|
|
623
663
|
readLedger: () => readLedger,
|
|
624
664
|
readReplyLedger: () => readReplyLedger,
|
|
625
665
|
recordFindings: () => recordFindings,
|
|
@@ -654,11 +694,11 @@ __export(guardrails_exports, {
|
|
|
654
694
|
viewBlankedWhileLoading: () => viewBlankedWhileLoading,
|
|
655
695
|
wideNamedImports: () => wideNamedImports
|
|
656
696
|
});
|
|
657
|
-
import { homedir as
|
|
697
|
+
import { homedir as homedir3 } from "os";
|
|
658
698
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
659
699
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
660
|
-
import { dirname as dirname2, join as
|
|
661
|
-
import { appendFileSync, mkdirSync as mkdirSync2, readFileSync as
|
|
700
|
+
import { dirname as dirname2, join as join6, resolve as resolve2, sep as sep2 } from "path";
|
|
701
|
+
import { appendFileSync, mkdirSync as mkdirSync2, readFileSync as readFileSync5, readdirSync as readdirSync2, writeFileSync as writeFileSync3, statSync as statSync2, existsSync as existsSync4 } from "fs";
|
|
662
702
|
function newPasswordAffordanceOnSignIn(content) {
|
|
663
703
|
if (!CURRENT_PASSWORD.test(content) || NEW_PASSWORD.test(content)) return [];
|
|
664
704
|
const out = [];
|
|
@@ -676,7 +716,7 @@ function applyFixes(file, findings, stage2 = "edit") {
|
|
|
676
716
|
if (!fixable.length) return { fixed: [], remaining: findings };
|
|
677
717
|
let content;
|
|
678
718
|
try {
|
|
679
|
-
content =
|
|
719
|
+
content = readFileSync5(file, "utf8");
|
|
680
720
|
} catch {
|
|
681
721
|
return { fixed: [], remaining: findings };
|
|
682
722
|
}
|
|
@@ -1002,6 +1042,63 @@ function nativeSelectOverPrimitive(content) {
|
|
|
1002
1042
|
}
|
|
1003
1043
|
return [];
|
|
1004
1044
|
}
|
|
1045
|
+
function projectImageComponent(root) {
|
|
1046
|
+
const cached = imageComponentCache.get(root);
|
|
1047
|
+
if (cached !== void 0) return cached;
|
|
1048
|
+
let deps = {};
|
|
1049
|
+
try {
|
|
1050
|
+
deps = JSON.parse(readFileSync5(join6(root, "package.json"), "utf8")).dependencies ?? {};
|
|
1051
|
+
} catch {
|
|
1052
|
+
}
|
|
1053
|
+
let found = null;
|
|
1054
|
+
if ("@enigmax/primitives" in deps) found = "`Image` from @enigmax/primitives (`enigma add image`)";
|
|
1055
|
+
else if ("next" in deps) found = "`Image` from next/image";
|
|
1056
|
+
else if ("astro" in deps) found = "`Image` from astro:assets";
|
|
1057
|
+
else if ("@nuxt/image" in deps) found = "`NuxtImg` from @nuxt/image";
|
|
1058
|
+
else {
|
|
1059
|
+
for (const dir of LOCAL_IMAGE_COMPONENTS) {
|
|
1060
|
+
let names = [];
|
|
1061
|
+
try {
|
|
1062
|
+
names = readdirSync2(join6(root, dir));
|
|
1063
|
+
} catch {
|
|
1064
|
+
continue;
|
|
1065
|
+
}
|
|
1066
|
+
const hit = names.find((n) => /^(?:image|img|picture)\.(?:tsx|jsx|vue|svelte|astro)$/i.test(n));
|
|
1067
|
+
if (hit) {
|
|
1068
|
+
found = `the project's own \`${dir}/${hit}\``;
|
|
1069
|
+
break;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
imageComponentCache.set(root, found);
|
|
1074
|
+
return found;
|
|
1075
|
+
}
|
|
1076
|
+
function imgElementAt(lines, start) {
|
|
1077
|
+
const parts = [];
|
|
1078
|
+
for (let i = start; i < lines.length && i < start + 30; i++) {
|
|
1079
|
+
const text = i === start ? lines[i].slice(lines[i].search(RAW_IMG)) : lines[i];
|
|
1080
|
+
parts.push(text);
|
|
1081
|
+
if (/(?<!=)>/.test(text)) break;
|
|
1082
|
+
}
|
|
1083
|
+
return parts.join("\n");
|
|
1084
|
+
}
|
|
1085
|
+
function rawImgOverComponent(content, file) {
|
|
1086
|
+
const norm = file.replace(/\\/g, "/");
|
|
1087
|
+
if (!RAW_IMG.test(content) || IMAGE_COMPONENT_FILE.test(norm) || DEFINES_IMAGE.test(content)) return [];
|
|
1088
|
+
const root = findProjectRoot(file);
|
|
1089
|
+
const component = root ? projectImageComponent(root) : null;
|
|
1090
|
+
if (!component) return [];
|
|
1091
|
+
const lines = content.split("\n");
|
|
1092
|
+
const hits = [];
|
|
1093
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1094
|
+
const line = lines[i];
|
|
1095
|
+
if (!RAW_IMG.test(line) || COMMENT_LINE.test(line) || allowedAt(lines, i, ALLOW_RAW_IMG)) continue;
|
|
1096
|
+
const element = imgElementAt(lines, i);
|
|
1097
|
+
if (DECORATIVE_IMG.test(element) || ALLOW_RAW_IMG.test(element)) continue;
|
|
1098
|
+
hits.push({ line: i + 1, detail: `this project already has ${component}` });
|
|
1099
|
+
}
|
|
1100
|
+
return hits;
|
|
1101
|
+
}
|
|
1005
1102
|
function handRolledToast(content) {
|
|
1006
1103
|
if (TOAST_MITIGATED.test(content)) return [];
|
|
1007
1104
|
if (!TOAST_QUEUE.test(content) || !TOAST_TIMER.test(content)) return [];
|
|
@@ -1082,11 +1179,11 @@ function nearestTsconfig(file) {
|
|
|
1082
1179
|
return cached;
|
|
1083
1180
|
}
|
|
1084
1181
|
seen.push(dir);
|
|
1085
|
-
const candidate =
|
|
1182
|
+
const candidate = join6(dir, "tsconfig.json");
|
|
1086
1183
|
if (existsSync4(candidate)) {
|
|
1087
1184
|
let found = null;
|
|
1088
1185
|
try {
|
|
1089
|
-
found = { dir, text:
|
|
1186
|
+
found = { dir, text: readFileSync5(candidate, "utf8") };
|
|
1090
1187
|
} catch {
|
|
1091
1188
|
found = null;
|
|
1092
1189
|
}
|
|
@@ -1149,14 +1246,14 @@ function deepRelativeImports(content, file) {
|
|
|
1149
1246
|
function missingPathAlias(content, file) {
|
|
1150
1247
|
if (/["'](?:paths|extends)["']\s*:/.test(content)) return [];
|
|
1151
1248
|
const dir = dirname2(resolve2(file));
|
|
1152
|
-
const src = ["src", "app", "lib"].find((d) => existsSync4(
|
|
1249
|
+
const src = ["src", "app", "lib"].find((d) => existsSync4(join6(dir, d)));
|
|
1153
1250
|
if (!src) return [];
|
|
1154
1251
|
const anchor = content.split("\n").findIndex((l) => /["']compilerOptions["']/.test(l));
|
|
1155
1252
|
return [{ line: anchor === -1 ? 1 : anchor + 1, detail: `no alias for ./${src}` }];
|
|
1156
1253
|
}
|
|
1157
1254
|
function operatorHome() {
|
|
1158
1255
|
const env = process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME;
|
|
1159
|
-
return env?.trim() ||
|
|
1256
|
+
return env?.trim() || homedir3();
|
|
1160
1257
|
}
|
|
1161
1258
|
function operatorHomePathLeak(content) {
|
|
1162
1259
|
const re = operatorHomeRegex("i");
|
|
@@ -1168,6 +1265,14 @@ function operatorHomePathLeak(content) {
|
|
|
1168
1265
|
}
|
|
1169
1266
|
return out;
|
|
1170
1267
|
}
|
|
1268
|
+
function privateTermLeak(content) {
|
|
1269
|
+
const terms = readPrivateTerms();
|
|
1270
|
+
const key = terms.join("\n");
|
|
1271
|
+
if (privateTermCache?.key !== key) privateTermCache = { key, re: privateTermMatcher(terms) };
|
|
1272
|
+
const re = privateTermCache.re;
|
|
1273
|
+
if (re === null) return [];
|
|
1274
|
+
return privateTermLines(content, re).map((line) => ({ line, detail: "a private term from your list" }));
|
|
1275
|
+
}
|
|
1171
1276
|
function redactOperatorHome(text) {
|
|
1172
1277
|
const re = operatorHomeRegex("gi");
|
|
1173
1278
|
return re === null ? text : text.replace(re, "~");
|
|
@@ -1540,7 +1645,7 @@ function wideNamedImports(content, max) {
|
|
|
1540
1645
|
}
|
|
1541
1646
|
function readPkgDeps(root) {
|
|
1542
1647
|
try {
|
|
1543
|
-
const pkg = JSON.parse(
|
|
1648
|
+
const pkg = JSON.parse(readFileSync5(join6(root, "package.json"), "utf8"));
|
|
1544
1649
|
return { ...pkg.dependencies, ...pkg.devDependencies, ...pkg.optionalDependencies, ...pkg.peerDependencies };
|
|
1545
1650
|
} catch {
|
|
1546
1651
|
return null;
|
|
@@ -1561,7 +1666,7 @@ function globRe(glob, ignoreCase = false) {
|
|
|
1561
1666
|
return re;
|
|
1562
1667
|
}
|
|
1563
1668
|
function guardrailsConfigPath() {
|
|
1564
|
-
return process.env.ENIGMA_GUARDRAILS_CONFIG ||
|
|
1669
|
+
return process.env.ENIGMA_GUARDRAILS_CONFIG || join6(homedir3(), ".enigma-guardrails.json");
|
|
1565
1670
|
}
|
|
1566
1671
|
function isValidRule(r) {
|
|
1567
1672
|
const x = r;
|
|
@@ -1575,7 +1680,7 @@ function loadRules() {
|
|
|
1575
1680
|
let disabled = [];
|
|
1576
1681
|
let custom = [];
|
|
1577
1682
|
try {
|
|
1578
|
-
const raw = JSON.parse(
|
|
1683
|
+
const raw = JSON.parse(readFileSync5(guardrailsConfigPath(), "utf8"));
|
|
1579
1684
|
if (Array.isArray(raw.disabled)) disabled = raw.disabled.filter((s) => typeof s === "string");
|
|
1580
1685
|
if (Array.isArray(raw.rules)) custom = raw.rules.filter(isValidRule);
|
|
1581
1686
|
} catch {
|
|
@@ -1586,10 +1691,10 @@ function loadRules() {
|
|
|
1586
1691
|
function findProjectRoot(file) {
|
|
1587
1692
|
let dir = dirname2(resolve2(file));
|
|
1588
1693
|
for (let i = 0; i < 40; i++) {
|
|
1589
|
-
const isProj = existsSync4(
|
|
1694
|
+
const isProj = existsSync4(join6(dir, "package.json")) || existsSync4(join6(dir, ".enigma.json"));
|
|
1590
1695
|
let hasGit = false;
|
|
1591
1696
|
try {
|
|
1592
|
-
hasGit = statSync2(
|
|
1697
|
+
hasGit = statSync2(join6(dir, ".git")).isDirectory();
|
|
1593
1698
|
} catch {
|
|
1594
1699
|
}
|
|
1595
1700
|
if (isProj || hasGit) return dir;
|
|
@@ -1655,7 +1760,7 @@ function formatFindings(findings) {
|
|
|
1655
1760
|
function checkPath(file, stage2 = "edit") {
|
|
1656
1761
|
let content;
|
|
1657
1762
|
try {
|
|
1658
|
-
content =
|
|
1763
|
+
content = readFileSync5(file, "utf8");
|
|
1659
1764
|
} catch {
|
|
1660
1765
|
return [];
|
|
1661
1766
|
}
|
|
@@ -1693,7 +1798,7 @@ function repairableDiffFindings(file) {
|
|
|
1693
1798
|
if (!changed) return [];
|
|
1694
1799
|
let content;
|
|
1695
1800
|
try {
|
|
1696
|
-
content =
|
|
1801
|
+
content = readFileSync5(file, "utf8");
|
|
1697
1802
|
} catch {
|
|
1698
1803
|
return [];
|
|
1699
1804
|
}
|
|
@@ -1707,7 +1812,7 @@ function repairableDiffFindings(file) {
|
|
|
1707
1812
|
function runGuardrailsHook(payload) {
|
|
1708
1813
|
let file;
|
|
1709
1814
|
try {
|
|
1710
|
-
file = JSON.parse(payload ??
|
|
1815
|
+
file = JSON.parse(payload ?? readFileSync5(0, "utf8"))?.tool_input?.file_path;
|
|
1711
1816
|
} catch {
|
|
1712
1817
|
}
|
|
1713
1818
|
if (!file || typeof file !== "string") return 0;
|
|
@@ -2031,16 +2136,16 @@ function pathJoinedFromInput(content, file) {
|
|
|
2031
2136
|
if (!python && /^\s*["']use client["']/m.test(content)) return [];
|
|
2032
2137
|
if (PATH_CONTAINED.test(content)) return [];
|
|
2033
2138
|
const input = python ? REQUEST_INPUT_PY : REQUEST_INPUT_JS;
|
|
2034
|
-
const
|
|
2139
|
+
const join11 = python ? PATH_JOIN_PY : PATH_JOIN_JS;
|
|
2035
2140
|
const lines = content.split("\n");
|
|
2036
2141
|
const names = namesFromInput(lines, input);
|
|
2037
2142
|
const out = [];
|
|
2038
2143
|
for (let i = 0; i < lines.length; i++) {
|
|
2039
2144
|
const text = lines[i];
|
|
2040
2145
|
if (auditSkipLine(text)) continue;
|
|
2041
|
-
|
|
2146
|
+
join11.lastIndex = 0;
|
|
2042
2147
|
let m;
|
|
2043
|
-
while (m =
|
|
2148
|
+
while (m = join11.exec(text)) {
|
|
2044
2149
|
const parts = (m[1] ?? m[2] ?? "").split(",").slice(1).map((s) => s.trim());
|
|
2045
2150
|
if (parts.some((p) => input.test(p) || names.has(p) || names.has(p.replace(/^`\$\{(\w+)\}.*`$/, "$1")))) {
|
|
2046
2151
|
out.push({ line: i + 1, detail: text.trim().slice(0, 120) });
|
|
@@ -2359,7 +2464,7 @@ function sparklesIcon(content) {
|
|
|
2359
2464
|
return out;
|
|
2360
2465
|
}
|
|
2361
2466
|
function ledgerPath() {
|
|
2362
|
-
return process.env.ENIGMA_GUARDRAILS_LOG ||
|
|
2467
|
+
return process.env.ENIGMA_GUARDRAILS_LOG || join6(homedir3(), ".enigma", "guardrail-log.jsonl");
|
|
2363
2468
|
}
|
|
2364
2469
|
function ledgerKey(rule, outcome, file, line) {
|
|
2365
2470
|
return JSON.stringify([rule, outcome, file, line ?? null]);
|
|
@@ -2395,7 +2500,7 @@ function recordFindings(findings, outcome, stage2 = "edit") {
|
|
|
2395
2500
|
} catch {
|
|
2396
2501
|
}
|
|
2397
2502
|
if (size > LEDGER_MAX_BYTES) {
|
|
2398
|
-
const kept = rotateLedger(
|
|
2503
|
+
const kept = rotateLedger(readFileSync5(path, "utf8"));
|
|
2399
2504
|
writeFileSync3(path, kept.length ? `${kept.join("\n")}
|
|
2400
2505
|
` : "");
|
|
2401
2506
|
}
|
|
@@ -2430,7 +2535,7 @@ function rotateLedger(text) {
|
|
|
2430
2535
|
function eachLedgerEntry(sinceDays, visit) {
|
|
2431
2536
|
let text;
|
|
2432
2537
|
try {
|
|
2433
|
-
text =
|
|
2538
|
+
text = readFileSync5(ledgerPath(), "utf8");
|
|
2434
2539
|
} catch {
|
|
2435
2540
|
return;
|
|
2436
2541
|
}
|
|
@@ -2500,7 +2605,7 @@ function runGuardrailsScan(all) {
|
|
|
2500
2605
|
for (const file of files) {
|
|
2501
2606
|
let content;
|
|
2502
2607
|
try {
|
|
2503
|
-
content =
|
|
2608
|
+
content = readFileSync5(file, "utf8");
|
|
2504
2609
|
} catch {
|
|
2505
2610
|
continue;
|
|
2506
2611
|
}
|
|
@@ -2529,10 +2634,11 @@ enigma-guardrails: BLOCKED - ${r.blocks.length} convention violation(s):`);
|
|
|
2529
2634
|
console.log(`enigma-guardrails: ${r.count} ${all ? "tracked" : "staged"} file(s) checked, no blocking violations.`);
|
|
2530
2635
|
return 0;
|
|
2531
2636
|
}
|
|
2532
|
-
var COMMENT_LINE, SKELETON_GUARD_SRC, SKELETON_SIGNAL_SRC, INJECTION_EXCLUDES, WORKFLOW_FILES, UI_CODE_FILES, UI_STYLE_FILES, TELL_EXCLUDES, DB_SCRIPT_EXCLUDES, BUILTIN_RULES, PROJECT_CHECKS, CURRENT_PASSWORD, NEW_PASSWORD, NEW_PASSWORD_PROP, NOT_A_JSX_PROP, FILE_CHECKS, FIXERS, SPAWNERS, MUTATING_REQUEST, ENTITY_WRITE, OPTIMISTIC_SIGNAL, ALLOW_SERVER_FIRST, RESULT_BINDING, BLOCK_LOOKBACK, BLOCK_LOOKAHEAD, REVALIDATE_AFTER, PROMISE_FANOUT, FANOUT_LITERAL, FANOUT_MAPPED, FANOUT_OVER_OBJECT, EXPENSIVE_PER_ITEM, CONCURRENCY_BOUND, FANOUT_WINDOW, TEXTAREA, TEXTAREA_LOWER, TEXTAREA_UPPER, TEXTAREA_FIXED, TEXTAREA_AUTOSIZE, VIEW_GUARD, LOADING_NAME, LOADING_VALUE, VIEW_GUARD_LOADING, VIEW_CHROME_HEADING, VIEW_CHROME_TEXT, VIEW_BODY_LOOKAHEAD, ALLOW_VIEW_SKELETON, SKELETON_GUARD, SKELETON_SIGNAL, REMOTE_COLLECTION_BINDINGS, LIST_BOUNDED, ALLOW_UNBOUNDED_LIST, MENU_HANDLER, MENU_POINT, MENU_STATE, MENU_MITIGATED, SELECT_RANGE_KEY, SELECT_ADD_KEY, SELECT_STATE, SELECT_MITIGATED, COLOR_MATHS, COLOR_DRAG, COLOR_MEASURE, COLOR_STATE, COLOR_MITIGATED, LIGHTBOX_IMAGE, LIGHTBOX_WHEEL, LIGHTBOX_ZOOM, LIGHTBOX_DIALOG, LIGHTBOX_MITIGATED, VIDEO_ELEMENT, VIDEO_DRIVEN, VIDEO_TIMELINE, VIDEO_CONTROLS, VIDEO_MITIGATED, TWOFA_SUBJECT, TWOFA_ENROLL, TWOFA_PASSWORD, TWOFA_MITIGATED, TWOFA_WINDOW, LISTBOX_ROLE, LISTBOX_OPEN, LISTBOX_OPTIONS, LISTBOX_MITIGATED, NATIVE_SELECT, NATIVE_SELECT_MAPPED, NATIVE_SELECT_TRIGGER, NATIVE_SELECT_MITIGATED, TOAST_QUEUE, TOAST_TIMER, TOAST_MITIGATED, PALETTE_SHORTCUT, PALETTE_MODIFIER, PALETTE_OPEN, PALETTE_FILTER, PALETTE_MITIGATED, CLIP_ONE_LINE, DYNAMIC_CHILD, VALUE_REACHABLE, WRAPPER_LOOKBACK, ALLOW_CLIPPED_VALUE, CLIPPED_SIMPLE_VALUE, NOT_TEXT_BINDING, ASYNC_ROUTE, SERVER_DATA_AWAIT, STREAM_BOUNDARY, ALLOW_BLOCKING_PAGE, LOADING_BOUNDARY_FILE, SPECIFIER, MODULE_EXT, JS_EXT, tsconfigCache, NAMED_IMPORT, GENERIC_ACCOUNTS, MINIFIED_LINE, CALL_LEAD_JS, CALL_LEAD_PY, CONSTANT_EXPR, SQL_DML, SQL_VALUE_POSITION, SQL_PLACEHOLDER_BUILDER, SQL_SAFE_EXPR, SQL_CALL_JS, SQL_CALL_PY, SHELL_SAFE_EXPR, INSECURE_TLS_JS, INSECURE_TLS_PY, PY_HTTP_CLIENT, LOOPBACK_HINT, TLS_OPERATOR_TOGGLE, CORS_ANY_ORIGIN_JS, CORS_CREDENTIALS_JS, CORS_ANY_ORIGIN_PY, CORS_CREDENTIALS_PY, CORS_WINDOW, PYYAML_IMPORT, PICKLE_FROM_OUTSIDE, RAW_HTML_MARKDOWN, OPT_IN_HTML_MARKDOWN, MARKDOWN_HTML_ON, HTML_SANITIZER, HTML_SINK, RADIX_OVERLAY_MODULE, RADIX_UMBRELLA_PARTS, INTERNAL_MODULE, globCache, DIFF_HUNK, REQUEST_INPUT_JS, REQUEST_INPUT_PY, SSH_HOST_KEY_OFF, ACTION_USES, FULL_SHA, IMAGE_DIGEST, PRIVILEGED_TRIGGER, PR_HEAD_REF, CHECKOUT_SITE, SAME_REPO_GUARD, UNTRUSTED_EVENT_TEXT, SCRIPT_KEY, BLOCK_SCALAR, OPERAND_BEFORE, OPERAND_AFTER, LITERAL_OPERAND, CONFIGURED_SECRET, PRESENTED_CREDENTIAL, COMPUTED_MAC, SECRET_NAMED, NOT_SECRET_NAMED, REMEMBERED_COPY, SLASH_PREFIX_CHECK, DESTINATION_NAME, GENERIC_PARAM_NAME, NAVIGATION_SINK, DESTINATION_HELPER, SIGN_IN_FILE, RESOLVED_ORIGIN_CHECK, COMPLETE_PREFIX_CHECK, REDIRECT_SINK_JS, REDIRECT_SINK_PY, REDIRECT_VALIDATED, OUTBOUND_JS, OUTBOUND_PY, SSRF_GUARDED, DNS_RESOLUTION, PRIVATE_ADDRESS_CHECK, OUTBOUND_CALL, CONNECTION_PINNED, RESPONSE_TYPE_CALL_JS, RESPONSE_TYPE_KEY_JS, RESPONSE_OPENER_JS, RESPONSE_TYPE_KWARG_PY, RESPONSE_OPENER_PY, STORED_FILE_TYPE, RESPONSE_SANDBOXED, PATH_JOIN_JS, PATH_JOIN_PY, PATH_CONTAINED, TW_HUE, TW_STOP, HEX_COLOR, ALLOW_BRAND_GRADIENT, ALLOW_GRADIENT_TEXT, SHIMMER, TW_LEFT_STRIPE, TW_ROUNDED_LEFT, ALLOW_ACCENT_STRIPE, EMOJI, EMOJI_LEAD, EMOJI_ICON, EMOJI_COMMIT_SAMPLE, TERMINAL_MARKUP, ALLOW_EMOJI_ICON, COPY_PROP, POWERED_BY_BUZZWORD, ALLOW_POWERED_BY, CHAT_RESIDUE, CHAT_LINK, ALLOW_CHAT_RESIDUE, AI_VOCABULARY, SEAMLESS_LITERAL, ALLOW_AI_VOCABULARY, REQUEST_SERVING, READ_BOUND, ALLOW_UNBOUNDED_READ, ALLOW_SELECT_STAR, ORM_READ_CALL, ROW_LOOP, ALLOW_QUERY_IN_LOOP, FIXED_CANVAS_FILE, TW_DECORATION, EMAIL_MARKUP, TW_WIDTH_GUARD, ALLOW_FIXED_WIDTH, TW_VIEWPORT_HEIGHT, DYNAMIC_VIEWPORT, ALLOW_VIEWPORT_HEIGHT, SPARKLES_IMPORT, SPARKLES_NAME, ALLOW_SPARKLES, LEDGER_MAX_BYTES, LEDGER_KEEP, LEDGER_KEEP_REPLY, grEntry, isGrEntry;
|
|
2637
|
+
var COMMENT_LINE, SKELETON_GUARD_SRC, SKELETON_SIGNAL_SRC, INJECTION_EXCLUDES, WORKFLOW_FILES, UI_CODE_FILES, UI_STYLE_FILES, TELL_EXCLUDES, DB_SCRIPT_EXCLUDES, BUILTIN_RULES, PROJECT_CHECKS, CURRENT_PASSWORD, NEW_PASSWORD, NEW_PASSWORD_PROP, NOT_A_JSX_PROP, FILE_CHECKS, FIXERS, SPAWNERS, MUTATING_REQUEST, ENTITY_WRITE, OPTIMISTIC_SIGNAL, ALLOW_SERVER_FIRST, RESULT_BINDING, BLOCK_LOOKBACK, BLOCK_LOOKAHEAD, REVALIDATE_AFTER, PROMISE_FANOUT, FANOUT_LITERAL, FANOUT_MAPPED, FANOUT_OVER_OBJECT, EXPENSIVE_PER_ITEM, CONCURRENCY_BOUND, FANOUT_WINDOW, TEXTAREA, TEXTAREA_LOWER, TEXTAREA_UPPER, TEXTAREA_FIXED, TEXTAREA_AUTOSIZE, VIEW_GUARD, LOADING_NAME, LOADING_VALUE, VIEW_GUARD_LOADING, VIEW_CHROME_HEADING, VIEW_CHROME_TEXT, VIEW_BODY_LOOKAHEAD, ALLOW_VIEW_SKELETON, SKELETON_GUARD, SKELETON_SIGNAL, REMOTE_COLLECTION_BINDINGS, LIST_BOUNDED, ALLOW_UNBOUNDED_LIST, MENU_HANDLER, MENU_POINT, MENU_STATE, MENU_MITIGATED, SELECT_RANGE_KEY, SELECT_ADD_KEY, SELECT_STATE, SELECT_MITIGATED, COLOR_MATHS, COLOR_DRAG, COLOR_MEASURE, COLOR_STATE, COLOR_MITIGATED, LIGHTBOX_IMAGE, LIGHTBOX_WHEEL, LIGHTBOX_ZOOM, LIGHTBOX_DIALOG, LIGHTBOX_MITIGATED, VIDEO_ELEMENT, VIDEO_DRIVEN, VIDEO_TIMELINE, VIDEO_CONTROLS, VIDEO_MITIGATED, TWOFA_SUBJECT, TWOFA_ENROLL, TWOFA_PASSWORD, TWOFA_MITIGATED, TWOFA_WINDOW, LISTBOX_ROLE, LISTBOX_OPEN, LISTBOX_OPTIONS, LISTBOX_MITIGATED, NATIVE_SELECT, NATIVE_SELECT_MAPPED, NATIVE_SELECT_TRIGGER, NATIVE_SELECT_MITIGATED, RAW_IMG, ALLOW_RAW_IMG, DECORATIVE_IMG, IMAGE_COMPONENT_FILE, DEFINES_IMAGE, LOCAL_IMAGE_COMPONENTS, imageComponentCache, TOAST_QUEUE, TOAST_TIMER, TOAST_MITIGATED, PALETTE_SHORTCUT, PALETTE_MODIFIER, PALETTE_OPEN, PALETTE_FILTER, PALETTE_MITIGATED, CLIP_ONE_LINE, DYNAMIC_CHILD, VALUE_REACHABLE, WRAPPER_LOOKBACK, ALLOW_CLIPPED_VALUE, CLIPPED_SIMPLE_VALUE, NOT_TEXT_BINDING, ASYNC_ROUTE, SERVER_DATA_AWAIT, STREAM_BOUNDARY, ALLOW_BLOCKING_PAGE, LOADING_BOUNDARY_FILE, SPECIFIER, MODULE_EXT, JS_EXT, tsconfigCache, NAMED_IMPORT, GENERIC_ACCOUNTS, privateTermCache, MINIFIED_LINE, CALL_LEAD_JS, CALL_LEAD_PY, CONSTANT_EXPR, SQL_DML, SQL_VALUE_POSITION, SQL_PLACEHOLDER_BUILDER, SQL_SAFE_EXPR, SQL_CALL_JS, SQL_CALL_PY, SHELL_SAFE_EXPR, INSECURE_TLS_JS, INSECURE_TLS_PY, PY_HTTP_CLIENT, LOOPBACK_HINT, TLS_OPERATOR_TOGGLE, CORS_ANY_ORIGIN_JS, CORS_CREDENTIALS_JS, CORS_ANY_ORIGIN_PY, CORS_CREDENTIALS_PY, CORS_WINDOW, PYYAML_IMPORT, PICKLE_FROM_OUTSIDE, RAW_HTML_MARKDOWN, OPT_IN_HTML_MARKDOWN, MARKDOWN_HTML_ON, HTML_SANITIZER, HTML_SINK, RADIX_OVERLAY_MODULE, RADIX_UMBRELLA_PARTS, INTERNAL_MODULE, globCache, DIFF_HUNK, REQUEST_INPUT_JS, REQUEST_INPUT_PY, SSH_HOST_KEY_OFF, ACTION_USES, FULL_SHA, IMAGE_DIGEST, PRIVILEGED_TRIGGER, PR_HEAD_REF, CHECKOUT_SITE, SAME_REPO_GUARD, UNTRUSTED_EVENT_TEXT, SCRIPT_KEY, BLOCK_SCALAR, OPERAND_BEFORE, OPERAND_AFTER, LITERAL_OPERAND, CONFIGURED_SECRET, PRESENTED_CREDENTIAL, COMPUTED_MAC, SECRET_NAMED, NOT_SECRET_NAMED, REMEMBERED_COPY, SLASH_PREFIX_CHECK, DESTINATION_NAME, GENERIC_PARAM_NAME, NAVIGATION_SINK, DESTINATION_HELPER, SIGN_IN_FILE, RESOLVED_ORIGIN_CHECK, COMPLETE_PREFIX_CHECK, REDIRECT_SINK_JS, REDIRECT_SINK_PY, REDIRECT_VALIDATED, OUTBOUND_JS, OUTBOUND_PY, SSRF_GUARDED, DNS_RESOLUTION, PRIVATE_ADDRESS_CHECK, OUTBOUND_CALL, CONNECTION_PINNED, RESPONSE_TYPE_CALL_JS, RESPONSE_TYPE_KEY_JS, RESPONSE_OPENER_JS, RESPONSE_TYPE_KWARG_PY, RESPONSE_OPENER_PY, STORED_FILE_TYPE, RESPONSE_SANDBOXED, PATH_JOIN_JS, PATH_JOIN_PY, PATH_CONTAINED, TW_HUE, TW_STOP, HEX_COLOR, ALLOW_BRAND_GRADIENT, ALLOW_GRADIENT_TEXT, SHIMMER, TW_LEFT_STRIPE, TW_ROUNDED_LEFT, ALLOW_ACCENT_STRIPE, EMOJI, EMOJI_LEAD, EMOJI_ICON, EMOJI_COMMIT_SAMPLE, TERMINAL_MARKUP, ALLOW_EMOJI_ICON, COPY_PROP, POWERED_BY_BUZZWORD, ALLOW_POWERED_BY, CHAT_RESIDUE, CHAT_LINK, ALLOW_CHAT_RESIDUE, AI_VOCABULARY, SEAMLESS_LITERAL, ALLOW_AI_VOCABULARY, REQUEST_SERVING, READ_BOUND, ALLOW_UNBOUNDED_READ, ALLOW_SELECT_STAR, ORM_READ_CALL, ROW_LOOP, ALLOW_QUERY_IN_LOOP, FIXED_CANVAS_FILE, TW_DECORATION, EMAIL_MARKUP, TW_WIDTH_GUARD, ALLOW_FIXED_WIDTH, TW_VIEWPORT_HEIGHT, DYNAMIC_VIEWPORT, ALLOW_VIEWPORT_HEIGHT, SPARKLES_IMPORT, SPARKLES_NAME, ALLOW_SPARKLES, LEDGER_MAX_BYTES, LEDGER_KEEP, LEDGER_KEEP_REPLY, grEntry, isGrEntry;
|
|
2533
2638
|
var init_guardrails = __esm({
|
|
2534
2639
|
"src/guardrails.ts"() {
|
|
2535
2640
|
"use strict";
|
|
2641
|
+
init_private_terms();
|
|
2536
2642
|
COMMENT_LINE = /^\s*(\/\/|#|\*|--|<!--|\{?\/\*)/;
|
|
2537
2643
|
SKELETON_GUARD_SRC = "\\bif\\s*\\(\\s*(isLoading|isPending|isFetching|loading|pending)\\s*\\)\\s*return\\s+(null\\b|<\\s*\\w*(Spinner|Loader|Loading|CircularProgress)\\b)";
|
|
2538
2644
|
SKELETON_SIGNAL_SRC = "skeleton|animate-pulse|shimmer|Suspense|ContentLoader|content-loader|<\\s*Placeholder";
|
|
@@ -3937,6 +4043,35 @@ var init_guardrails = __esm({
|
|
|
3937
4043
|
severity: "block",
|
|
3938
4044
|
skill: "frontend-policy"
|
|
3939
4045
|
},
|
|
4046
|
+
{
|
|
4047
|
+
id: "fe-raw-img-over-component",
|
|
4048
|
+
label: "A project with an image component uses it",
|
|
4049
|
+
files: ["*.tsx", "*.jsx", "*.vue", "*.svelte", "*.astro"],
|
|
4050
|
+
excludeFiles: [
|
|
4051
|
+
"*.test.*",
|
|
4052
|
+
"*.spec.*",
|
|
4053
|
+
"**/tests/**",
|
|
4054
|
+
"**/__tests__/**",
|
|
4055
|
+
"**/fixtures/**",
|
|
4056
|
+
"*.stories.*",
|
|
4057
|
+
"**/dist/**",
|
|
4058
|
+
"**/build/**",
|
|
4059
|
+
"**/node_modules/**",
|
|
4060
|
+
"**/vendor/**",
|
|
4061
|
+
"**/emails/**",
|
|
4062
|
+
"dist/**",
|
|
4063
|
+
"build/**",
|
|
4064
|
+
"node_modules/**",
|
|
4065
|
+
"vendor/**",
|
|
4066
|
+
"emails/**"
|
|
4067
|
+
],
|
|
4068
|
+
scope: "file",
|
|
4069
|
+
stage: "diff",
|
|
4070
|
+
fileCheck: "fe-raw-img-over-component",
|
|
4071
|
+
message: "A raw <img> in a project that already has an image component. Every content image goes through ONE component, so loading, sizing, alt text, lazy decoding and the lightbox stay the same everywhere and a fix lands once; an <img> per page is a second, drifting copy of it. Use the component named here. Mark the line `enigma:allow-raw-img` where the element must stay raw - an email template, a tracking pixel, or markup the component cannot render (frontend-policy).",
|
|
4072
|
+
severity: "block",
|
|
4073
|
+
skill: "frontend-policy"
|
|
4074
|
+
},
|
|
3940
4075
|
{
|
|
3941
4076
|
id: "fe-toast-hand-rolled",
|
|
3942
4077
|
label: "Toasts come from the primitive",
|
|
@@ -4747,6 +4882,35 @@ var init_guardrails = __esm({
|
|
|
4747
4882
|
severity: "block",
|
|
4748
4883
|
skill: "security-policy"
|
|
4749
4884
|
},
|
|
4885
|
+
{
|
|
4886
|
+
id: "sec-private-term",
|
|
4887
|
+
label: "Real people's data stays out of the repository",
|
|
4888
|
+
files: ["**"],
|
|
4889
|
+
excludeFiles: [
|
|
4890
|
+
"**/node_modules/**",
|
|
4891
|
+
"**/dist/**",
|
|
4892
|
+
"**/build/**",
|
|
4893
|
+
"**/vendor/**",
|
|
4894
|
+
"**/.next/**",
|
|
4895
|
+
"node_modules/**",
|
|
4896
|
+
"dist/**",
|
|
4897
|
+
"build/**",
|
|
4898
|
+
"vendor/**",
|
|
4899
|
+
"*.lock",
|
|
4900
|
+
"*.lockb",
|
|
4901
|
+
"package-lock.json",
|
|
4902
|
+
"*.min.js",
|
|
4903
|
+
"*.map"
|
|
4904
|
+
],
|
|
4905
|
+
scope: "file",
|
|
4906
|
+
// DIFF stage like sec-operator-env-leak: it fires on the line being added, the moment
|
|
4907
|
+
// before the value reaches the history for good, and never on an old file it did not write.
|
|
4908
|
+
stage: "diff",
|
|
4909
|
+
fileCheck: "sec-private-term",
|
|
4910
|
+
message: "This line carries one of your private terms - the real name, handle, server or community name of you or of the people you serve (the list in ~/.enigma-guard.json, `enigma config private-terms`). Real data never goes into a tracked file, and tests, fixtures, examples and docs use ANONYMOUS data: invent the values (`Player One`, `alice@example.test`, `Test Server`) and, when a real log or payload shape matters, reproduce the shape with invented values instead of pasting the capture. Being shown a real value to work with is permission to use it, never to publish it (security-policy, testing-policy).",
|
|
4911
|
+
severity: "block",
|
|
4912
|
+
skill: "security-policy"
|
|
4913
|
+
},
|
|
4750
4914
|
// The injection class. Every one but sec-unsafe-deserialization is DIFF stage for the reason the
|
|
4751
4915
|
// stage exists: each has a measured backlog in real repositories (the counts are in guardrails.md),
|
|
4752
4916
|
// so at the edit stage it would block unrelated edits to a file that already carried the shape.
|
|
@@ -5216,7 +5380,7 @@ var init_guardrails = __esm({
|
|
|
5216
5380
|
"ts-relational-no-prisma": (root) => {
|
|
5217
5381
|
const pkg = readPkgDeps(root);
|
|
5218
5382
|
if (!pkg) return false;
|
|
5219
|
-
const hasTs = "typescript" in pkg || existsSync4(
|
|
5383
|
+
const hasTs = "typescript" in pkg || existsSync4(join6(root, "tsconfig.json"));
|
|
5220
5384
|
if (!hasTs) return false;
|
|
5221
5385
|
const relational = ["typeorm", "sequelize", "knex", "drizzle-orm", "pg", "mysql", "mysql2", "better-sqlite3", "@mikro-orm/core"];
|
|
5222
5386
|
if (!relational.some((d) => d in pkg)) return false;
|
|
@@ -5241,6 +5405,7 @@ var init_guardrails = __esm({
|
|
|
5241
5405
|
"ts-alias-deep-relative": (content, file) => deepRelativeImports(content, file),
|
|
5242
5406
|
"ts-alias-paths": (content, file) => missingPathAlias(content, file),
|
|
5243
5407
|
"sec-operator-env-leak": (content) => operatorHomePathLeak(content),
|
|
5408
|
+
"sec-private-term": (content) => privateTermLeak(content),
|
|
5244
5409
|
"fe-new-password-affordance-on-signin": (content) => newPasswordAffordanceOnSignIn(content),
|
|
5245
5410
|
"fe-context-menu-hand-rolled": (content) => handRolledContextMenu(content),
|
|
5246
5411
|
"fe-selection-hand-rolled": (content) => handRolledSelection(content),
|
|
@@ -5250,6 +5415,7 @@ var init_guardrails = __esm({
|
|
|
5250
5415
|
"sec-2fa-reauth-prompt": (content) => twoFactorPasswordPrompt(content),
|
|
5251
5416
|
"fe-select-hand-rolled": (content) => handRolledSelect(content),
|
|
5252
5417
|
"fe-native-select-over-primitive": (content) => nativeSelectOverPrimitive(content),
|
|
5418
|
+
"fe-raw-img-over-component": (content, file) => rawImgOverComponent(content, file),
|
|
5253
5419
|
"fe-toast-hand-rolled": (content) => handRolledToast(content),
|
|
5254
5420
|
"fe-palette-hand-rolled": (content) => handRolledPalette(content),
|
|
5255
5421
|
"sec-sql-built-from-values": (content, file) => sqlBuiltFromValues(content, file),
|
|
@@ -5387,6 +5553,13 @@ var init_guardrails = __esm({
|
|
|
5387
5553
|
NATIVE_SELECT_MAPPED = /<option\b|\.map\(/;
|
|
5388
5554
|
NATIVE_SELECT_TRIGGER = /@enigmax\/primitives/;
|
|
5389
5555
|
NATIVE_SELECT_MITIGATED = /enigma:allow-native-select|SelectRoot|useSelectContext/;
|
|
5556
|
+
RAW_IMG = /<img\b/;
|
|
5557
|
+
ALLOW_RAW_IMG = /enigma:allow-raw-img/;
|
|
5558
|
+
DECORATIVE_IMG = /alt=(?:""|''|\{["']{2}\})|\.svg\b|\b(?:logo|icon|flag|favicon|sprite)/i;
|
|
5559
|
+
IMAGE_COMPONENT_FILE = /(?:^|\/)(?:image|img|picture)\.(?:tsx|jsx|vue|svelte|astro)$/i;
|
|
5560
|
+
DEFINES_IMAGE = /\b(?:function|const|class)\s+(?:Image|Img|Picture)\b/;
|
|
5561
|
+
LOCAL_IMAGE_COMPONENTS = ["src/components", "components", "src/components/ui", "components/ui", "src/lib/components", "app/components"];
|
|
5562
|
+
imageComponentCache = /* @__PURE__ */ new Map();
|
|
5390
5563
|
TOAST_QUEUE = /\bset(?:Toasts?|Snackbars?)\b|\btoasts\.(?:map|filter|slice)\b|\btoastQueue\b/;
|
|
5391
5564
|
TOAST_TIMER = /setTimeout\(/;
|
|
5392
5565
|
TOAST_MITIGATED = /@enigmax\/primitives|createNotifications|useNotifications|<Toaster|ToastT\b|ToasterProps\b|["']sonner["']|react-hot-toast|react-toastify|notistack|@radix-ui|@mui\/material|@chakra-ui|@mantine|antd|enigma:allow-hand-rolled-toast/;
|
|
@@ -5588,10 +5761,10 @@ var init_governor = __esm({
|
|
|
5588
5761
|
|
|
5589
5762
|
// src/repo-keyed-file.ts
|
|
5590
5763
|
import { dirname as dirname3 } from "path";
|
|
5591
|
-
import { mkdirSync as mkdirSync3, readFileSync as
|
|
5764
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync6, renameSync, rmSync, writeFileSync as writeFileSync4 } from "fs";
|
|
5592
5765
|
function readRepoKeyedFile(path, version) {
|
|
5593
5766
|
try {
|
|
5594
|
-
const parsed = JSON.parse(
|
|
5767
|
+
const parsed = JSON.parse(readFileSync6(path, "utf8"));
|
|
5595
5768
|
if (!parsed || parsed.version !== version || typeof parsed.repos !== "object" || parsed.repos === null) return {};
|
|
5596
5769
|
return parsed.repos;
|
|
5597
5770
|
} catch {
|
|
@@ -6031,8 +6204,8 @@ var init_codegraph_extract_docs = __esm({
|
|
|
6031
6204
|
|
|
6032
6205
|
// src/codegraph-extract.ts
|
|
6033
6206
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
6034
|
-
import { extname as extname2, join as
|
|
6035
|
-
import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as
|
|
6207
|
+
import { extname as extname2, join as join7, relative as relative2, resolve as resolve3 } from "path";
|
|
6208
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
|
|
6036
6209
|
function isCodeLang(lang) {
|
|
6037
6210
|
return !DOC_LANGS.has(lang);
|
|
6038
6211
|
}
|
|
@@ -6349,7 +6522,7 @@ function gitList(root, args) {
|
|
|
6349
6522
|
function gitSourceFiles(root) {
|
|
6350
6523
|
const out = gitList(root, ["--cached", "--others", "--exclude-standard"]);
|
|
6351
6524
|
if (out === null) return null;
|
|
6352
|
-
const nested = existsSync5(
|
|
6525
|
+
const nested = existsSync5(join7(root, ".gitmodules")) ? gitList(root, ["--recurse-submodules"]) : null;
|
|
6353
6526
|
const code = [];
|
|
6354
6527
|
const docs = [];
|
|
6355
6528
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -6359,7 +6532,7 @@ function gitSourceFiles(root) {
|
|
|
6359
6532
|
seen.add(rel);
|
|
6360
6533
|
if (!LANG_BY_EXT[extname2(rel).toLowerCase()]) continue;
|
|
6361
6534
|
if (rel.split("/").some((seg) => seg.startsWith(".") || IGNORE_DIRS.has(seg))) continue;
|
|
6362
|
-
(isDocPath(rel) ? docs : code).push(
|
|
6535
|
+
(isDocPath(rel) ? docs : code).push(join7(root, rel));
|
|
6363
6536
|
}
|
|
6364
6537
|
return [...code, ...pickDocs(root, docs, MAX_FILES - code.length)];
|
|
6365
6538
|
}
|
|
@@ -6380,11 +6553,11 @@ function walk(root) {
|
|
|
6380
6553
|
for (const e of entries) {
|
|
6381
6554
|
if (code.length >= MAX_FILES || ++visited >= MAX_ENTRIES) break;
|
|
6382
6555
|
if (e.isDirectory()) {
|
|
6383
|
-
if (!e.name.startsWith(".") && !IGNORE_DIRS.has(e.name)) queue.push(
|
|
6556
|
+
if (!e.name.startsWith(".") && !IGNORE_DIRS.has(e.name)) queue.push(join7(queue[head], e.name));
|
|
6384
6557
|
continue;
|
|
6385
6558
|
}
|
|
6386
6559
|
if (!e.isFile() || !LANG_BY_EXT[extname2(e.name).toLowerCase()]) continue;
|
|
6387
|
-
(isDocPath(e.name) ? docs : code).push(
|
|
6560
|
+
(isDocPath(e.name) ? docs : code).push(join7(queue[head], e.name));
|
|
6388
6561
|
}
|
|
6389
6562
|
}
|
|
6390
6563
|
const ordered = [...code, ...pickDocs(root, docs, MAX_FILES - code.length)];
|
|
@@ -6414,7 +6587,7 @@ function scanFiles(rootDir) {
|
|
|
6414
6587
|
}
|
|
6415
6588
|
let content;
|
|
6416
6589
|
try {
|
|
6417
|
-
content =
|
|
6590
|
+
content = readFileSync7(abs, "utf8");
|
|
6418
6591
|
} catch {
|
|
6419
6592
|
continue;
|
|
6420
6593
|
}
|
|
@@ -6540,25 +6713,25 @@ __export(codegraph_exports, {
|
|
|
6540
6713
|
scanFiles: () => scanFiles,
|
|
6541
6714
|
searchGraph: () => searchGraph
|
|
6542
6715
|
});
|
|
6543
|
-
import { homedir as
|
|
6716
|
+
import { homedir as homedir4 } from "os";
|
|
6544
6717
|
import { createHash as createHash2 } from "crypto";
|
|
6545
|
-
import { basename as basename2, join as
|
|
6546
|
-
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as
|
|
6718
|
+
import { basename as basename2, join as join8, resolve as resolve4 } from "path";
|
|
6719
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync8, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
6547
6720
|
function storeDir() {
|
|
6548
|
-
return process.env.ENIGMA_CODEGRAPH_DIR ||
|
|
6721
|
+
return process.env.ENIGMA_CODEGRAPH_DIR || join8(homedir4(), ".enigma", "codegraph");
|
|
6549
6722
|
}
|
|
6550
6723
|
function projectsFile() {
|
|
6551
|
-
return
|
|
6724
|
+
return join8(storeDir(), "projects.json");
|
|
6552
6725
|
}
|
|
6553
6726
|
function graphFile(id) {
|
|
6554
|
-
return
|
|
6727
|
+
return join8(storeDir(), `${id}.json`);
|
|
6555
6728
|
}
|
|
6556
6729
|
function bodyIndexFile(id) {
|
|
6557
|
-
return
|
|
6730
|
+
return join8(storeDir(), `${id}.bodies.json`);
|
|
6558
6731
|
}
|
|
6559
6732
|
function readJsonSafe(file, fallback) {
|
|
6560
6733
|
try {
|
|
6561
|
-
return existsSync6(file) ? JSON.parse(
|
|
6734
|
+
return existsSync6(file) ? JSON.parse(readFileSync8(file, "utf8")) : fallback;
|
|
6562
6735
|
} catch {
|
|
6563
6736
|
return fallback;
|
|
6564
6737
|
}
|
|
@@ -6587,7 +6760,7 @@ function orders(all, byLang) {
|
|
|
6587
6760
|
}
|
|
6588
6761
|
function goModulePath(root) {
|
|
6589
6762
|
try {
|
|
6590
|
-
return /^\s*module\s+(\S+)/m.exec(
|
|
6763
|
+
return /^\s*module\s+(\S+)/m.exec(readFileSync8(join8(root, "go.mod"), "utf8"))?.[1] ?? null;
|
|
6591
6764
|
} catch {
|
|
6592
6765
|
return null;
|
|
6593
6766
|
}
|
|
@@ -6605,7 +6778,7 @@ function createImportResolver(root, known) {
|
|
|
6605
6778
|
return null;
|
|
6606
6779
|
};
|
|
6607
6780
|
const relative4 = (fromRel, spec, lang) => {
|
|
6608
|
-
const abs = resolve4(
|
|
6781
|
+
const abs = resolve4(join8(root, fromRel), "..", spec);
|
|
6609
6782
|
return verifyModule(relPath(root, abs), known, lang);
|
|
6610
6783
|
};
|
|
6611
6784
|
return (fromRel, lang, spec) => {
|
|
@@ -6782,7 +6955,7 @@ function buildEdges(root, files, importEdges, resolveImport) {
|
|
|
6782
6955
|
for (const f of files) {
|
|
6783
6956
|
let content = "";
|
|
6784
6957
|
try {
|
|
6785
|
-
content =
|
|
6958
|
+
content = readFileSync8(join8(root, f.path), "utf8");
|
|
6786
6959
|
} catch {
|
|
6787
6960
|
continue;
|
|
6788
6961
|
}
|
|
@@ -6849,7 +7022,7 @@ function topTokens(text, limit) {
|
|
|
6849
7022
|
return [...m.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, limit);
|
|
6850
7023
|
}
|
|
6851
7024
|
function isManagedPath(dir) {
|
|
6852
|
-
const managed = resolve4(
|
|
7025
|
+
const managed = resolve4(join8(enigmaHome(), ".enigma"));
|
|
6853
7026
|
return dir === managed || dir.startsWith(managed + (dir.includes("\\") ? "\\" : "/"));
|
|
6854
7027
|
}
|
|
6855
7028
|
function indexProject(rootDir) {
|
|
@@ -6918,14 +7091,14 @@ function covering(all, dir) {
|
|
|
6918
7091
|
}
|
|
6919
7092
|
function projectRootOf(dir) {
|
|
6920
7093
|
const start = resolve4(dir);
|
|
6921
|
-
if (ROOT_MARKERS.some((marker) => existsSync6(
|
|
6922
|
-
const home = resolve4(
|
|
7094
|
+
if (ROOT_MARKERS.some((marker) => existsSync6(join8(start, marker)))) return start;
|
|
7095
|
+
const home = resolve4(homedir4());
|
|
6923
7096
|
let current = start;
|
|
6924
7097
|
for (let up = 0; up < MAX_ROOT_WALK; up++) {
|
|
6925
7098
|
const parent = resolve4(current, "..");
|
|
6926
7099
|
if (parent === current || parent === home) break;
|
|
6927
7100
|
current = parent;
|
|
6928
|
-
if (existsSync6(
|
|
7101
|
+
if (existsSync6(join8(current, ".git"))) return current;
|
|
6929
7102
|
}
|
|
6930
7103
|
return start;
|
|
6931
7104
|
}
|
|
@@ -7127,8 +7300,8 @@ __export(codegraph_query_exports, {
|
|
|
7127
7300
|
isCleanDrift: () => isCleanDrift,
|
|
7128
7301
|
resolveSymbolNodes: () => resolveSymbolNodes
|
|
7129
7302
|
});
|
|
7130
|
-
import { join as
|
|
7131
|
-
import { readFileSync as
|
|
7303
|
+
import { join as join9 } from "path";
|
|
7304
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
7132
7305
|
function load(opts) {
|
|
7133
7306
|
const graph = loadFreshGraph(opts.project, opts.refresh ?? true);
|
|
7134
7307
|
if (!graph) return null;
|
|
@@ -7156,7 +7329,7 @@ function savingsFor(nodes, paths) {
|
|
|
7156
7329
|
}
|
|
7157
7330
|
function sliceSpan(root, path, from, to) {
|
|
7158
7331
|
try {
|
|
7159
|
-
const lines =
|
|
7332
|
+
const lines = readFileSync9(join9(root, path), "utf8").split("\n");
|
|
7160
7333
|
const start = Math.max(1, from);
|
|
7161
7334
|
const end = Math.min(lines.length, to);
|
|
7162
7335
|
const slice = lines.slice(start - 1, end);
|
|
@@ -7510,7 +7683,7 @@ function codeGraphGrep(pattern, opts = {}) {
|
|
|
7510
7683
|
for (const file of files) {
|
|
7511
7684
|
let text;
|
|
7512
7685
|
try {
|
|
7513
|
-
text =
|
|
7686
|
+
text = readFileSync9(join9(loaded.graph.root, file.path), "utf8");
|
|
7514
7687
|
} catch {
|
|
7515
7688
|
truncatedFiles++;
|
|
7516
7689
|
continue;
|
|
@@ -8065,10 +8238,10 @@ var codegraph_hook_exports = {};
|
|
|
8065
8238
|
__export(codegraph_hook_exports, {
|
|
8066
8239
|
runCodeGraphHook: () => runCodeGraphHook
|
|
8067
8240
|
});
|
|
8068
|
-
import { homedir as
|
|
8241
|
+
import { homedir as homedir5 } from "os";
|
|
8069
8242
|
import { spawn } from "child_process";
|
|
8070
|
-
import { basename as basename3, join as
|
|
8071
|
-
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as
|
|
8243
|
+
import { basename as basename3, join as join10, relative as relative3, resolve as resolve5 } from "path";
|
|
8244
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync5, readFileSync as readFileSync10, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
8072
8245
|
function parsePayload(raw) {
|
|
8073
8246
|
try {
|
|
8074
8247
|
return JSON.parse(raw);
|
|
@@ -8083,15 +8256,15 @@ function emit(event, additionalContext) {
|
|
|
8083
8256
|
process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: event, additionalContext } }));
|
|
8084
8257
|
}
|
|
8085
8258
|
function stateDir() {
|
|
8086
|
-
return
|
|
8259
|
+
return join10(process.env.ENIGMA_CODEGRAPH_DIR || join10(homedir5(), ".enigma", "codegraph"), "sessions");
|
|
8087
8260
|
}
|
|
8088
8261
|
function stateFile(dir, sessionId) {
|
|
8089
8262
|
const key = `${dir}|${sessionId}`.replace(/[^A-Za-z0-9]/g, "").slice(-40) || "default";
|
|
8090
|
-
return
|
|
8263
|
+
return join10(stateDir(), `${key}.json`);
|
|
8091
8264
|
}
|
|
8092
8265
|
function readState(dir, sessionId) {
|
|
8093
8266
|
try {
|
|
8094
|
-
const parsed = JSON.parse(
|
|
8267
|
+
const parsed = JSON.parse(readFileSync10(stateFile(dir, sessionId), "utf8"));
|
|
8095
8268
|
return { injected: Array.isArray(parsed.injected) ? parsed.injected : [], dirty: parsed.dirty === true };
|
|
8096
8269
|
} catch {
|
|
8097
8270
|
return { injected: [], dirty: false };
|
|
@@ -8113,8 +8286,8 @@ function projectRoot(cg, project, fallback) {
|
|
|
8113
8286
|
}
|
|
8114
8287
|
function writeStatuslineSnapshot(root, symbols, stale) {
|
|
8115
8288
|
try {
|
|
8116
|
-
const dir = process.env.ENIGMA_CODEGRAPH_DIR ||
|
|
8117
|
-
const target =
|
|
8289
|
+
const dir = process.env.ENIGMA_CODEGRAPH_DIR || join10(homedir5(), ".enigma", "codegraph");
|
|
8290
|
+
const target = join10(dir, "statusline.json");
|
|
8118
8291
|
const repos = readRepoKeyedFile(target, STATUSLINE_SNAPSHOT_VERSION);
|
|
8119
8292
|
repos[root] = { root, symbols, stale, at: Date.now() };
|
|
8120
8293
|
writeRepoKeyedFile(target, STATUSLINE_SNAPSHOT_VERSION, repos, (s) => s.at);
|
|
@@ -8158,7 +8331,7 @@ function coveringProject(cg, dir) {
|
|
|
8158
8331
|
}
|
|
8159
8332
|
function indexMarkerFile(dir) {
|
|
8160
8333
|
const key = dir.replace(/[^A-Za-z0-9]/g, "").slice(-40) || "default";
|
|
8161
|
-
return
|
|
8334
|
+
return join10(stateDir(), `indexing-${key}`);
|
|
8162
8335
|
}
|
|
8163
8336
|
function claimIndex(dir) {
|
|
8164
8337
|
const file = indexMarkerFile(dir);
|
|
@@ -8325,20 +8498,20 @@ async function runLintStep(payload) {
|
|
|
8325
8498
|
}
|
|
8326
8499
|
const file = input?.file_path;
|
|
8327
8500
|
if (!file || !LINTABLE.test(file)) return 0;
|
|
8328
|
-
const { join:
|
|
8329
|
-
const { homedir:
|
|
8501
|
+
const { join: join11 } = await import("path");
|
|
8502
|
+
const { homedir: homedir6 } = await import("os");
|
|
8330
8503
|
const { createRequire } = await import("module");
|
|
8331
|
-
const { readFileSync:
|
|
8332
|
-
const dir = process.env.ENIGMA_LINT_DIR ||
|
|
8504
|
+
const { readFileSync: readFileSync11, writeFileSync: writeFileSync7 } = await import("fs");
|
|
8505
|
+
const dir = process.env.ENIGMA_LINT_DIR || join11(homedir6(), ".enigma", "linter");
|
|
8333
8506
|
let linter;
|
|
8334
8507
|
try {
|
|
8335
|
-
linter = createRequire(
|
|
8508
|
+
linter = createRequire(join11(dir, "noop.js"))("@enigmax/linter");
|
|
8336
8509
|
} catch {
|
|
8337
8510
|
return 0;
|
|
8338
8511
|
}
|
|
8339
8512
|
let text;
|
|
8340
8513
|
try {
|
|
8341
|
-
text =
|
|
8514
|
+
text = readFileSync11(file, "utf8");
|
|
8342
8515
|
} catch {
|
|
8343
8516
|
return 0;
|
|
8344
8517
|
}
|