@kernlang/review 2.0.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/LICENSE +661 -0
- package/dist/differ.d.ts +11 -0
- package/dist/differ.js +132 -0
- package/dist/differ.js.map +1 -0
- package/dist/external-tools.d.ts +32 -0
- package/dist/external-tools.js +173 -0
- package/dist/external-tools.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +98 -0
- package/dist/index.js.map +1 -0
- package/dist/inferrer.d.ts +15 -0
- package/dist/inferrer.js +502 -0
- package/dist/inferrer.js.map +1 -0
- package/dist/llm-review.d.ts +24 -0
- package/dist/llm-review.js +197 -0
- package/dist/llm-review.js.map +1 -0
- package/dist/quality-rules.d.ts +12 -0
- package/dist/quality-rules.js +28 -0
- package/dist/quality-rules.js.map +1 -0
- package/dist/reporter.d.ts +15 -0
- package/dist/reporter.js +217 -0
- package/dist/reporter.js.map +1 -0
- package/dist/rules/base.d.ts +10 -0
- package/dist/rules/base.js +556 -0
- package/dist/rules/base.js.map +1 -0
- package/dist/rules/express.d.ts +9 -0
- package/dist/rules/express.js +107 -0
- package/dist/rules/express.js.map +1 -0
- package/dist/rules/index.d.ts +16 -0
- package/dist/rules/index.js +38 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/nextjs.d.ts +9 -0
- package/dist/rules/nextjs.js +128 -0
- package/dist/rules/nextjs.js.map +1 -0
- package/dist/rules/react.d.ts +9 -0
- package/dist/rules/react.js +252 -0
- package/dist/rules/react.js.map +1 -0
- package/dist/rules/vue.d.ts +9 -0
- package/dist/rules/vue.js +198 -0
- package/dist/rules/vue.js.map +1 -0
- package/dist/template-detector.d.ts +12 -0
- package/dist/template-detector.js +225 -0
- package/dist/template-detector.js.map +1 -0
- package/dist/types.d.ts +152 -0
- package/dist/types.js +17 -0
- package/dist/types.js.map +1 -0
- package/package.json +23 -0
package/dist/differ.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Differ — compares original TS with recompiled TS from inferred KERN nodes.
|
|
3
|
+
*
|
|
4
|
+
* v2: Returns unified ReviewFinding[] instead of DiffFinding[].
|
|
5
|
+
*/
|
|
6
|
+
import { generateCoreNode, isCoreNode } from '@kernlang/core';
|
|
7
|
+
import { createFingerprint } from './types.js';
|
|
8
|
+
function span(file, line, col = 1) {
|
|
9
|
+
return { file, startLine: line, startCol: col, endLine: line, endCol: col };
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Perform a structural diff between original source and what KERN would generate.
|
|
13
|
+
* Returns unified ReviewFinding[] tagged with source='kern'.
|
|
14
|
+
*/
|
|
15
|
+
export function structuralDiff(originalSource, inferred, filePath = 'input.ts') {
|
|
16
|
+
const findings = [];
|
|
17
|
+
const originalLines = originalSource.split('\n');
|
|
18
|
+
const totalLines = originalLines.length;
|
|
19
|
+
// Track which lines are covered by inferred constructs
|
|
20
|
+
const coveredLines = new Set();
|
|
21
|
+
for (const r of inferred) {
|
|
22
|
+
for (let i = r.startLine; i <= r.endLine; i++) {
|
|
23
|
+
coveredLines.add(i);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Find uncovered non-trivial lines (potential dead code or non-KERN patterns)
|
|
27
|
+
let consecutiveUncovered = 0;
|
|
28
|
+
let uncoveredStart = 0;
|
|
29
|
+
for (let i = 1; i <= totalLines; i++) {
|
|
30
|
+
const line = originalLines[i - 1];
|
|
31
|
+
const trimmed = line.trim();
|
|
32
|
+
const isTrivial = !trimmed ||
|
|
33
|
+
trimmed.startsWith('//') ||
|
|
34
|
+
trimmed.startsWith('/*') ||
|
|
35
|
+
trimmed.startsWith('*') ||
|
|
36
|
+
trimmed === '*/';
|
|
37
|
+
if (!coveredLines.has(i) && !isTrivial) {
|
|
38
|
+
if (consecutiveUncovered === 0)
|
|
39
|
+
uncoveredStart = i;
|
|
40
|
+
consecutiveUncovered++;
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
if (consecutiveUncovered >= 3) {
|
|
44
|
+
findings.push({
|
|
45
|
+
source: 'kern',
|
|
46
|
+
ruleId: 'extra-code',
|
|
47
|
+
severity: 'info',
|
|
48
|
+
category: 'structure',
|
|
49
|
+
message: `${consecutiveUncovered} uncovered lines (L${uncoveredStart}-${uncoveredStart + consecutiveUncovered - 1}) — not expressible as KERN`,
|
|
50
|
+
primarySpan: span(filePath, uncoveredStart),
|
|
51
|
+
fingerprint: createFingerprint('extra-code', uncoveredStart, 1),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
consecutiveUncovered = 0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Flush remaining
|
|
58
|
+
if (consecutiveUncovered >= 3) {
|
|
59
|
+
findings.push({
|
|
60
|
+
source: 'kern',
|
|
61
|
+
ruleId: 'extra-code',
|
|
62
|
+
severity: 'info',
|
|
63
|
+
category: 'structure',
|
|
64
|
+
message: `${consecutiveUncovered} uncovered lines (L${uncoveredStart}-${uncoveredStart + consecutiveUncovered - 1}) — not expressible as KERN`,
|
|
65
|
+
primarySpan: span(filePath, uncoveredStart),
|
|
66
|
+
fingerprint: createFingerprint('extra-code', uncoveredStart, 1),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
// Check for inconsistent patterns in inferred constructs
|
|
70
|
+
for (const r of inferred) {
|
|
71
|
+
if (r.node.type === 'interface' && r.node.children) {
|
|
72
|
+
const fields = r.node.children.filter(c => c.type === 'field');
|
|
73
|
+
const optionalFields = fields.filter(f => f.props?.optional === 'true');
|
|
74
|
+
if (fields.length > 5 && optionalFields.length === 0) {
|
|
75
|
+
findings.push({
|
|
76
|
+
source: 'kern',
|
|
77
|
+
ruleId: 'inconsistent-pattern',
|
|
78
|
+
severity: 'info',
|
|
79
|
+
category: 'pattern',
|
|
80
|
+
message: `Interface ${r.node.props?.name} has ${fields.length} fields but none optional — consider which fields are truly required`,
|
|
81
|
+
primarySpan: span(filePath, r.startLine),
|
|
82
|
+
fingerprint: createFingerprint('inconsistent-pattern', r.startLine, 1),
|
|
83
|
+
nodeIds: [r.nodeId],
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (r.node.type === 'fn') {
|
|
88
|
+
if (!r.node.props?.returns) {
|
|
89
|
+
findings.push({
|
|
90
|
+
source: 'kern',
|
|
91
|
+
ruleId: 'missing-type',
|
|
92
|
+
severity: 'warning',
|
|
93
|
+
category: 'type',
|
|
94
|
+
message: `Function ${r.node.props?.name} has no explicit return type`,
|
|
95
|
+
primarySpan: span(filePath, r.startLine),
|
|
96
|
+
fingerprint: createFingerprint('missing-type', r.startLine, 1),
|
|
97
|
+
nodeIds: [r.nodeId],
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Roundtrip comparison: recompile inferred nodes and compare
|
|
103
|
+
for (const r of inferred) {
|
|
104
|
+
if (!isCoreNode(r.node.type))
|
|
105
|
+
continue;
|
|
106
|
+
try {
|
|
107
|
+
const recompiled = generateCoreNode(r.node);
|
|
108
|
+
if (recompiled.length === 0)
|
|
109
|
+
continue;
|
|
110
|
+
const originalLineCount = r.endLine - r.startLine + 1;
|
|
111
|
+
const recompiledLineCount = recompiled.length;
|
|
112
|
+
const delta = Math.abs(originalLineCount - recompiledLineCount);
|
|
113
|
+
if (delta > originalLineCount * 0.5 && originalLineCount > 5) {
|
|
114
|
+
findings.push({
|
|
115
|
+
source: 'kern',
|
|
116
|
+
ruleId: 'style-difference',
|
|
117
|
+
severity: 'info',
|
|
118
|
+
category: 'style',
|
|
119
|
+
message: `${r.node.props?.name}: roundtrip diff — original ${originalLineCount} lines vs recompiled ${recompiledLineCount} lines`,
|
|
120
|
+
primarySpan: span(filePath, r.startLine),
|
|
121
|
+
fingerprint: createFingerprint('style-difference', r.startLine, 1),
|
|
122
|
+
nodeIds: [r.nodeId],
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Recompilation failed — the inference was probably incomplete
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return findings;
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=differ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"differ.js","sourceRoot":"","sources":["../src/differ.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External Tools — ESLint Node API + ts-morph diagnostics integration.
|
|
3
|
+
*
|
|
4
|
+
* Uses Node APIs (not child processes). Batched per tsconfig.
|
|
5
|
+
* ESLint is an optional peer dependency — gracefully degrades if not available.
|
|
6
|
+
*
|
|
7
|
+
* Phase 3 of the review pipeline.
|
|
8
|
+
*/
|
|
9
|
+
import type { Project } from 'ts-morph';
|
|
10
|
+
import type { InferResult, ReviewFinding } from './types.js';
|
|
11
|
+
/**
|
|
12
|
+
* Run ESLint on given file paths using the Node API.
|
|
13
|
+
* Returns normalized ReviewFinding[] with source='eslint'.
|
|
14
|
+
* Returns empty array if ESLint is not installed.
|
|
15
|
+
*/
|
|
16
|
+
export declare function runESLint(filePaths: string[], cwd: string): Promise<ReviewFinding[]>;
|
|
17
|
+
/**
|
|
18
|
+
* Run TypeScript compiler diagnostics using ts-morph's existing Project.
|
|
19
|
+
* Reuses the Project already created by the inferrer — no extra compilation.
|
|
20
|
+
*/
|
|
21
|
+
export declare function runTSCDiagnostics(project: Project): ReviewFinding[];
|
|
22
|
+
/**
|
|
23
|
+
* Run TypeScript compiler diagnostics from file paths (no pre-existing Project).
|
|
24
|
+
* Creates a real-filesystem Project, adds files, runs diagnostics.
|
|
25
|
+
* Used by the CLI --lint path where only file paths are available.
|
|
26
|
+
*/
|
|
27
|
+
export declare function runTSCDiagnosticsFromPaths(filePaths: string[]): ReviewFinding[];
|
|
28
|
+
/**
|
|
29
|
+
* For each external finding, find the inferred node whose sourceSpan contains it.
|
|
30
|
+
* Attaches nodeId to the finding for cross-referencing.
|
|
31
|
+
*/
|
|
32
|
+
export declare function linkToNodes(findings: ReviewFinding[], inferred: InferResult[]): ReviewFinding[];
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External Tools — ESLint Node API + ts-morph diagnostics integration.
|
|
3
|
+
*
|
|
4
|
+
* Uses Node APIs (not child processes). Batched per tsconfig.
|
|
5
|
+
* ESLint is an optional peer dependency — gracefully degrades if not available.
|
|
6
|
+
*
|
|
7
|
+
* Phase 3 of the review pipeline.
|
|
8
|
+
*/
|
|
9
|
+
import { createFingerprint } from './types.js';
|
|
10
|
+
import { createProject } from './inferrer.js';
|
|
11
|
+
// ── ESLint via Node API ──────────────────────────────────────────────────
|
|
12
|
+
/**
|
|
13
|
+
* Run ESLint on given file paths using the Node API.
|
|
14
|
+
* Returns normalized ReviewFinding[] with source='eslint'.
|
|
15
|
+
* Returns empty array if ESLint is not installed.
|
|
16
|
+
*/
|
|
17
|
+
export async function runESLint(filePaths, cwd) {
|
|
18
|
+
try {
|
|
19
|
+
// Dynamic import — ESLint is an optional peer dep
|
|
20
|
+
const eslintModuleName = 'eslint';
|
|
21
|
+
const eslintModule = await import(eslintModuleName);
|
|
22
|
+
const ESLint = eslintModule.ESLint || eslintModule.default?.ESLint;
|
|
23
|
+
if (!ESLint)
|
|
24
|
+
return [];
|
|
25
|
+
const eslint = new ESLint({ cwd });
|
|
26
|
+
const results = await eslint.lintFiles(filePaths);
|
|
27
|
+
const findings = [];
|
|
28
|
+
for (const result of results) {
|
|
29
|
+
for (const msg of result.messages) {
|
|
30
|
+
const severity = msg.severity === 2 ? 'error' : msg.severity === 1 ? 'warning' : 'info';
|
|
31
|
+
const primarySpan = {
|
|
32
|
+
file: result.filePath,
|
|
33
|
+
startLine: msg.line,
|
|
34
|
+
startCol: msg.column,
|
|
35
|
+
endLine: msg.endLine ?? msg.line,
|
|
36
|
+
endCol: msg.endColumn ?? msg.column,
|
|
37
|
+
};
|
|
38
|
+
findings.push({
|
|
39
|
+
source: 'eslint',
|
|
40
|
+
ruleId: msg.ruleId || 'eslint-unknown',
|
|
41
|
+
severity,
|
|
42
|
+
category: categorizeESLintRule(msg.ruleId || ''),
|
|
43
|
+
message: msg.message,
|
|
44
|
+
primarySpan,
|
|
45
|
+
suggestion: msg.fix ? 'Auto-fixable' : undefined,
|
|
46
|
+
fingerprint: createFingerprint(msg.ruleId || 'eslint', msg.line, msg.column),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return findings;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// ESLint not installed or failed to load
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Map ESLint rule IDs to ReviewFinding categories.
|
|
59
|
+
*/
|
|
60
|
+
function categorizeESLintRule(ruleId) {
|
|
61
|
+
if (ruleId.includes('no-unused') || ruleId.includes('prefer-'))
|
|
62
|
+
return 'style';
|
|
63
|
+
if (ruleId.includes('no-undef') || ruleId.includes('type'))
|
|
64
|
+
return 'type';
|
|
65
|
+
if (ruleId.includes('security') || ruleId.includes('injection'))
|
|
66
|
+
return 'bug';
|
|
67
|
+
if (ruleId.includes('import') || ruleId.includes('module'))
|
|
68
|
+
return 'structure';
|
|
69
|
+
return 'pattern';
|
|
70
|
+
}
|
|
71
|
+
// ── tsc Diagnostics via ts-morph ─────────────────────────────────────────
|
|
72
|
+
/**
|
|
73
|
+
* Run TypeScript compiler diagnostics using ts-morph's existing Project.
|
|
74
|
+
* Reuses the Project already created by the inferrer — no extra compilation.
|
|
75
|
+
*/
|
|
76
|
+
export function runTSCDiagnostics(project) {
|
|
77
|
+
const findings = [];
|
|
78
|
+
try {
|
|
79
|
+
const diagnostics = project.getPreEmitDiagnostics();
|
|
80
|
+
for (const diag of diagnostics) {
|
|
81
|
+
const sourceFile = diag.getSourceFile();
|
|
82
|
+
if (!sourceFile)
|
|
83
|
+
continue;
|
|
84
|
+
const filePath = sourceFile.getFilePath();
|
|
85
|
+
const start = diag.getStart();
|
|
86
|
+
const length = diag.getLength();
|
|
87
|
+
let startLine = 1;
|
|
88
|
+
let startCol = 1;
|
|
89
|
+
let endLine = 1;
|
|
90
|
+
let endCol = 1;
|
|
91
|
+
if (start !== undefined) {
|
|
92
|
+
const startPos = sourceFile.getLineAndColumnAtPos(start);
|
|
93
|
+
startLine = startPos.line;
|
|
94
|
+
startCol = startPos.column;
|
|
95
|
+
if (length !== undefined) {
|
|
96
|
+
const endPos = sourceFile.getLineAndColumnAtPos(start + length);
|
|
97
|
+
endLine = endPos.line;
|
|
98
|
+
endCol = endPos.column;
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
endLine = startLine;
|
|
102
|
+
endCol = startCol;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const category = diag.getCategory();
|
|
106
|
+
const severity = category === 1 /* Error */ ? 'error' :
|
|
107
|
+
category === 0 /* Warning */ ? 'warning' : 'info';
|
|
108
|
+
const code = diag.getCode();
|
|
109
|
+
const message = diag.getMessageText();
|
|
110
|
+
const messageStr = typeof message === 'string' ? message : message.getMessageText();
|
|
111
|
+
findings.push({
|
|
112
|
+
source: 'tsc',
|
|
113
|
+
ruleId: `ts${code}`,
|
|
114
|
+
severity,
|
|
115
|
+
category: 'type',
|
|
116
|
+
message: messageStr,
|
|
117
|
+
primarySpan: {
|
|
118
|
+
file: filePath,
|
|
119
|
+
startLine,
|
|
120
|
+
startCol,
|
|
121
|
+
endLine,
|
|
122
|
+
endCol,
|
|
123
|
+
},
|
|
124
|
+
fingerprint: createFingerprint(`ts${code}`, startLine, startCol),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Diagnostics collection failed — skip
|
|
130
|
+
}
|
|
131
|
+
return findings;
|
|
132
|
+
}
|
|
133
|
+
// ── tsc Diagnostics from file paths ───────────────────────────────────
|
|
134
|
+
/**
|
|
135
|
+
* Run TypeScript compiler diagnostics from file paths (no pre-existing Project).
|
|
136
|
+
* Creates a real-filesystem Project, adds files, runs diagnostics.
|
|
137
|
+
* Used by the CLI --lint path where only file paths are available.
|
|
138
|
+
*/
|
|
139
|
+
export function runTSCDiagnosticsFromPaths(filePaths) {
|
|
140
|
+
if (filePaths.length === 0)
|
|
141
|
+
return [];
|
|
142
|
+
try {
|
|
143
|
+
const project = createProject();
|
|
144
|
+
for (const fp of filePaths) {
|
|
145
|
+
try {
|
|
146
|
+
project.addSourceFileAtPath(fp);
|
|
147
|
+
}
|
|
148
|
+
catch { /* skip unreadable files */ }
|
|
149
|
+
}
|
|
150
|
+
return runTSCDiagnostics(project);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// ── Link External Findings to KERN NodeIds ───────────────────────────────
|
|
157
|
+
/**
|
|
158
|
+
* For each external finding, find the inferred node whose sourceSpan contains it.
|
|
159
|
+
* Attaches nodeId to the finding for cross-referencing.
|
|
160
|
+
*/
|
|
161
|
+
export function linkToNodes(findings, inferred) {
|
|
162
|
+
for (const f of findings) {
|
|
163
|
+
if (f.nodeIds && f.nodeIds.length > 0)
|
|
164
|
+
continue; // already linked
|
|
165
|
+
const line = f.primarySpan.startLine;
|
|
166
|
+
const matchingNode = inferred.find(r => r.startLine <= line && r.endLine >= line);
|
|
167
|
+
if (matchingNode) {
|
|
168
|
+
f.nodeIds = [matchingNode.nodeId];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return findings;
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=external-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-tools.js","sourceRoot":"","sources":["../src/external-tools.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @kernlang/review — Scan TS, infer .kern IR, review pipeline, report.
|
|
3
|
+
*
|
|
4
|
+
* Public API:
|
|
5
|
+
* reviewFile(filePath, config?) — review a single file
|
|
6
|
+
* reviewSource(source, filePath?, config?) — review source string
|
|
7
|
+
* reviewDirectory(dirPath, config?) — review all .ts/.tsx files
|
|
8
|
+
*
|
|
9
|
+
* v2: Unified ReviewFinding pipeline. All findings merged into single array.
|
|
10
|
+
*/
|
|
11
|
+
import type { ReviewReport, ReviewConfig } from './types.js';
|
|
12
|
+
export type { ReviewReport, InferResult, TemplateMatch, ReviewFinding, SourceSpan } from './types.js';
|
|
13
|
+
export type { ReviewStats, Confidence, ReviewConfig, EnforceResult, RuleContext, ReviewRule } from './types.js';
|
|
14
|
+
export { createFingerprint } from './types.js';
|
|
15
|
+
export { inferFromSource, inferFromFile } from './inferrer.js';
|
|
16
|
+
export { detectTemplates } from './template-detector.js';
|
|
17
|
+
export { structuralDiff } from './differ.js';
|
|
18
|
+
export { runQualityRules } from './quality-rules.js';
|
|
19
|
+
export { calculateStats, formatReport, formatReportJSON, formatSummary, checkEnforcement, formatEnforcement, dedup } from './reporter.js';
|
|
20
|
+
export { exportKernIR, buildLLMPrompt, parseLLMResponse } from './llm-review.js';
|
|
21
|
+
export { runESLint, runTSCDiagnostics, runTSCDiagnosticsFromPaths, linkToNodes } from './external-tools.js';
|
|
22
|
+
/**
|
|
23
|
+
* Review a single TypeScript file.
|
|
24
|
+
*/
|
|
25
|
+
export declare function reviewFile(filePath: string, config?: ReviewConfig): ReviewReport;
|
|
26
|
+
/**
|
|
27
|
+
* Review TypeScript source code (string).
|
|
28
|
+
*/
|
|
29
|
+
export declare function reviewSource(source: string, filePath?: string, config?: ReviewConfig): ReviewReport;
|
|
30
|
+
/**
|
|
31
|
+
* Review all .ts/.tsx files in a directory.
|
|
32
|
+
*/
|
|
33
|
+
export declare function reviewDirectory(dirPath: string, recursive?: boolean, config?: ReviewConfig): ReviewReport[];
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @kernlang/review — Scan TS, infer .kern IR, review pipeline, report.
|
|
3
|
+
*
|
|
4
|
+
* Public API:
|
|
5
|
+
* reviewFile(filePath, config?) — review a single file
|
|
6
|
+
* reviewSource(source, filePath?, config?) — review source string
|
|
7
|
+
* reviewDirectory(dirPath, config?) — review all .ts/.tsx files
|
|
8
|
+
*
|
|
9
|
+
* v2: Unified ReviewFinding pipeline. All findings merged into single array.
|
|
10
|
+
*/
|
|
11
|
+
import { readFileSync, readdirSync, statSync } from 'fs';
|
|
12
|
+
import { relative, join } from 'path';
|
|
13
|
+
import { inferFromSource, createInMemoryProject } from './inferrer.js';
|
|
14
|
+
import { detectTemplates } from './template-detector.js';
|
|
15
|
+
import { structuralDiff } from './differ.js';
|
|
16
|
+
import { runQualityRules } from './quality-rules.js';
|
|
17
|
+
import { calculateStats, dedup } from './reporter.js';
|
|
18
|
+
export { createFingerprint } from './types.js';
|
|
19
|
+
export { inferFromSource, inferFromFile } from './inferrer.js';
|
|
20
|
+
export { detectTemplates } from './template-detector.js';
|
|
21
|
+
export { structuralDiff } from './differ.js';
|
|
22
|
+
export { runQualityRules } from './quality-rules.js';
|
|
23
|
+
export { calculateStats, formatReport, formatReportJSON, formatSummary, checkEnforcement, formatEnforcement, dedup } from './reporter.js';
|
|
24
|
+
export { exportKernIR, buildLLMPrompt, parseLLMResponse } from './llm-review.js';
|
|
25
|
+
export { runESLint, runTSCDiagnostics, runTSCDiagnosticsFromPaths, linkToNodes } from './external-tools.js';
|
|
26
|
+
/**
|
|
27
|
+
* Review a single TypeScript file.
|
|
28
|
+
*/
|
|
29
|
+
export function reviewFile(filePath, config) {
|
|
30
|
+
const source = readFileSync(filePath, 'utf-8');
|
|
31
|
+
return reviewSource(source, filePath, config);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Review TypeScript source code (string).
|
|
35
|
+
*/
|
|
36
|
+
export function reviewSource(source, filePath = 'input.ts', config) {
|
|
37
|
+
const totalLines = source.split('\n').length;
|
|
38
|
+
// Phase 1+2: Infer KERN constructs (with nodeIds + sourceSpans)
|
|
39
|
+
const inferred = inferFromSource(source, filePath);
|
|
40
|
+
// Phase 3: Template detection (config-aware)
|
|
41
|
+
const project = createInMemoryProject();
|
|
42
|
+
const sourceFile = project.createSourceFile(filePath, source);
|
|
43
|
+
const templateMatches = detectTemplates(sourceFile, config);
|
|
44
|
+
// Phase 4: Structural diff → unified findings
|
|
45
|
+
const diffFindings = structuralDiff(source, inferred, filePath);
|
|
46
|
+
// Phase 5: Quality rules → unified findings
|
|
47
|
+
const qualityFindings = runQualityRules(sourceFile, inferred, templateMatches, config);
|
|
48
|
+
// Merge all findings into single unified array
|
|
49
|
+
const findings = dedup([...diffFindings, ...qualityFindings]);
|
|
50
|
+
// Sort: severity (error > warning > info), then by line
|
|
51
|
+
const severityOrder = { error: 0, warning: 1, info: 2 };
|
|
52
|
+
findings.sort((a, b) => {
|
|
53
|
+
const sd = severityOrder[a.severity] - severityOrder[b.severity];
|
|
54
|
+
if (sd !== 0)
|
|
55
|
+
return sd;
|
|
56
|
+
return a.primarySpan.startLine - b.primarySpan.startLine;
|
|
57
|
+
});
|
|
58
|
+
// Calculate stats
|
|
59
|
+
const stats = calculateStats(inferred, templateMatches, findings, totalLines);
|
|
60
|
+
return {
|
|
61
|
+
filePath,
|
|
62
|
+
inferred,
|
|
63
|
+
templateMatches,
|
|
64
|
+
findings,
|
|
65
|
+
stats,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Review all .ts/.tsx files in a directory.
|
|
70
|
+
*/
|
|
71
|
+
export function reviewDirectory(dirPath, recursive = false, config) {
|
|
72
|
+
const reports = [];
|
|
73
|
+
const files = collectTsFiles(dirPath, recursive);
|
|
74
|
+
for (const file of files) {
|
|
75
|
+
try {
|
|
76
|
+
reports.push(reviewFile(file, config));
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
console.error(` Skipping ${relative(process.cwd(), file)}: ${err.message}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return reports;
|
|
83
|
+
}
|
|
84
|
+
function collectTsFiles(dirPath, recursive) {
|
|
85
|
+
const files = [];
|
|
86
|
+
for (const entry of readdirSync(dirPath)) {
|
|
87
|
+
const full = join(dirPath, entry);
|
|
88
|
+
const stat = statSync(full);
|
|
89
|
+
if (stat.isDirectory() && recursive && !entry.startsWith('.') && entry !== 'node_modules' && entry !== 'dist') {
|
|
90
|
+
files.push(...collectTsFiles(full, true));
|
|
91
|
+
}
|
|
92
|
+
else if ((entry.endsWith('.ts') || entry.endsWith('.tsx')) && !entry.endsWith('.d.ts') && !entry.endsWith('.test.ts') && !entry.endsWith('.test.tsx')) {
|
|
93
|
+
files.push(full);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return files;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TS → .kern Inferrer — scans TypeScript AST and infers KERN IR nodes.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1 (deterministic): type, interface, fn, error, import, const
|
|
5
|
+
* Phase 2 (composite): machine, config, event, module
|
|
6
|
+
*
|
|
7
|
+
* v2: Adds stable nodeId, promptAlias, sourceSpans to InferResult.
|
|
8
|
+
*/
|
|
9
|
+
import { Project, SourceFile } from 'ts-morph';
|
|
10
|
+
import type { InferResult } from './types.js';
|
|
11
|
+
export declare function createProject(): Project;
|
|
12
|
+
export declare function createInMemoryProject(): Project;
|
|
13
|
+
export declare function inferFromSource(source: string, filePath?: string): InferResult[];
|
|
14
|
+
export declare function inferFromFile(filePath: string): InferResult[];
|
|
15
|
+
export declare function inferFromSourceFile(sourceFile: SourceFile): InferResult[];
|