@curiousnerd/keel 0.1.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.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +250 -0
  3. package/data/capability-buckets.json +15 -0
  4. package/dist/analyze/docDrift.d.ts +9 -0
  5. package/dist/analyze/docDrift.js +116 -0
  6. package/dist/analyze/docDrift.js.map +1 -0
  7. package/dist/analyze/drift.d.ts +4 -0
  8. package/dist/analyze/drift.js +134 -0
  9. package/dist/analyze/drift.js.map +1 -0
  10. package/dist/analyze/duplication.d.ts +7 -0
  11. package/dist/analyze/duplication.js +46 -0
  12. package/dist/analyze/duplication.js.map +1 -0
  13. package/dist/analyze/index.d.ts +10 -0
  14. package/dist/analyze/index.js +28 -0
  15. package/dist/analyze/index.js.map +1 -0
  16. package/dist/analyze/libConflicts.d.ts +9 -0
  17. package/dist/analyze/libConflicts.js +36 -0
  18. package/dist/analyze/libConflicts.js.map +1 -0
  19. package/dist/analyze/nearDup.d.ts +11 -0
  20. package/dist/analyze/nearDup.js +67 -0
  21. package/dist/analyze/nearDup.js.map +1 -0
  22. package/dist/analyze/score.d.ts +6 -0
  23. package/dist/analyze/score.js +39 -0
  24. package/dist/analyze/score.js.map +1 -0
  25. package/dist/analyze/shared.d.ts +19 -0
  26. package/dist/analyze/shared.js +53 -0
  27. package/dist/analyze/shared.js.map +1 -0
  28. package/dist/cache/hashCache.d.ts +19 -0
  29. package/dist/cache/hashCache.js +49 -0
  30. package/dist/cache/hashCache.js.map +1 -0
  31. package/dist/claims/parseBlock.d.ts +4 -0
  32. package/dist/claims/parseBlock.js +66 -0
  33. package/dist/claims/parseBlock.js.map +1 -0
  34. package/dist/cli.d.ts +2 -0
  35. package/dist/cli.js +136 -0
  36. package/dist/cli.js.map +1 -0
  37. package/dist/config.d.ts +32 -0
  38. package/dist/config.js +37 -0
  39. package/dist/config.js.map +1 -0
  40. package/dist/extract/imports.d.ts +12 -0
  41. package/dist/extract/imports.js +74 -0
  42. package/dist/extract/imports.js.map +1 -0
  43. package/dist/extract/index.d.ts +24 -0
  44. package/dist/extract/index.js +117 -0
  45. package/dist/extract/index.js.map +1 -0
  46. package/dist/extract/language.d.ts +3 -0
  47. package/dist/extract/language.js +13 -0
  48. package/dist/extract/language.js.map +1 -0
  49. package/dist/extract/naming.d.ts +11 -0
  50. package/dist/extract/naming.js +57 -0
  51. package/dist/extract/naming.js.map +1 -0
  52. package/dist/extract/packageJson.d.ts +3 -0
  53. package/dist/extract/packageJson.js +43 -0
  54. package/dist/extract/packageJson.js.map +1 -0
  55. package/dist/extract/python.d.ts +11 -0
  56. package/dist/extract/python.js +244 -0
  57. package/dist/extract/python.js.map +1 -0
  58. package/dist/extract/scan.d.ts +12 -0
  59. package/dist/extract/scan.js +16 -0
  60. package/dist/extract/scan.js.map +1 -0
  61. package/dist/extract/symbols.d.ts +9 -0
  62. package/dist/extract/symbols.js +120 -0
  63. package/dist/extract/symbols.js.map +1 -0
  64. package/dist/extract/walk.d.ts +10 -0
  65. package/dist/extract/walk.js +115 -0
  66. package/dist/extract/walk.js.map +1 -0
  67. package/dist/llm/cache.d.ts +17 -0
  68. package/dist/llm/cache.js +50 -0
  69. package/dist/llm/cache.js.map +1 -0
  70. package/dist/llm/claimsFromDocs.d.ts +16 -0
  71. package/dist/llm/claimsFromDocs.js +95 -0
  72. package/dist/llm/claimsFromDocs.js.map +1 -0
  73. package/dist/llm/explain.d.ts +10 -0
  74. package/dist/llm/explain.js +63 -0
  75. package/dist/llm/explain.js.map +1 -0
  76. package/dist/llm/improve.d.ts +9 -0
  77. package/dist/llm/improve.js +37 -0
  78. package/dist/llm/improve.js.map +1 -0
  79. package/dist/llm/provider.d.ts +24 -0
  80. package/dist/llm/provider.js +210 -0
  81. package/dist/llm/provider.js.map +1 -0
  82. package/dist/mcp/server.d.ts +7 -0
  83. package/dist/mcp/server.js +43 -0
  84. package/dist/mcp/server.js.map +1 -0
  85. package/dist/mcp/tools.d.ts +9 -0
  86. package/dist/mcp/tools.js +173 -0
  87. package/dist/mcp/tools.js.map +1 -0
  88. package/dist/report/json.d.ts +3 -0
  89. package/dist/report/json.js +5 -0
  90. package/dist/report/json.js.map +1 -0
  91. package/dist/report/markdown.d.ts +9 -0
  92. package/dist/report/markdown.js +97 -0
  93. package/dist/report/markdown.js.map +1 -0
  94. package/dist/report/text.d.ts +11 -0
  95. package/dist/report/text.js +76 -0
  96. package/dist/report/text.js.map +1 -0
  97. package/dist/suppress.d.ts +22 -0
  98. package/dist/suppress.js +80 -0
  99. package/dist/suppress.js.map +1 -0
  100. package/dist/types.d.ts +144 -0
  101. package/dist/types.js +9 -0
  102. package/dist/types.js.map +1 -0
  103. package/dist/util/fingerprint.d.ts +12 -0
  104. package/dist/util/fingerprint.js +60 -0
  105. package/dist/util/fingerprint.js.map +1 -0
  106. package/dist/util/hash.d.ts +4 -0
  107. package/dist/util/hash.js +15 -0
  108. package/dist/util/hash.js.map +1 -0
  109. package/package.json +58 -0
@@ -0,0 +1,173 @@
1
+ import { analyze } from "../analyze/index.js";
2
+ import { filesByPackage, loadCapabilityBuckets } from "../analyze/shared.js";
3
+ /** Split an identifier into lowercase word tokens: formatDate -> [format, date]. */
4
+ function nameTokens(name) {
5
+ return name
6
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
7
+ .replace(/[_\-]/g, " ")
8
+ .toLowerCase()
9
+ .split(/\s+/)
10
+ .filter(Boolean);
11
+ }
12
+ const STOPWORDS = new Set([
13
+ "a", "an", "the", "to", "of", "for", "and", "or", "in", "on", "with", "that", "this",
14
+ "write", "writing", "create", "creating", "add", "adding", "new", "function", "method",
15
+ "make", "build", "some", "thing", "code", "want", "need",
16
+ ]);
17
+ function intentTokens(intent) {
18
+ return new Set(intent
19
+ .toLowerCase()
20
+ .split(/[^a-z0-9]+/)
21
+ .filter((t) => t.length >= 2 && !STOPWORDS.has(t)));
22
+ }
23
+ /** Keywords that map a natural-language intent onto a capability bucket. */
24
+ const BUCKET_KEYWORDS = {
25
+ http: ["http", "request", "fetch", "api", "endpoint", "rest", "post", "ajax", "axios"],
26
+ date: ["date", "time", "timestamp", "datetime", "calendar"],
27
+ validation: ["validate", "validation", "schema", "sanitize", "validator"],
28
+ logging: ["log", "logger", "logging"],
29
+ uuid: ["uuid", "guid", "nanoid", "ulid"],
30
+ state: ["state", "store", "reducer"],
31
+ orm: ["query", "database", "orm", "migration", "repository"],
32
+ test: ["test", "spec", "fixture", "mock"],
33
+ };
34
+ function bucketsForIntent(tokens) {
35
+ const out = [];
36
+ for (const [bucket, keywords] of Object.entries(BUCKET_KEYWORDS)) {
37
+ if (keywords.some((k) => tokens.has(k)))
38
+ out.push(bucket);
39
+ }
40
+ return out;
41
+ }
42
+ /** Per-bucket packages actually imported, "pkg (N files)". */
43
+ function librariesInUse(facts, buckets) {
44
+ const byPkg = filesByPackage(facts);
45
+ const out = new Map();
46
+ for (const [bucket, members] of Object.entries(buckets)) {
47
+ const present = members
48
+ .map((pkg) => ({ pkg, n: byPkg.get(pkg)?.length ?? 0 }))
49
+ .filter((e) => e.n > 0)
50
+ .map((e) => `${e.pkg} (${e.n} file${e.n === 1 ? "" : "s"})`);
51
+ if (present.length)
52
+ out.set(bucket, present);
53
+ }
54
+ return out;
55
+ }
56
+ function dominantNaming(facts, pathPrefix) {
57
+ const totals = { lower: 0, camelCase: 0, PascalCase: 0, snake_case: 0, CONSTANT_CASE: 0, other: 0 };
58
+ for (const f of facts.files) {
59
+ if (pathPrefix && !f.path.startsWith(pathPrefix))
60
+ continue;
61
+ for (const k of Object.keys(totals))
62
+ totals[k] += f.naming[k];
63
+ }
64
+ if (totals.camelCase + totals.snake_case < 4)
65
+ return null; // too little signal
66
+ return totals.camelCase >= totals.snake_case ? "camelCase" : "snake_case";
67
+ }
68
+ function dominantLanguage(facts, pathPrefix) {
69
+ const counts = new Map();
70
+ for (const f of facts.files) {
71
+ if (pathPrefix && !f.path.startsWith(pathPrefix))
72
+ continue;
73
+ counts.set(f.language, (counts.get(f.language) ?? 0) + 1);
74
+ }
75
+ let best = null;
76
+ let max = 0;
77
+ for (const [lang, n] of counts)
78
+ if (n > max)
79
+ ((max = n), (best = lang));
80
+ return best;
81
+ }
82
+ // ---------------------------------------------------------------------------
83
+ // Tool 1: check_before_write
84
+ // ---------------------------------------------------------------------------
85
+ /** Suggest existing utilities + libraries to reuse before the agent writes new code. */
86
+ export function checkBeforeWrite(facts, intent, buckets = loadCapabilityBuckets()) {
87
+ const tokens = intentTokens(intent);
88
+ if (tokens.size === 0)
89
+ return `No specific intent recognized in "${intent}". Write it in the project's conventions.`;
90
+ // Existing named functions whose name overlaps the intent.
91
+ const matches = [];
92
+ for (const file of facts.files) {
93
+ for (const fn of file.functions) {
94
+ if (!fn.name)
95
+ continue;
96
+ const overlap = nameTokens(fn.name).filter((t) => tokens.has(t)).length;
97
+ if (overlap > 0)
98
+ matches.push({ name: fn.name, loc: `${fn.filePath}:${fn.startLine}`, score: overlap });
99
+ }
100
+ }
101
+ matches.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name));
102
+ const top = dedupeByName(matches).slice(0, 6);
103
+ // Libraries already in use for any capability the intent implies.
104
+ const inUse = librariesInUse(facts, buckets);
105
+ const relevant = bucketsForIntent(tokens)
106
+ .map((b) => ({ bucket: b, libs: inUse.get(b) }))
107
+ .filter((e) => Array.isArray(e.libs));
108
+ const lines = [`Before writing for "${intent}":`];
109
+ if (top.length) {
110
+ lines.push("", "Existing functions you may be about to reinvent — reuse if they fit:");
111
+ for (const m of top)
112
+ lines.push(` - ${m.name}() @ ${m.loc}`);
113
+ }
114
+ if (relevant.length) {
115
+ lines.push("", "This project already uses these libraries — match them, don't add a competitor:");
116
+ for (const e of relevant)
117
+ lines.push(` - ${e.bucket}: ${e.libs.join(", ")}`);
118
+ }
119
+ const naming = dominantNaming(facts);
120
+ if (top.length === 0 && relevant.length === 0) {
121
+ lines.push("", `Nothing existing matches — safe to write it fresh${naming ? ` (use ${naming})` : ""}.`);
122
+ }
123
+ else if (naming) {
124
+ lines.push("", `If you do write new code, name it in ${naming} to match this project.`);
125
+ }
126
+ return lines.join("\n");
127
+ }
128
+ function dedupeByName(matches) {
129
+ const seen = new Set();
130
+ return matches.filter((m) => (seen.has(m.name) ? false : (seen.add(m.name), true)));
131
+ }
132
+ // ---------------------------------------------------------------------------
133
+ // Tool 2: get_conventions
134
+ // ---------------------------------------------------------------------------
135
+ /** Report the verified, current conventions for an area of the codebase. */
136
+ export function getConventions(facts, path, buckets = loadCapabilityBuckets()) {
137
+ const prefix = path.replace(/^\.?\//, "").replace(/\/[^/]*$/, ""); // directory of the path
138
+ const scoped = facts.files.some((f) => f.path.startsWith(prefix)) ? prefix : undefined;
139
+ const language = dominantLanguage(facts, scoped);
140
+ const naming = dominantNaming(facts, scoped);
141
+ const inUse = librariesInUse(facts, buckets);
142
+ const lines = [`Conventions for ${path || "this repo"} (derived from the actual code):`];
143
+ if (language)
144
+ lines.push(` - Language: ${language}`);
145
+ if (naming)
146
+ lines.push(` - Naming: ${naming}`);
147
+ if (facts.pkg)
148
+ lines.push(` - Package manager: ${facts.pkg.packageManager}`);
149
+ if (inUse.size) {
150
+ const libs = [...inUse.entries()].map(([b, l]) => `${b}=${l.map((s) => s.split(" ")[0]).join("/")}`);
151
+ lines.push(` - Libraries in use: ${libs.join(", ")}`);
152
+ }
153
+ if (lines.length === 1)
154
+ lines.push(" (not enough code yet to infer conventions)");
155
+ else
156
+ lines.push("Match these when adding code here.");
157
+ return lines.join("\n");
158
+ }
159
+ // ---------------------------------------------------------------------------
160
+ // Tool 3: report_drift
161
+ // ---------------------------------------------------------------------------
162
+ /** Current deterministic drift + library-conflict findings (the "you're inconsistent" signals). */
163
+ export function reportDrift(facts, claims, config) {
164
+ const { findings } = analyze(facts, claims, config);
165
+ const relevant = findings.filter((f) => f.kind === "drift" || f.kind === "conflict");
166
+ if (relevant.length === 0)
167
+ return "No drift or library conflicts detected. The code is consistent with its stated decisions.";
168
+ const lines = ["Current drift & library conflicts — fix these or don't add to them:"];
169
+ for (const f of relevant)
170
+ lines.push(` - ${f.kind.toUpperCase()}: ${f.title}`);
171
+ return lines.join("\n");
172
+ }
173
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Report } from "../types.js";
2
+ /** Render a Report as stable, machine-readable JSON. */
3
+ export declare function renderJson(report: Report): string;
@@ -0,0 +1,5 @@
1
+ /** Render a Report as stable, machine-readable JSON. */
2
+ export function renderJson(report) {
3
+ return JSON.stringify(report, null, 2);
4
+ }
5
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/report/json.ts"],"names":[],"mappings":"AAEA,wDAAwD;AACxD,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Report } from "../types.js";
2
+ /**
3
+ * Deterministic, $0 "next steps" — derived from the score breakdown and finding
4
+ * counts. Used as the How-to-improve section when the LLM is off, and as a
5
+ * fallback when it's on.
6
+ */
7
+ export declare function improvementSummary(report: Report): string;
8
+ /** Render a full, uncapped Markdown report. */
9
+ export declare function renderMarkdown(report: Report, improvement: string): string;
@@ -0,0 +1,97 @@
1
+ const KIND_SECTIONS = [
2
+ { kind: "drift", heading: "Drift — stated decisions & docs vs the code" },
3
+ { kind: "conflict", heading: "Library conflicts" },
4
+ { kind: "dup", heading: "Exact duplicates" },
5
+ { kind: "dup-near", heading: "Near duplicates" },
6
+ ];
7
+ function scoreBadge(score) {
8
+ if (score >= 80)
9
+ return "🟢";
10
+ if (score >= 50)
11
+ return "🟡";
12
+ return "🔴";
13
+ }
14
+ /** Render the templated detail string as clean nested bullets. */
15
+ function detailBullets(detail) {
16
+ if (!detail)
17
+ return [];
18
+ return detail
19
+ .split("\n")
20
+ .map((l) => l.trim())
21
+ .filter(Boolean)
22
+ .map((l) => ` - ${l}`);
23
+ }
24
+ function findingBlock(f) {
25
+ const lines = [];
26
+ const loc = f.location ? ` _(${f.location})_` : "";
27
+ lines.push(`- **${f.title}**${loc}`);
28
+ lines.push(...detailBullets(f.detail));
29
+ if (f.explanation)
30
+ lines.push(` - 💡 ${f.explanation}`);
31
+ return lines.join("\n");
32
+ }
33
+ /**
34
+ * Deterministic, $0 "next steps" — derived from the score breakdown and finding
35
+ * counts. Used as the How-to-improve section when the LLM is off, and as a
36
+ * fallback when it's on.
37
+ */
38
+ export function improvementSummary(report) {
39
+ const { breakdown, findings } = report;
40
+ const n = (k) => findings.filter((f) => f.kind === k).length;
41
+ const docDrift = findings.filter((f) => f.kind === "drift" && /references missing path/.test(f.title)).length;
42
+ const stackDrift = n("drift") - docDrift;
43
+ const items = [];
44
+ if (breakdown.duplication > 0) {
45
+ items.push({
46
+ penalty: breakdown.duplication,
47
+ text: `**Duplication (−${breakdown.duplication})** — ${n("dup")} exact + ${n("dup-near")} near-duplicate functions. Extract the shared logic into one utility and update the call sites; this is usually the biggest, safest win.`,
48
+ });
49
+ }
50
+ if (breakdown.drift > 0) {
51
+ const parts = [];
52
+ if (docDrift)
53
+ parts.push(`${docDrift} doc reference${docDrift === 1 ? "" : "s"} point at files that no longer exist — update or delete those links/paths`);
54
+ if (stackDrift)
55
+ parts.push(`${stackDrift} stated decision${stackDrift === 1 ? "" : "s"} contradict the code — align the docs or the code`);
56
+ items.push({ penalty: breakdown.drift, text: `**Drift (−${breakdown.drift})** — ${parts.join("; ")}.` });
57
+ }
58
+ if (breakdown.conflict > 0) {
59
+ items.push({
60
+ penalty: breakdown.conflict,
61
+ text: `**Library conflicts (−${breakdown.conflict})** — ${n("conflict")} capability area${n("conflict") === 1 ? " has" : "s have"} competing libraries. Standardize on one and migrate the rest (or suppress an intentional migration in \`.keelignore\`).`,
62
+ });
63
+ }
64
+ if (items.length === 0)
65
+ return "Nothing to fix — the score is already clean. 🎉";
66
+ items.sort((a, b) => b.penalty - a.penalty);
67
+ return ["Tackle the largest category first — each point back is one fewer inconsistency:", "", ...items.map((it) => `1. ${it.text}`)].join("\n");
68
+ }
69
+ /** Render a full, uncapped Markdown report. */
70
+ export function renderMarkdown(report, improvement) {
71
+ const { score, breakdown, findings, stats } = report;
72
+ const out = [];
73
+ out.push(`# keel report`, "");
74
+ out.push(`## ${scoreBadge(score)} Coherence Score: ${score} / 100`, "");
75
+ out.push(`| Category | Findings | Penalty |`, `|---|---:|---:|`);
76
+ out.push(`| Drift (decisions & docs vs code) | ${findings.filter((f) => f.kind === "drift").length} | −${breakdown.drift} |`);
77
+ out.push(`| Library conflicts | ${findings.filter((f) => f.kind === "conflict").length} | −${breakdown.conflict} |`);
78
+ out.push(`| Duplication (exact + near) | ${findings.filter((f) => f.kind === "dup" || f.kind === "dup-near").length} | −${breakdown.duplication} |`);
79
+ out.push("", `\`scanned ${stats.filesScanned} files · ${stats.durationMs.toFixed(0)}ms · $${stats.costUsd.toFixed(2)}\``, "");
80
+ out.push(`## How to improve`, "", improvement, "");
81
+ out.push(`## Findings (${findings.length})`, "");
82
+ if (findings.length === 0) {
83
+ out.push("No issues found. 🎉", "");
84
+ }
85
+ for (const { kind, heading } of KIND_SECTIONS) {
86
+ const group = findings.filter((f) => f.kind === kind);
87
+ if (group.length === 0)
88
+ continue;
89
+ out.push(`### ${heading} (${group.length})`, "");
90
+ for (const f of group)
91
+ out.push(findingBlock(f));
92
+ out.push("");
93
+ }
94
+ out.push("---", "", "_Generated by keel · suppress a finding with `// keel-ignore`, a `.keelignore` file, or `keel.config.json`._", "");
95
+ return out.join("\n");
96
+ }
97
+ //# sourceMappingURL=markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/report/markdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { Finding, Report } from "../types.js";
2
+ export declare function sortFindings(findings: Finding[]): Finding[];
3
+ /** Default cap on findings printed in text mode; --json always shows all. */
4
+ export declare const DEFAULT_FINDING_LIMIT = 25;
5
+ export interface RenderOptions {
6
+ verbose?: boolean;
7
+ /** Max findings to print (the rest are summarized). 0/undefined uses the default. */
8
+ limit?: number;
9
+ }
10
+ /** Render a Report as the human-facing terminal output. */
11
+ export declare function renderText(report: Report, opts?: RenderOptions): string;
@@ -0,0 +1,76 @@
1
+ import pc from "picocolors";
2
+ function scoreColor(score) {
3
+ if (score >= 80)
4
+ return pc.green;
5
+ if (score >= 50)
6
+ return pc.yellow;
7
+ return pc.red;
8
+ }
9
+ const KIND_ORDER = ["drift", "conflict", "dup", "dup-near"];
10
+ const KIND_LABEL = {
11
+ drift: "DRIFT",
12
+ conflict: "CONFLICT",
13
+ dup: "DUP",
14
+ "dup-near": "DUP~",
15
+ };
16
+ function marker(f) {
17
+ if (f.confidence === "high")
18
+ return pc.red("✗");
19
+ if (f.confidence === "medium")
20
+ return pc.yellow("!");
21
+ return pc.dim("~");
22
+ }
23
+ export function sortFindings(findings) {
24
+ return [...findings].sort((a, b) => KIND_ORDER.indexOf(a.kind) - KIND_ORDER.indexOf(b.kind) ||
25
+ b.penalty - a.penalty ||
26
+ a.title.localeCompare(b.title));
27
+ }
28
+ /** Default cap on findings printed in text mode; --json always shows all. */
29
+ export const DEFAULT_FINDING_LIMIT = 25;
30
+ /** Render a Report as the human-facing terminal output. */
31
+ export function renderText(report, opts = {}) {
32
+ const { score, breakdown, findings, stats } = report;
33
+ const out = [];
34
+ const color = scoreColor(score);
35
+ out.push("");
36
+ out.push(` ${pc.bold("Coherence Score:")} ${color(pc.bold(`${score} / 100`))}`);
37
+ out.push("");
38
+ const rows = [
39
+ ["Drift", breakdown.drift],
40
+ ["Library conflicts", breakdown.conflict],
41
+ ["Duplication", breakdown.duplication],
42
+ ];
43
+ for (const [label, penalty] of rows) {
44
+ if (penalty === 0)
45
+ continue;
46
+ out.push(` ${pc.dim(label.padEnd(20))}${pc.red(`-${penalty}`)}`);
47
+ }
48
+ if (findings.length === 0) {
49
+ out.push(` ${pc.green("No issues found.")}`);
50
+ }
51
+ else {
52
+ out.push("");
53
+ out.push(` ${pc.bold("Findings")}`);
54
+ const limit = opts.limit && opts.limit > 0 ? opts.limit : DEFAULT_FINDING_LIMIT;
55
+ const sorted = sortFindings(findings);
56
+ for (const f of sorted.slice(0, limit)) {
57
+ out.push(` ${marker(f)} ${pc.bold(KIND_LABEL[f.kind].padEnd(9))} ${f.title}`);
58
+ if (f.explanation) {
59
+ out.push(` ${pc.cyan("›")} ${pc.dim(f.explanation)}`);
60
+ }
61
+ if (opts.verbose && f.detail) {
62
+ for (const line of f.detail.split("\n"))
63
+ out.push(` ${pc.dim(line)}`);
64
+ }
65
+ }
66
+ if (sorted.length > limit) {
67
+ out.push(` ${pc.dim(`… and ${sorted.length - limit} more (use --json to see all, or --limit N)`)}`);
68
+ }
69
+ }
70
+ out.push("");
71
+ out.push(pc.dim(` scanned ${stats.filesScanned} files · ${stats.durationMs.toFixed(1)}ms · ` +
72
+ `$${stats.costUsd.toFixed(2)}`));
73
+ out.push("");
74
+ return out.join("\n");
75
+ }
76
+ //# sourceMappingURL=text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/report/text.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /** Parsed `.keelignore` rules. */
2
+ export interface Suppressions {
3
+ /** Capability buckets to never flag, e.g. "http". */
4
+ buckets: Set<string>;
5
+ /** Path matchers; a finding located in a matching file is dropped. */
6
+ paths: PathRule[];
7
+ }
8
+ interface PathRule {
9
+ raw: string;
10
+ test: (file: string) => boolean;
11
+ }
12
+ /** Lines (1-based) of a file's content that carry an inline keel-ignore marker. */
13
+ export declare function ignoreLinesOf(content: string): number[];
14
+ /** True if `text` (a single line) carries an inline keel-ignore marker. */
15
+ export declare function lineHasIgnore(text: string | undefined): boolean;
16
+ /** Load `.keelignore` from the repo root (absent file => no suppressions). */
17
+ export declare function loadKeelignore(root: string): Suppressions;
18
+ /** Extract the file portion of a "path:line" location string. */
19
+ export declare function locationFile(location: string): string;
20
+ /** True if a finding located in `file` is suppressed by a `.keelignore` path rule. */
21
+ export declare function isPathSuppressed(supp: Suppressions, file: string): boolean;
22
+ export {};
@@ -0,0 +1,80 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ /** The marker keel looks for in inline comments to suppress a finding. */
4
+ const MARKER = /keel-ignore/i;
5
+ /** Lines (1-based) of a file's content that carry an inline keel-ignore marker. */
6
+ export function ignoreLinesOf(content) {
7
+ const out = [];
8
+ const lines = content.split(/\r?\n/);
9
+ for (let i = 0; i < lines.length; i++) {
10
+ if (MARKER.test(lines[i]))
11
+ out.push(i + 1);
12
+ }
13
+ return out;
14
+ }
15
+ /** True if `text` (a single line) carries an inline keel-ignore marker. */
16
+ export function lineHasIgnore(text) {
17
+ return text !== undefined && MARKER.test(text);
18
+ }
19
+ const REGEX_SPECIAL = new Set([".", "+", "^", "$", "{", "}", "(", ")", "|", "[", "]", "\\"]);
20
+ /** Compile a glob (`*` = within a path segment, `**` = across segments) to a RegExp. */
21
+ function globToRegExp(glob) {
22
+ let re = "";
23
+ for (let i = 0; i < glob.length; i++) {
24
+ const c = glob[i];
25
+ if (c === "*") {
26
+ if (glob[i + 1] === "*") {
27
+ re += ".*";
28
+ i += 1;
29
+ }
30
+ else {
31
+ re += "[^/]*";
32
+ }
33
+ }
34
+ else if (REGEX_SPECIAL.has(c)) {
35
+ re += "\\" + c;
36
+ }
37
+ else {
38
+ re += c;
39
+ }
40
+ }
41
+ return new RegExp("^" + re + "$");
42
+ }
43
+ function makePathRule(raw) {
44
+ if (raw.includes("*")) {
45
+ const re = globToRegExp(raw);
46
+ return { raw, test: (file) => re.test(file) };
47
+ }
48
+ // No wildcard: treat as exact file or directory prefix.
49
+ const prefix = raw.endsWith("/") ? raw : raw + "/";
50
+ return { raw, test: (file) => file === raw || file.startsWith(prefix) };
51
+ }
52
+ /** Load `.keelignore` from the repo root (absent file => no suppressions). */
53
+ export function loadKeelignore(root) {
54
+ const buckets = new Set();
55
+ const paths = [];
56
+ const path = join(root, ".keelignore");
57
+ if (!existsSync(path))
58
+ return { buckets, paths };
59
+ for (const line of readFileSync(path, "utf8").split(/\r?\n/)) {
60
+ const trimmed = line.trim();
61
+ if (!trimmed || trimmed.startsWith("#"))
62
+ continue;
63
+ if (trimmed.startsWith("bucket:")) {
64
+ buckets.add(trimmed.slice("bucket:".length).trim());
65
+ }
66
+ else {
67
+ paths.push(makePathRule(trimmed));
68
+ }
69
+ }
70
+ return { buckets, paths };
71
+ }
72
+ /** Extract the file portion of a "path:line" location string. */
73
+ export function locationFile(location) {
74
+ return location.replace(/:\d+$/, "");
75
+ }
76
+ /** True if a finding located in `file` is suppressed by a `.keelignore` path rule. */
77
+ export function isPathSuppressed(supp, file) {
78
+ return supp.paths.some((rule) => rule.test(file));
79
+ }
80
+ //# sourceMappingURL=suppress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suppress.js","sourceRoot":"","sources":["../src/suppress.ts"],"names":[],"mappings":"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"}