@kb-labs/review-contracts 0.5.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/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # @kb-labs/review-contracts
2
+
3
+ Type definitions and contracts for AI Review plugin.
4
+
5
+ ## Overview
6
+
7
+ This package contains all TypeScript interfaces, types, and contracts used across the AI Review plugin packages.
8
+
9
+ ## Key Types
10
+
11
+ ### Core Types
12
+
13
+ - **`ReviewFinding`** - Represents a single code review finding
14
+ - **`ReviewRule`** - Rule definition (heuristic or LLM)
15
+ - **`ReviewPreset`** - Configuration preset (set of rules)
16
+ - **`ReviewResult`** - Complete review result with findings and metadata
17
+
18
+ ### Engine Types
19
+
20
+ - **`EngineType`** - Engine categories (compiler, linter, sast, ast, llm)
21
+ - **`HeuristicEngine`** - Engine registry entry mapping tools to types
22
+ - **`FindingSeverity`** - Severity levels (blocker, high, medium, low, info)
23
+ - **`FindingConfidence`** - Confidence levels (certain, likely, heuristic)
24
+
25
+ ### Review Flow
26
+
27
+ - **`ReviewRequest`** - Input for review operation
28
+ - **`ReviewContext`** - Context for LLM analyzers
29
+ - **`ParsedFile`** - File representation with content hash
30
+
31
+ ## Usage
32
+
33
+ ```typescript
34
+ import type {
35
+ ReviewFinding,
36
+ ReviewRule,
37
+ ReviewPreset,
38
+ ReviewResult,
39
+ } from '@kb-labs/review-contracts';
40
+ ```
41
+
42
+ ## Design Principles
43
+
44
+ 1. **Unified Contract** - All engines use same interfaces
45
+ 2. **Engine Type Priority** - Deduplication by engine type, not specific tool
46
+ 3. **Content-Hash Based** - Deterministic caching keys
47
+ 4. **Agent Gating** - Confidence + fix + scope fields for agent filtering
@@ -0,0 +1,471 @@
1
+ /**
2
+ * @module @kb-labs/review-contracts/types
3
+ * Core type definitions for AI Review plugin
4
+ */
5
+ /**
6
+ * Engine types (not specific tools!)
7
+ * Used for deduplication priority
8
+ */
9
+ type EngineType = 'compiler' | 'linter' | 'sast' | 'ast' | 'llm';
10
+ /**
11
+ * Engine registry entry
12
+ * Maps concrete tools to engine types
13
+ */
14
+ interface HeuristicEngine {
15
+ id: string;
16
+ name: string;
17
+ language: string[];
18
+ type: EngineType;
19
+ }
20
+ /**
21
+ * Rule categories
22
+ */
23
+ type RuleCategory = 'style' | 'correctness' | 'security' | 'architecture' | 'maintainability';
24
+ /**
25
+ * Finding severity levels
26
+ */
27
+ type FindingSeverity = 'blocker' | 'high' | 'medium' | 'low' | 'info';
28
+ /**
29
+ * Confidence levels for findings
30
+ * Critical for agent gating!
31
+ */
32
+ type FindingConfidence = 'certain' | 'likely' | 'heuristic';
33
+ /**
34
+ * Review modes
35
+ */
36
+ type ReviewMode = 'heuristic' | 'llm' | 'full';
37
+ /**
38
+ * Unified rule contract
39
+ * All rules (heuristic/LLM) follow same contract
40
+ */
41
+ interface ReviewRule {
42
+ id: string;
43
+ title: string;
44
+ category: RuleCategory;
45
+ severity: FindingSeverity;
46
+ engine: string;
47
+ confidence: FindingConfidence;
48
+ rationale?: string;
49
+ references?: string[];
50
+ quickFix?: FixTemplate[];
51
+ }
52
+ /**
53
+ * Fix template for auto-fixing
54
+ */
55
+ interface FixTemplate {
56
+ type: 'replace' | 'insert' | 'delete';
57
+ pattern?: string;
58
+ replacement?: string;
59
+ position?: {
60
+ line: number;
61
+ column: number;
62
+ };
63
+ }
64
+ /**
65
+ * Review finding
66
+ */
67
+ interface ReviewFinding {
68
+ id: string;
69
+ ruleId: string;
70
+ type: string;
71
+ severity: FindingSeverity;
72
+ confidence: FindingConfidence;
73
+ file: string;
74
+ line: number;
75
+ column?: number;
76
+ endLine?: number;
77
+ message: string;
78
+ snippet?: string;
79
+ suggestion?: string;
80
+ rationale?: string;
81
+ engine: string;
82
+ source: string;
83
+ fix?: FixTemplate[];
84
+ scope?: 'local' | 'global';
85
+ automated?: boolean;
86
+ }
87
+ /**
88
+ * Finding fingerprint for deduplication
89
+ */
90
+ interface FindingFingerprint {
91
+ key: string;
92
+ bucket: {
93
+ file: string;
94
+ lineStart: number;
95
+ lineEnd: number;
96
+ };
97
+ snippetHash: string;
98
+ }
99
+ /**
100
+ * Parsed file for analysis
101
+ */
102
+ interface ParsedFile {
103
+ path: string;
104
+ content: string;
105
+ contentHash: string;
106
+ language: string;
107
+ ast?: unknown;
108
+ }
109
+ /**
110
+ * Review context for LLM analyzers
111
+ */
112
+ interface ReviewContext {
113
+ preset: string;
114
+ file: string;
115
+ taskContext?: string;
116
+ repoScope?: string[];
117
+ documents: Document[];
118
+ examples: Example[];
119
+ relatedADRs: ADR[];
120
+ conventions: Record<string, string>;
121
+ }
122
+ /**
123
+ * Static document (from preset config)
124
+ */
125
+ interface Document {
126
+ id: string;
127
+ title: string;
128
+ content: string;
129
+ type: 'rule' | 'convention' | 'guide';
130
+ }
131
+ /**
132
+ * Example from codebase (via Mind RAG)
133
+ */
134
+ interface Example {
135
+ file: string;
136
+ snippet: string;
137
+ description: string;
138
+ }
139
+ /**
140
+ * Architecture Decision Record
141
+ */
142
+ interface ADR {
143
+ id: string;
144
+ title: string;
145
+ path: string;
146
+ summary: string;
147
+ }
148
+ /**
149
+ * LLM Analyzer interface
150
+ */
151
+ interface LLMAnalyzer {
152
+ readonly id: string;
153
+ readonly name: string;
154
+ analyze(files: ParsedFile[], context: ReviewContext): Promise<ReviewFinding[]>;
155
+ }
156
+ /**
157
+ * Input file (from CLI layer)
158
+ * Simple file representation before orchestrator parsing
159
+ */
160
+ interface InputFile {
161
+ path: string;
162
+ content: string;
163
+ }
164
+ /**
165
+ * Review request
166
+ */
167
+ interface ReviewRequest {
168
+ files: InputFile[];
169
+ mode: ReviewMode;
170
+ presetId: string;
171
+ cwd?: string;
172
+ taskContext?: string;
173
+ repoScope?: string[];
174
+ config?: {
175
+ eslintConfig?: string;
176
+ ruffConfig?: string;
177
+ golangciConfig?: string;
178
+ clippyConfig?: string;
179
+ };
180
+ }
181
+ /**
182
+ * Review preset configuration
183
+ */
184
+ interface ReviewPreset {
185
+ id: string;
186
+ name: string;
187
+ description?: string;
188
+ rules: string[];
189
+ excludeRules?: string[];
190
+ llm?: {
191
+ enabled: boolean;
192
+ analyzers: string[];
193
+ };
194
+ severity?: {
195
+ failOn?: FindingSeverity;
196
+ };
197
+ documents?: Document[];
198
+ }
199
+ /**
200
+ * Engine configuration within a preset
201
+ */
202
+ interface PresetEngineConfig {
203
+ enabled: boolean;
204
+ rules?: string[];
205
+ config?: Record<string, unknown>;
206
+ }
207
+ /**
208
+ * LLM analyzer context - passed to LLM analyzers for guidance
209
+ */
210
+ interface LLMAnalyzerContext {
211
+ projectType?: string;
212
+ framework?: string;
213
+ language?: string;
214
+ conventions?: Record<string, string>;
215
+ adrs?: string[];
216
+ }
217
+ /**
218
+ * Detailed preset configuration with engine-specific settings
219
+ */
220
+ interface PresetDefinition extends ReviewPreset {
221
+ extends?: string;
222
+ engines?: {
223
+ eslint?: PresetEngineConfig;
224
+ ruff?: PresetEngineConfig;
225
+ golangci?: PresetEngineConfig;
226
+ clippy?: PresetEngineConfig;
227
+ };
228
+ include?: string[];
229
+ exclude?: string[];
230
+ maxConcurrent?: number;
231
+ timeout?: number;
232
+ context?: LLMAnalyzerContext;
233
+ atomicRules?: Record<string, {
234
+ include?: string[];
235
+ exclude?: string[];
236
+ }>;
237
+ }
238
+ /**
239
+ * Review result
240
+ */
241
+ interface ReviewResult {
242
+ findings: ReviewFinding[];
243
+ summary: ReviewSummary;
244
+ metadata: ReviewMetadata;
245
+ }
246
+ /**
247
+ * Review summary statistics
248
+ */
249
+ interface ReviewSummary {
250
+ total: number;
251
+ bySeverity: {
252
+ error: number;
253
+ warning: number;
254
+ info: number;
255
+ };
256
+ byType: Record<string, number>;
257
+ }
258
+ /**
259
+ * Review metadata
260
+ */
261
+ interface ReviewMetadata {
262
+ preset: string;
263
+ mode: ReviewMode;
264
+ filesReviewed: number;
265
+ analyzedFiles: number;
266
+ heuristicFindings: number;
267
+ llmFindings: number;
268
+ totalFindings: number;
269
+ durationMs: number;
270
+ engines: string[];
271
+ llmLite?: LLMLiteMetadata;
272
+ incremental?: IncrementalMetadata;
273
+ }
274
+ /**
275
+ * Incremental review metadata
276
+ * Tracks cached files and new vs known findings
277
+ */
278
+ interface IncrementalMetadata {
279
+ /** Files skipped (unchanged, used cache) */
280
+ cachedFiles: number;
281
+ /** Files analyzed fresh */
282
+ analyzedFiles: number;
283
+ /** New findings (not seen before) */
284
+ newFindings: number;
285
+ /** Known findings (seen in previous review) */
286
+ knownFindings: number;
287
+ /** Findings from cached files */
288
+ cachedFindings: number;
289
+ }
290
+ /**
291
+ * LLM-Lite analysis metadata (v2)
292
+ * Detailed stats for batch tool-based review
293
+ */
294
+ interface LLMLiteMetadata {
295
+ /** Number of LLM API calls */
296
+ llmCalls: number;
297
+ /** Tool call counts */
298
+ toolCalls: {
299
+ get_diffs: number;
300
+ get_file_chunks: number;
301
+ report_findings: number;
302
+ };
303
+ /** Token usage */
304
+ tokens: {
305
+ input: number;
306
+ output: number;
307
+ total: number;
308
+ };
309
+ /** Estimated cost in USD */
310
+ estimatedCost: number;
311
+ /** Verification stats */
312
+ verification: {
313
+ /** Raw findings before verification */
314
+ rawFindings: number;
315
+ /** Findings that passed verification */
316
+ verified: number;
317
+ /** Findings downgraded due to uncertainty */
318
+ downgraded: number;
319
+ /** Findings discarded as hallucinations */
320
+ discarded: number;
321
+ /** Hallucination rate (0.0-1.0) */
322
+ hallucinationRate: number;
323
+ };
324
+ /** Timing breakdown */
325
+ timing: {
326
+ /** Total analysis time (ms) */
327
+ totalMs: number;
328
+ /** Time spent on LLM calls (ms) */
329
+ llmMs: number;
330
+ /** Time spent on verification (ms) */
331
+ verifyMs: number;
332
+ };
333
+ }
334
+ /**
335
+ * Review configuration in kb.config.json
336
+ */
337
+ interface ReviewConfig {
338
+ defaultPreset?: string;
339
+ presetsDir?: string;
340
+ presets?: Array<PresetDefinition | string>;
341
+ engines?: {
342
+ eslint?: {
343
+ configPath?: string;
344
+ enabled?: boolean;
345
+ };
346
+ ruff?: {
347
+ configPath?: string;
348
+ enabled?: boolean;
349
+ };
350
+ golangci?: {
351
+ configPath?: string;
352
+ enabled?: boolean;
353
+ };
354
+ clippy?: {
355
+ enabled?: boolean;
356
+ };
357
+ };
358
+ include?: string[];
359
+ exclude?: string[];
360
+ analyzersDir?: string;
361
+ rulesDir?: string;
362
+ promptsDir?: string;
363
+ llm?: {
364
+ minTurns?: number;
365
+ maxTurns?: number;
366
+ filesPerTurn?: number;
367
+ linesPerTurn?: number;
368
+ };
369
+ }
370
+ /**
371
+ * Base LLM analyzer class
372
+ * Extend this to create custom analyzers
373
+ */
374
+ declare abstract class BaseLLMAnalyzer implements LLMAnalyzer {
375
+ abstract readonly id: string;
376
+ abstract readonly name: string;
377
+ abstract analyze(files: ParsedFile[], context: ReviewContext): Promise<ReviewFinding[]>;
378
+ /**
379
+ * Generate cache key for file + preset combination
380
+ */
381
+ protected generateCacheKey(file: ParsedFile, preset: string): string;
382
+ /**
383
+ * Build finding ID
384
+ */
385
+ protected buildFindingId(file: ParsedFile, line: number, type: string): string;
386
+ }
387
+ /**
388
+ * Parsed diff hunk with line information
389
+ */
390
+ interface DiffHunk {
391
+ /** Starting line in new file (1-indexed) */
392
+ newStart: number;
393
+ /** Number of lines in new file */
394
+ newLines: number;
395
+ /** Starting line in old file (1-indexed) */
396
+ oldStart: number;
397
+ /** Number of lines in old file */
398
+ oldLines: number;
399
+ /** Raw hunk content (unified diff format) */
400
+ content: string;
401
+ /** Lines that were added */
402
+ addedLines: number[];
403
+ /** Lines that were deleted (relative to old file) */
404
+ deletedLines: number[];
405
+ }
406
+ /**
407
+ * File diff with parsed hunks
408
+ */
409
+ interface FileDiff {
410
+ /** File path (relative to repo root) */
411
+ file: string;
412
+ /** Raw unified diff */
413
+ diff: string;
414
+ /** Number of lines added */
415
+ additions: number;
416
+ /** Number of lines deleted */
417
+ deletions: number;
418
+ /** Whether this is a new file */
419
+ isNewFile: boolean;
420
+ /** Whether this is a deleted file */
421
+ isDeleted: boolean;
422
+ /** Whether this is a renamed file */
423
+ isRenamed: boolean;
424
+ /** Parsed hunks */
425
+ hunks: DiffHunk[];
426
+ /** Set of changed line numbers (in new file) */
427
+ changedLines: Set<number>;
428
+ }
429
+ /**
430
+ * Batch diff request
431
+ */
432
+ interface BatchDiffRequest {
433
+ /** Directory containing .git */
434
+ cwd: string;
435
+ /** Files to get diffs for */
436
+ files: string[];
437
+ /** Include staged changes */
438
+ staged?: boolean;
439
+ /** Include unstaged changes */
440
+ unstaged?: boolean;
441
+ /** Max lines per file diff (truncate large diffs) */
442
+ maxLinesPerFile?: number;
443
+ }
444
+ /**
445
+ * Batch diff result
446
+ */
447
+ interface BatchDiffResult {
448
+ /** Successfully fetched diffs */
449
+ diffs: FileDiff[];
450
+ /** Files that failed to fetch */
451
+ errors: Array<{
452
+ file: string;
453
+ error: string;
454
+ }>;
455
+ /** Total lines in all diffs */
456
+ totalLines: number;
457
+ }
458
+ /**
459
+ * DiffProvider interface (implementation in review-core)
460
+ */
461
+ interface IDiffProvider {
462
+ getDiffs(request: BatchDiffRequest): Promise<BatchDiffResult>;
463
+ getFileDiff(file: string, staged?: boolean, unstaged?: boolean, maxLines?: number): Promise<FileDiff | null>;
464
+ isLineInDiff(fileDiff: FileDiff, lineNumber: number): boolean;
465
+ getLineContext(file: string, lineNumber: number, contextLines?: number): Promise<{
466
+ lines: string[];
467
+ startLine: number;
468
+ } | null>;
469
+ }
470
+
471
+ export { type ADR, BaseLLMAnalyzer, type BatchDiffRequest, type BatchDiffResult, type DiffHunk, type Document, type EngineType, type Example, type FileDiff, type FindingConfidence, type FindingFingerprint, type FindingSeverity, type FixTemplate, type HeuristicEngine, type IDiffProvider, type IncrementalMetadata, type InputFile, type LLMAnalyzer, type LLMAnalyzerContext, type LLMLiteMetadata, type ParsedFile, type PresetDefinition, type PresetEngineConfig, type ReviewConfig, type ReviewContext, type ReviewFinding, type ReviewMetadata, type ReviewMode, type ReviewPreset, type ReviewRequest, type ReviewResult, type ReviewRule, type ReviewSummary, type RuleCategory };
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ // src/types.ts
2
+ var BaseLLMAnalyzer = class {
3
+ /**
4
+ * Generate cache key for file + preset combination
5
+ */
6
+ generateCacheKey(file, preset) {
7
+ return `review:${this.id}:${file.contentHash}:${preset}`;
8
+ }
9
+ /**
10
+ * Build finding ID
11
+ */
12
+ buildFindingId(file, line, type) {
13
+ return `${this.id}-${file.path}-${line}-${type}`;
14
+ }
15
+ };
16
+
17
+ export { BaseLLMAnalyzer };
18
+ //# sourceMappingURL=index.js.map
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"names":[],"mappings":";AA0fO,IAAe,kBAAf,MAAsD;AAAA;AAAA;AAAA;AAAA,EASjD,gBAAA,CAAiB,MAAkB,MAAA,EAAwB;AACnE,IAAA,OAAO,UAAU,IAAA,CAAK,EAAE,IAAI,IAAA,CAAK,WAAW,IAAI,MAAM,CAAA,CAAA;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKU,cAAA,CAAe,IAAA,EAAkB,IAAA,EAAc,IAAA,EAAsB;AAC7E,IAAA,OAAO,CAAA,EAAG,KAAK,EAAE,CAAA,CAAA,EAAI,KAAK,IAAI,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA;AAAA,EAChD;AACF","file":"index.js","sourcesContent":["/**\n * @module @kb-labs/review-contracts/types\n * Core type definitions for AI Review plugin\n */\n\n/**\n * Engine types (not specific tools!)\n * Used for deduplication priority\n */\nexport type EngineType =\n | 'compiler' // TypeScript compiler, rustc, go build\n | 'linter' // ESLint, Ruff, golangci-lint, Clippy, RuboCop\n | 'sast' // Semgrep, CodeQL, Bandit\n | 'ast' // tree-sitter (read-only AST analysis)\n | 'llm'; // LLM-based analysis\n\n/**\n * Engine registry entry\n * Maps concrete tools to engine types\n */\nexport interface HeuristicEngine {\n id: string; // 'eslint', 'ruff', 'golangci', 'clippy'\n name: string;\n language: string[]; // ['typescript', 'javascript']\n type: EngineType; // Maps to priority tier\n}\n\n/**\n * Rule categories\n */\nexport type RuleCategory =\n | 'style'\n | 'correctness'\n | 'security'\n | 'architecture'\n | 'maintainability';\n\n/**\n * Finding severity levels\n */\nexport type FindingSeverity =\n | 'blocker'\n | 'high'\n | 'medium'\n | 'low'\n | 'info';\n\n/**\n * Confidence levels for findings\n * Critical for agent gating!\n */\nexport type FindingConfidence =\n | 'certain' // Deterministic, can be auto-fixed\n | 'likely' // High confidence but needs human review\n | 'heuristic'; // Pattern-based, might have false positives\n\n/**\n * Review modes\n */\nexport type ReviewMode =\n | 'heuristic' // Fast, deterministic (default in CI)\n | 'llm' // LLM-only (for complex analysis)\n | 'full'; // Heuristic + LLM (local development)\n\n/**\n * Unified rule contract\n * All rules (heuristic/LLM) follow same contract\n */\nexport interface ReviewRule {\n id: string; // Stable ID (e.g., \"eslint:no-unused-vars\")\n title: string;\n category: RuleCategory;\n severity: FindingSeverity;\n engine: string; // Engine ID (e.g., 'eslint', 'ruff')\n confidence: FindingConfidence;\n\n rationale?: string; // Why this rule exists\n references?: string[]; // ADR/doc links\n quickFix?: FixTemplate[]; // Concrete fix (if available)\n}\n\n/**\n * Fix template for auto-fixing\n */\nexport interface FixTemplate {\n type: 'replace' | 'insert' | 'delete';\n pattern?: string; // Regex pattern to match\n replacement?: string; // Replacement text\n position?: {\n line: number;\n column: number;\n };\n}\n\n/**\n * Review finding\n */\nexport interface ReviewFinding {\n id: string; // Unique finding ID\n ruleId: string; // Rule that generated this finding\n type: string; // Finding type (e.g., 'security', 'style')\n severity: FindingSeverity;\n confidence: FindingConfidence;\n \n file: string; // File path\n line: number; // Line number\n column?: number; // Column number (optional)\n endLine?: number; // End line for multi-line findings\n \n message: string; // Human-readable message\n snippet?: string; // Code snippet showing the issue\n suggestion?: string; // Suggested fix\n rationale?: string; // Why this is an issue\n \n engine: string; // Which engine found this\n source: string; // Source identifier (e.g., 'eslint', 'llm-architecture')\n \n fix?: FixTemplate[]; // Auto-fix instructions\n \n // For agent mode gating\n scope?: 'local' | 'global'; // 1-2 files (local) vs architecture redesign (global)\n automated?: boolean; // Can be auto-applied?\n}\n\n/**\n * Finding fingerprint for deduplication\n */\nexport interface FindingFingerprint {\n key: string; // sha1(ruleId|file|bucket|snippetHash)\n bucket: {\n file: string;\n lineStart: number; // line - 2\n lineEnd: number; // line + 2\n };\n snippetHash: string; // normalizeWhitespace(snippet).slice(0, 100) + hash\n}\n\n/**\n * Parsed file for analysis\n */\nexport interface ParsedFile {\n path: string;\n content: string;\n contentHash: string; // For deterministic caching\n language: string; // Detected language\n ast?: unknown; // Optional AST (if available)\n}\n\n/**\n * Review context for LLM analyzers\n */\nexport interface ReviewContext {\n preset: string; // Preset ID\n file: string; // Current file being analyzed\n\n // TASK CONTEXT: What is being reviewed and why\n taskContext?: string; // Description of the task (e.g., \"Add multi-tenancy support\")\n repoScope?: string[]; // Which repos are part of this task\n\n // PRIMARY: Static documents from preset (always available)\n documents: Document[];\n\n // OPTIONAL: Dynamic examples via Mind RAG (when available)\n examples: Example[];\n\n // OPTIONAL: Related ADRs (when Mind RAG available)\n relatedADRs: ADR[];\n\n // PRIMARY: Project conventions from atomic rules\n // Key = category name (e.g., 'naming', 'architecture', 'consistency')\n // Value = composed markdown content from all rules in category\n conventions: Record<string, string>;\n}\n\n/**\n * Static document (from preset config)\n */\nexport interface Document {\n id: string;\n title: string;\n content: string;\n type: 'rule' | 'convention' | 'guide';\n}\n\n/**\n * Example from codebase (via Mind RAG)\n */\nexport interface Example {\n file: string;\n snippet: string;\n description: string;\n}\n\n/**\n * Architecture Decision Record\n */\nexport interface ADR {\n id: string;\n title: string;\n path: string;\n summary: string;\n}\n\n\n/**\n * LLM Analyzer interface\n */\nexport interface LLMAnalyzer {\n readonly id: string;\n readonly name: string;\n\n analyze(files: ParsedFile[], context: ReviewContext): Promise<ReviewFinding[]>;\n}\n\n/**\n * Input file (from CLI layer)\n * Simple file representation before orchestrator parsing\n */\nexport interface InputFile {\n path: string;\n content: string;\n}\n\n/**\n * Review request\n */\nexport interface ReviewRequest {\n files: InputFile[]; // Simple files - orchestrator will parse to ParsedFile\n mode: ReviewMode;\n presetId: string;\n\n // Additional context\n cwd?: string; // Working directory\n\n // Task context for LLM (what are we trying to achieve?)\n taskContext?: string; // Description of the task being reviewed\n\n // Scope for diff-based review (submodule names)\n // When provided, system collects git diff from these repos\n repoScope?: string[]; // ['kb-labs-core', 'kb-labs-cli']\n\n config?: { // Engine-specific configuration\n eslintConfig?: string; // Path to ESLint config\n ruffConfig?: string; // Path to Ruff config (future)\n golangciConfig?: string; // Path to golangci-lint config (future)\n clippyConfig?: string; // Path to Clippy config (future)\n };\n}\n\n/**\n * Review preset configuration\n */\nexport interface ReviewPreset {\n id: string;\n name: string;\n description?: string;\n\n rules: string[]; // Rule IDs to enable\n excludeRules?: string[]; // Rule IDs to disable\n\n llm?: {\n enabled: boolean; // Enable LLM analysis?\n analyzers: string[]; // Which LLM analyzers to run\n };\n\n severity?: {\n failOn?: FindingSeverity; // Exit with error if severity >= this\n };\n\n // LLM context (for LLM analyzers)\n documents?: Document[]; // Static documents (guides, rules)\n}\n\n/**\n * Engine configuration within a preset\n */\nexport interface PresetEngineConfig {\n enabled: boolean;\n rules?: string[]; // Override which rules to enable\n config?: Record<string, unknown>; // Engine-specific config\n}\n\n/**\n * LLM analyzer context - passed to LLM analyzers for guidance\n */\nexport interface LLMAnalyzerContext {\n projectType?: string; // 'monorepo' | 'library' | 'application'\n framework?: string; // 'nodejs' | 'react' | 'next.js'\n language?: string; // 'typescript' | 'javascript' | 'python'\n\n // Dynamic conventions - any category name is valid\n // Categories map to .kb/ai-review/rules/{category}/ directories\n // Common categories: naming, architecture, security, testing, performance, errorHandling, consistency\n conventions?: Record<string, string>;\n\n adrs?: string[]; // ADR references for context\n}\n\n/**\n * Detailed preset configuration with engine-specific settings\n */\nexport interface PresetDefinition extends ReviewPreset {\n // Preset inheritance\n extends?: string; // Inherit from another preset (e.g., 'kb-labs', 'default')\n\n // Engine-specific configuration\n engines?: {\n eslint?: PresetEngineConfig;\n ruff?: PresetEngineConfig;\n golangci?: PresetEngineConfig;\n clippy?: PresetEngineConfig;\n };\n\n // File patterns\n include?: string[]; // Patterns to include\n exclude?: string[]; // Patterns to exclude\n\n // Performance tuning\n maxConcurrent?: number; // Max concurrent engine runs\n timeout?: number; // Timeout per file (ms)\n\n // LLM Analyzer context (guides LLM analyzers)\n context?: LLMAnalyzerContext;\n\n // Atomic rules composition (ESLint-style)\n // Dynamic categories - any category name is valid\n // Categories are defined by directory structure in .kb/ai-review/rules/{category}/\n atomicRules?: Record<string, {\n include?: string[]; // Include specific rules (e.g., ['pyramid-rule', 'typescript-naming'])\n exclude?: string[]; // Exclude specific rules\n }>;\n}\n\n/**\n * Review result\n */\nexport interface ReviewResult {\n findings: ReviewFinding[];\n summary: ReviewSummary;\n metadata: ReviewMetadata;\n}\n\n/**\n * Review summary statistics\n */\nexport interface ReviewSummary {\n total: number;\n bySeverity: {\n error: number;\n warning: number;\n info: number;\n };\n byType: Record<string, number>;\n}\n\n/**\n * Review metadata\n */\nexport interface ReviewMetadata {\n preset: string;\n mode: ReviewMode;\n filesReviewed: number;\n analyzedFiles: number; // Alias for filesReviewed (for backward compat)\n heuristicFindings: number;\n llmFindings: number;\n totalFindings: number;\n durationMs: number;\n engines: string[]; // List of engines used (e.g., ['eslint', 'ruff'])\n\n // LLM-Lite specific metadata (v2)\n llmLite?: LLMLiteMetadata;\n\n // Incremental review metadata\n incremental?: IncrementalMetadata;\n}\n\n/**\n * Incremental review metadata\n * Tracks cached files and new vs known findings\n */\nexport interface IncrementalMetadata {\n /** Files skipped (unchanged, used cache) */\n cachedFiles: number;\n /** Files analyzed fresh */\n analyzedFiles: number;\n /** New findings (not seen before) */\n newFindings: number;\n /** Known findings (seen in previous review) */\n knownFindings: number;\n /** Findings from cached files */\n cachedFindings: number;\n}\n\n/**\n * LLM-Lite analysis metadata (v2)\n * Detailed stats for batch tool-based review\n */\nexport interface LLMLiteMetadata {\n /** Number of LLM API calls */\n llmCalls: number;\n\n /** Tool call counts */\n toolCalls: {\n get_diffs: number;\n get_file_chunks: number;\n report_findings: number;\n };\n\n /** Token usage */\n tokens: {\n input: number;\n output: number;\n total: number;\n };\n\n /** Estimated cost in USD */\n estimatedCost: number;\n\n /** Verification stats */\n verification: {\n /** Raw findings before verification */\n rawFindings: number;\n /** Findings that passed verification */\n verified: number;\n /** Findings downgraded due to uncertainty */\n downgraded: number;\n /** Findings discarded as hallucinations */\n discarded: number;\n /** Hallucination rate (0.0-1.0) */\n hallucinationRate: number;\n };\n\n /** Timing breakdown */\n timing: {\n /** Total analysis time (ms) */\n totalMs: number;\n /** Time spent on LLM calls (ms) */\n llmMs: number;\n /** Time spent on verification (ms) */\n verifyMs: number;\n };\n}\n\n\n/**\n * Review configuration in kb.config.json\n */\nexport interface ReviewConfig {\n // Default preset to use\n defaultPreset?: string;\n\n // Presets directory (relative to .kb/)\n presetsDir?: string;\n\n // Custom presets (inline definitions or file paths)\n presets?: Array<PresetDefinition | string>;\n\n // Engine configurations\n engines?: {\n eslint?: {\n configPath?: string; // Path to ESLint config\n enabled?: boolean;\n };\n ruff?: {\n configPath?: string;\n enabled?: boolean;\n };\n golangci?: {\n configPath?: string;\n enabled?: boolean;\n };\n clippy?: {\n enabled?: boolean;\n };\n };\n\n // File patterns to include/exclude globally\n include?: string[];\n exclude?: string[];\n\n // Custom analyzers directory (default: .kb/review/analyzers)\n analyzersDir?: string;\n\n // Rules directory (relative to .kb/, default: ai-review/rules)\n rulesDir?: string;\n\n // Prompts directory (relative to .kb/, default: ai-review/prompts)\n promptsDir?: string;\n\n // LLM configuration for llm-lite mode\n llm?: {\n // Minimum turns for LLM conversation (default: 3)\n minTurns?: number;\n // Maximum turns for LLM conversation (default: 25)\n maxTurns?: number;\n // Files per turn for adaptive calculation (default: 10)\n filesPerTurn?: number;\n // Lines per turn for adaptive calculation (default: 500)\n linesPerTurn?: number;\n };\n}\n\n/**\n * Base LLM analyzer class\n * Extend this to create custom analyzers\n */\nexport abstract class BaseLLMAnalyzer implements LLMAnalyzer {\n abstract readonly id: string;\n abstract readonly name: string;\n\n abstract analyze(files: ParsedFile[], context: ReviewContext): Promise<ReviewFinding[]>;\n\n /**\n * Generate cache key for file + preset combination\n */\n protected generateCacheKey(file: ParsedFile, preset: string): string {\n return `review:${this.id}:${file.contentHash}:${preset}`;\n }\n\n /**\n * Build finding ID\n */\n protected buildFindingId(file: ParsedFile, line: number, type: string): string {\n return `${this.id}-${file.path}-${line}-${type}`;\n }\n}\n\n// =============================================================================\n// Diff Provider Types (for LLM-Lite mode)\n// =============================================================================\n\n/**\n * Parsed diff hunk with line information\n */\nexport interface DiffHunk {\n /** Starting line in new file (1-indexed) */\n newStart: number;\n /** Number of lines in new file */\n newLines: number;\n /** Starting line in old file (1-indexed) */\n oldStart: number;\n /** Number of lines in old file */\n oldLines: number;\n /** Raw hunk content (unified diff format) */\n content: string;\n /** Lines that were added */\n addedLines: number[];\n /** Lines that were deleted (relative to old file) */\n deletedLines: number[];\n}\n\n/**\n * File diff with parsed hunks\n */\nexport interface FileDiff {\n /** File path (relative to repo root) */\n file: string;\n /** Raw unified diff */\n diff: string;\n /** Number of lines added */\n additions: number;\n /** Number of lines deleted */\n deletions: number;\n /** Whether this is a new file */\n isNewFile: boolean;\n /** Whether this is a deleted file */\n isDeleted: boolean;\n /** Whether this is a renamed file */\n isRenamed: boolean;\n /** Parsed hunks */\n hunks: DiffHunk[];\n /** Set of changed line numbers (in new file) */\n changedLines: Set<number>;\n}\n\n/**\n * Batch diff request\n */\nexport interface BatchDiffRequest {\n /** Directory containing .git */\n cwd: string;\n /** Files to get diffs for */\n files: string[];\n /** Include staged changes */\n staged?: boolean;\n /** Include unstaged changes */\n unstaged?: boolean;\n /** Max lines per file diff (truncate large diffs) */\n maxLinesPerFile?: number;\n}\n\n/**\n * Batch diff result\n */\nexport interface BatchDiffResult {\n /** Successfully fetched diffs */\n diffs: FileDiff[];\n /** Files that failed to fetch */\n errors: Array<{ file: string; error: string }>;\n /** Total lines in all diffs */\n totalLines: number;\n}\n\n/**\n * DiffProvider interface (implementation in review-core)\n */\nexport interface IDiffProvider {\n getDiffs(request: BatchDiffRequest): Promise<BatchDiffResult>;\n getFileDiff(file: string, staged?: boolean, unstaged?: boolean, maxLines?: number): Promise<FileDiff | null>;\n isLineInDiff(fileDiff: FileDiff, lineNumber: number): boolean;\n getLineContext(file: string, lineNumber: number, contextLines?: number): Promise<{ lines: string[]; startLine: number } | null>;\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@kb-labs/review-contracts",
3
+ "version": "0.5.0",
4
+ "type": "module",
5
+ "description": "Type definitions and contracts for AI Review plugin",
6
+ "exports": {
7
+ ".": {
8
+ "import": "./dist/index.js",
9
+ "types": "./dist/index.d.ts"
10
+ }
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "clean": "rimraf dist",
18
+ "dev": "tsup --watch",
19
+ "lint": "eslint src --ext .ts",
20
+ "lint:fix": "eslint . --fix",
21
+ "type-check": "tsc --noEmit",
22
+ "test": "vitest run --passWithNoTests",
23
+ "test:watch": "vitest"
24
+ },
25
+ "dependencies": {},
26
+ "devDependencies": {
27
+ "@kb-labs/devkit": "link:../../../../infra/kb-labs-devkit",
28
+ "tsup": "^8.5.0",
29
+ "typescript": "^5.6.3",
30
+ "rimraf": "^6.0.1",
31
+ "@types/node": "^24.3.3",
32
+ "vitest": "^3.2.4"
33
+ },
34
+ "engines": {
35
+ "node": ">=20.0.0",
36
+ "pnpm": ">=9.0.0"
37
+ }
38
+ }