@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,144 @@
1
+ /**
2
+ * Core data shapes for keel.
3
+ *
4
+ * `Facts` is the deterministic ground truth extracted from the codebase.
5
+ * `Claims` is what the context file (CLAUDE.md / AGENTS.md) says is true.
6
+ * The analyze engines compare the two and emit `Finding`s.
7
+ */
8
+ /** A single `import`/`require` occurrence in a file. */
9
+ export interface ImportFact {
10
+ /** The raw module specifier, e.g. "zod", "./utils", "@scope/pkg/sub". */
11
+ specifier: string;
12
+ /** True when the specifier resolves to an external package (not relative/absolute local). */
13
+ external: boolean;
14
+ /**
15
+ * The npm package name for external specifiers, with subpaths stripped:
16
+ * "axios/lib/foo" -> "axios", "@scope/pkg/sub" -> "@scope/pkg".
17
+ * Null for local imports.
18
+ */
19
+ packageName: string | null;
20
+ /** Named bindings imported, e.g. ["z"] for `import { z } from "zod"`. */
21
+ named: string[];
22
+ /** 1-based line of the import statement. */
23
+ line: number;
24
+ }
25
+ /** A function/method declaration with the data needed for duplication detection. */
26
+ export interface FunctionFact {
27
+ /** Declared name, or null for anonymous functions. */
28
+ name: string | null;
29
+ /** Owning file, relative to the scan root. */
30
+ filePath: string;
31
+ /** 1-based start line. */
32
+ startLine: number;
33
+ /** 1-based end line. */
34
+ endLine: number;
35
+ /**
36
+ * Hash of the normalized function body (comments/whitespace stripped,
37
+ * locals renamed to placeholders). Equal hashes => exact duplicates.
38
+ * Null when the body is too trivial to consider (below the token gate).
39
+ */
40
+ bodyHash: string | null;
41
+ /** Count of significant tokens in the normalized body (the complexity gate). */
42
+ tokenCount: number;
43
+ /**
44
+ * Winnowing fingerprints of the normalized body, for near-duplicate
45
+ * similarity. Empty when the body is below the complexity gate.
46
+ */
47
+ fingerprints: number[];
48
+ /** Normalized signature string, used later for semantic-dup shortlisting. */
49
+ signature: string;
50
+ }
51
+ /** Identifier casing tally for a file, used for naming-convention drift. */
52
+ export interface NamingStats {
53
+ /** Single all-lowercase word (e.g. `count`) — valid camelCase *and* snake_case. */
54
+ lower: number;
55
+ /** Multi-word camelCase with an internal capital (e.g. `formatDate`). */
56
+ camelCase: number;
57
+ PascalCase: number;
58
+ snake_case: number;
59
+ CONSTANT_CASE: number;
60
+ other: number;
61
+ }
62
+ /** Source language of a file. */
63
+ export type Language = "javascript" | "python";
64
+ /** Everything extracted from one source file. */
65
+ export interface FileFacts {
66
+ /** Path relative to the scan root. */
67
+ path: string;
68
+ /** Content hash, used as the cache key. */
69
+ hash: string;
70
+ /** Source language this file was parsed as. */
71
+ language: Language;
72
+ imports: ImportFact[];
73
+ functions: FunctionFact[];
74
+ naming: NamingStats;
75
+ /** 1-based lines carrying an inline `keel-ignore` marker. */
76
+ ignoreLines: number[];
77
+ }
78
+ export type PackageManager = "npm" | "pnpm" | "yarn" | "bun" | "unknown";
79
+ /** Facts derived from package.json + lockfiles. */
80
+ export interface PackageFacts {
81
+ name: string | null;
82
+ dependencies: Record<string, string>;
83
+ devDependencies: Record<string, string>;
84
+ /** Union of dependency + devDependency names. */
85
+ allDeps: string[];
86
+ /** Inferred from the lockfile present in the repo root. */
87
+ packageManager: PackageManager;
88
+ }
89
+ /** The complete deterministic picture of a repository. */
90
+ export interface Facts {
91
+ /** Absolute scan root. */
92
+ root: string;
93
+ files: FileFacts[];
94
+ /** Null when no package.json was found. */
95
+ pkg: PackageFacts | null;
96
+ }
97
+ /** A single machine-checkable assertion from CLAUDE.md / AGENTS.md. */
98
+ export interface Claim {
99
+ /** Normalized key, e.g. "validation", "database", "packageManager", "naming". */
100
+ key: string;
101
+ /** Raw value as written, e.g. "Zod", "PostgreSQL", "camelCase". */
102
+ value: string;
103
+ /** Which file the claim came from. */
104
+ source: string;
105
+ /** 1-based line in the source file. */
106
+ line: number;
107
+ }
108
+ export interface Claims {
109
+ stack: Claim[];
110
+ constraints: Claim[];
111
+ }
112
+ export type FindingKind = "drift" | "conflict" | "dup" | "dup-near";
113
+ /** Confidence tier — drives severity styling and whether it can fail CI. */
114
+ export type Confidence = "high" | "medium" | "low";
115
+ export interface Finding {
116
+ kind: FindingKind;
117
+ confidence: Confidence;
118
+ /** Short headline, e.g. `HTTP: axios (4 files), ky (1 file)`. */
119
+ title: string;
120
+ /** Optional longer explanation (templated; LLM-enriched later). */
121
+ detail?: string;
122
+ /** Optional plain-language explanation from the LLM (advisory; never affects the score). */
123
+ explanation?: string;
124
+ /** Primary file/location this finding points at, when applicable. */
125
+ location?: string;
126
+ /** Point penalty applied to the Coherence Score. */
127
+ penalty: number;
128
+ }
129
+ export interface ScoreBreakdown {
130
+ drift: number;
131
+ conflict: number;
132
+ duplication: number;
133
+ }
134
+ export interface Report {
135
+ score: number;
136
+ breakdown: ScoreBreakdown;
137
+ findings: Finding[];
138
+ stats: {
139
+ filesScanned: number;
140
+ durationMs: number;
141
+ /** Always 0 in v0 (no LLM). */
142
+ costUsd: number;
143
+ };
144
+ }
package/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Core data shapes for keel.
3
+ *
4
+ * `Facts` is the deterministic ground truth extracted from the codebase.
5
+ * `Claims` is what the context file (CLAUDE.md / AGENTS.md) says is true.
6
+ * The analyze engines compare the two and emit `Finding`s.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Compute a document fingerprint for a normalized token stream using winnowing
3
+ * (the MOSS algorithm). Returns a sorted, de-duplicated set of k-gram hashes —
4
+ * a compact signature that survives small edits, so two near-identical bodies
5
+ * share most of their fingerprints.
6
+ */
7
+ export declare function computeFingerprints(tokens: string[], k?: number, w?: number): number[];
8
+ /**
9
+ * Jaccard similarity of two fingerprint sets (each already unique): the share
10
+ * of fingerprints they have in common. 1 = identical, 0 = nothing in common.
11
+ */
12
+ export declare function jaccard(a: number[], b: number[]): number;
@@ -0,0 +1,60 @@
1
+ import { fnv1a } from "./hash.js";
2
+ /** k-gram size: how many consecutive tokens form one fingerprint unit. */
3
+ const K = 5;
4
+ /** Winnowing window: guarantees any shared run of >= K + W - 1 tokens is caught. */
5
+ const W = 4;
6
+ /**
7
+ * Compute a document fingerprint for a normalized token stream using winnowing
8
+ * (the MOSS algorithm). Returns a sorted, de-duplicated set of k-gram hashes —
9
+ * a compact signature that survives small edits, so two near-identical bodies
10
+ * share most of their fingerprints.
11
+ */
12
+ export function computeFingerprints(tokens, k = K, w = W) {
13
+ if (tokens.length === 0)
14
+ return [];
15
+ if (tokens.length < k)
16
+ return [fnv1a(tokens.join(""))];
17
+ const grams = [];
18
+ for (let i = 0; i + k <= tokens.length; i++) {
19
+ grams.push(fnv1a(tokens.slice(i, i + k).join("")));
20
+ }
21
+ return winnow(grams, w);
22
+ }
23
+ /** Select fingerprints: in each window of `w` k-grams, keep the minimum hash. */
24
+ function winnow(hashes, w) {
25
+ const fps = new Set();
26
+ if (hashes.length <= w) {
27
+ fps.add(Math.min(...hashes));
28
+ return [...fps].sort((a, b) => a - b);
29
+ }
30
+ let lastIdx = -1;
31
+ for (let i = 0; i + w <= hashes.length; i++) {
32
+ let minIdx = i;
33
+ for (let j = i + 1; j < i + w; j++) {
34
+ if (hashes[j] <= hashes[minIdx])
35
+ minIdx = j; // rightmost-min on ties
36
+ }
37
+ if (minIdx !== lastIdx) {
38
+ fps.add(hashes[minIdx]);
39
+ lastIdx = minIdx;
40
+ }
41
+ }
42
+ return [...fps].sort((a, b) => a - b);
43
+ }
44
+ /**
45
+ * Jaccard similarity of two fingerprint sets (each already unique): the share
46
+ * of fingerprints they have in common. 1 = identical, 0 = nothing in common.
47
+ */
48
+ export function jaccard(a, b) {
49
+ if (a.length === 0 && b.length === 0)
50
+ return 1;
51
+ if (a.length === 0 || b.length === 0)
52
+ return 0;
53
+ const setA = new Set(a);
54
+ let intersection = 0;
55
+ for (const x of b)
56
+ if (setA.has(x))
57
+ intersection += 1;
58
+ return intersection / (a.length + b.length - intersection);
59
+ }
60
+ //# sourceMappingURL=fingerprint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../../src/util/fingerprint.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ /** Stable short hash of a string, used for file-content and body-normalization keys. */
2
+ export declare function sha1(text: string): string;
3
+ /** Fast 32-bit FNV-1a hash, used for k-gram fingerprints (non-cryptographic). */
4
+ export declare function fnv1a(text: string): number;
@@ -0,0 +1,15 @@
1
+ import { createHash } from "node:crypto";
2
+ /** Stable short hash of a string, used for file-content and body-normalization keys. */
3
+ export function sha1(text) {
4
+ return createHash("sha1").update(text).digest("hex");
5
+ }
6
+ /** Fast 32-bit FNV-1a hash, used for k-gram fingerprints (non-cryptographic). */
7
+ export function fnv1a(text) {
8
+ let h = 0x811c9dc5;
9
+ for (let i = 0; i < text.length; i++) {
10
+ h ^= text.charCodeAt(i);
11
+ h = Math.imul(h, 0x01000193);
12
+ }
13
+ return h >>> 0;
14
+ }
15
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/util/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,wFAAwF;AACxF,MAAM,UAAU,IAAI,CAAC,IAAY;IAC/B,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACvD,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,KAAK,CAAC,IAAY;IAChC,IAAI,CAAC,GAAG,UAAU,CAAC;IACnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACxB,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@curiousnerd/keel",
3
+ "version": "0.1.0",
4
+ "description": "A local-first verifier that keeps an AI coding agent honest: catches drift, library conflicts, and duplication across JS/TS and Python.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Aditya",
8
+ "keywords": [
9
+ "ai",
10
+ "coding-agent",
11
+ "static-analysis",
12
+ "drift",
13
+ "duplication",
14
+ "code-quality",
15
+ "mcp",
16
+ "claude",
17
+ "linter",
18
+ "typescript",
19
+ "python"
20
+ ],
21
+ "bin": {
22
+ "keel": "dist/cli.js"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "files": [
28
+ "dist",
29
+ "data"
30
+ ],
31
+ "engines": {
32
+ "node": ">=20"
33
+ },
34
+ "scripts": {
35
+ "build": "tsc -p tsconfig.json",
36
+ "dev": "tsx src/cli.ts",
37
+ "typecheck": "tsc -p tsconfig.json --noEmit",
38
+ "test": "vitest run",
39
+ "test:watch": "vitest",
40
+ "prepublishOnly": "npm run typecheck && npm run test && npm run build"
41
+ },
42
+ "dependencies": {
43
+ "@modelcontextprotocol/sdk": "^1.29.0",
44
+ "commander": "^12.1.0",
45
+ "ignore": "^5.3.2",
46
+ "picocolors": "^1.1.1",
47
+ "tree-sitter-wasms": "^0.1.13",
48
+ "ts-morph": "^23.0.0",
49
+ "web-tree-sitter": "^0.20.8",
50
+ "zod": "^4.4.3"
51
+ },
52
+ "devDependencies": {
53
+ "@types/node": "^22.10.0",
54
+ "tsx": "^4.19.0",
55
+ "typescript": "^5.7.0",
56
+ "vitest": "^2.1.0"
57
+ }
58
+ }