@kb-labs/review-core 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 +268 -0
- package/dist/index.d.ts +412 -0
- package/dist/index.js +1542 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# @kb-labs/review-core
|
|
2
|
+
|
|
3
|
+
Core orchestration logic for AI Review plugin.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This package contains the main orchestrator that coordinates:
|
|
8
|
+
- Heuristic analysis engines (ESLint, Ruff, etc.)
|
|
9
|
+
- LLM-based analyzers (naming, architecture, logic bugs)
|
|
10
|
+
- Caching via State Broker
|
|
11
|
+
- Deduplication with engine type priority
|
|
12
|
+
- Analytics tracking
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { runReview } from '@kb-labs/review-core';
|
|
18
|
+
|
|
19
|
+
const result = await runReview({
|
|
20
|
+
mode: 'heuristic', // 'heuristic' | 'full' | 'llm'
|
|
21
|
+
scope: 'changed', // 'all' | 'changed' | 'staged'
|
|
22
|
+
cwd: '/path/to/project',
|
|
23
|
+
files: ['src/**/*.ts'],
|
|
24
|
+
presetId: 'typescript-strict',
|
|
25
|
+
config: {
|
|
26
|
+
eslintConfig: '.eslintrc.json',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
console.log(`Found ${result.findings.length} issues`);
|
|
31
|
+
console.log(`Analyzed ${result.metadata.analyzedFiles} files`);
|
|
32
|
+
console.log(`Duration: ${result.metadata.duration}ms`);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Review Modes
|
|
36
|
+
|
|
37
|
+
### heuristic (CI Mode)
|
|
38
|
+
|
|
39
|
+
Fast, deterministic analysis using only heuristic engines:
|
|
40
|
+
- ✅ ESLint, Ruff, golangci-lint, Clippy
|
|
41
|
+
- ✅ No LLM calls (free, fast, predictable)
|
|
42
|
+
- ✅ Perfect for CI pipelines
|
|
43
|
+
- ❌ Limited to syntactic issues
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
await runReview({
|
|
47
|
+
mode: 'heuristic',
|
|
48
|
+
scope: 'changed',
|
|
49
|
+
cwd: process.cwd(),
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Use cases:**
|
|
54
|
+
- CI/CD pipelines
|
|
55
|
+
- Pre-commit hooks
|
|
56
|
+
- Quick local checks
|
|
57
|
+
|
|
58
|
+
### full (Local Mode)
|
|
59
|
+
|
|
60
|
+
Comprehensive analysis with heuristic + LLM:
|
|
61
|
+
- ✅ All heuristic engines
|
|
62
|
+
- ✅ LLM analyzers for complex issues
|
|
63
|
+
- ✅ Cached LLM results (fast on second run)
|
|
64
|
+
- ❌ Costs tokens (configurable budget)
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
await runReview({
|
|
68
|
+
mode: 'full',
|
|
69
|
+
scope: 'all',
|
|
70
|
+
cwd: process.cwd(),
|
|
71
|
+
presetId: 'typescript-strict',
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Use cases:**
|
|
76
|
+
- Local development
|
|
77
|
+
- Pre-PR review
|
|
78
|
+
- Weekly codebase audits
|
|
79
|
+
|
|
80
|
+
### llm (Deep Analysis Mode)
|
|
81
|
+
|
|
82
|
+
LLM-only analysis for semantic issues:
|
|
83
|
+
- ❌ No heuristic engines
|
|
84
|
+
- ✅ LLM analyzers for naming, architecture, logic bugs
|
|
85
|
+
- ✅ Best quality (uses large tier LLM)
|
|
86
|
+
- ❌ Most expensive
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
await runReview({
|
|
90
|
+
mode: 'llm',
|
|
91
|
+
scope: 'staged',
|
|
92
|
+
cwd: process.cwd(),
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Use cases:**
|
|
97
|
+
- Architecture reviews
|
|
98
|
+
- Complex refactoring
|
|
99
|
+
- Semantic bug detection
|
|
100
|
+
|
|
101
|
+
## Scope Options
|
|
102
|
+
|
|
103
|
+
Control which files to analyze:
|
|
104
|
+
|
|
105
|
+
- **`all`** - Analyze entire codebase
|
|
106
|
+
- **`changed`** - Analyze files changed vs main branch (git diff)
|
|
107
|
+
- **`staged`** - Analyze only staged files (git diff --staged)
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
await runReview({
|
|
111
|
+
mode: 'heuristic',
|
|
112
|
+
scope: 'changed', // Only changed files
|
|
113
|
+
cwd: process.cwd(),
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Platform Integration
|
|
118
|
+
|
|
119
|
+
The orchestrator uses KB Labs platform composables:
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { useLLM, useCache, useAnalytics } from '@kb-labs/sdk';
|
|
123
|
+
|
|
124
|
+
// Inside orchestrator
|
|
125
|
+
const llm = useLLM({ tier: 'medium' });
|
|
126
|
+
const cache = useCache();
|
|
127
|
+
const analytics = useAnalytics();
|
|
128
|
+
|
|
129
|
+
// LLM tier selection
|
|
130
|
+
// - 'small': Simple tasks (categorization, quick fixes)
|
|
131
|
+
// - 'medium': Standard analysis (most LLM analyzers)
|
|
132
|
+
// - 'large': Complex reasoning (architecture, logic bugs)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Caching
|
|
136
|
+
|
|
137
|
+
Results are cached via State Broker for fast repeat analysis:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
// First run: ~10s (runs ESLint)
|
|
141
|
+
await runReview({ mode: 'heuristic', scope: 'all', cwd: '.' });
|
|
142
|
+
|
|
143
|
+
// Second run: ~100ms (cached)
|
|
144
|
+
await runReview({ mode: 'heuristic', scope: 'all', cwd: '.' });
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Cache keys include:
|
|
148
|
+
- Engine ID (eslint, ruff, etc.)
|
|
149
|
+
- Scope (all, changed, staged)
|
|
150
|
+
- Preset ID
|
|
151
|
+
- File patterns
|
|
152
|
+
|
|
153
|
+
**Cache invalidation:**
|
|
154
|
+
- TTL: 1 hour
|
|
155
|
+
- Manual: Clear State Broker cache
|
|
156
|
+
- Automatic: File content hash changes (TODO)
|
|
157
|
+
|
|
158
|
+
## Deduplication
|
|
159
|
+
|
|
160
|
+
Findings from multiple engines are deduplicated using:
|
|
161
|
+
|
|
162
|
+
1. **Fingerprint**: `sha1(ruleId|file|bucket|snippetHash)`
|
|
163
|
+
2. **Engine Type Priority**: compiler > linter > sast > ast > llm
|
|
164
|
+
3. **Severity**: blocker > high > medium > low > info (if same type)
|
|
165
|
+
|
|
166
|
+
Example:
|
|
167
|
+
```typescript
|
|
168
|
+
// Input: 3 findings for same issue
|
|
169
|
+
// - ESLint: "no-unused-vars" (linter, medium)
|
|
170
|
+
// - TSC: "TS6133" (compiler, high)
|
|
171
|
+
// - Semgrep: "unused-variable" (sast, low)
|
|
172
|
+
|
|
173
|
+
// Output: 1 finding (TSC wins: compiler > linter > sast)
|
|
174
|
+
{
|
|
175
|
+
id: 'tsc:/path/file.ts:TS6133:10:5',
|
|
176
|
+
engine: 'tsc',
|
|
177
|
+
severity: 'high',
|
|
178
|
+
message: "'foo' is declared but never used.",
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Analytics
|
|
183
|
+
|
|
184
|
+
The orchestrator tracks analytics events:
|
|
185
|
+
|
|
186
|
+
- `review:started` - Review started
|
|
187
|
+
- `review:completed` - Review completed successfully
|
|
188
|
+
- `review:failed` - Review failed with error
|
|
189
|
+
|
|
190
|
+
Metrics:
|
|
191
|
+
- Mode used
|
|
192
|
+
- Findings count
|
|
193
|
+
- Duration
|
|
194
|
+
- Engines used
|
|
195
|
+
- Files analyzed
|
|
196
|
+
|
|
197
|
+
## Configuration
|
|
198
|
+
|
|
199
|
+
Pass config via ReviewRequest:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
await runReview({
|
|
203
|
+
mode: 'full',
|
|
204
|
+
scope: 'all',
|
|
205
|
+
cwd: '/path/to/project',
|
|
206
|
+
files: ['src/**/*.ts', 'src/**/*.tsx'],
|
|
207
|
+
presetId: 'typescript-strict',
|
|
208
|
+
config: {
|
|
209
|
+
eslintConfig: '.eslintrc.json',
|
|
210
|
+
// Add more engine configs here
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Architecture
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
ReviewOrchestrator
|
|
219
|
+
│
|
|
220
|
+
├─ runHeuristicAnalysis()
|
|
221
|
+
│ ├─ runESLint() → analyzeWithESLint()
|
|
222
|
+
│ ├─ runRuff() (TODO)
|
|
223
|
+
│ ├─ runGolangci() (TODO)
|
|
224
|
+
│ └─ deduplicateFindings()
|
|
225
|
+
│
|
|
226
|
+
├─ runFullAnalysis()
|
|
227
|
+
│ ├─ runHeuristicAnalysis()
|
|
228
|
+
│ ├─ runLLMAnalyzers() (TODO)
|
|
229
|
+
│ └─ deduplicateFindings()
|
|
230
|
+
│
|
|
231
|
+
└─ runLLMAnalysis()
|
|
232
|
+
├─ runLLMAnalyzers() (TODO)
|
|
233
|
+
└─ deduplicateFindings()
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
## Error Handling
|
|
237
|
+
|
|
238
|
+
The orchestrator catches and tracks errors:
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
try {
|
|
242
|
+
const result = await runReview(request);
|
|
243
|
+
console.log('Success:', result.findings.length);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
// Analytics tracked automatically
|
|
246
|
+
console.error('Review failed:', error);
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Future Enhancements
|
|
251
|
+
|
|
252
|
+
### Phase 1 (Current)
|
|
253
|
+
- ✅ Heuristic mode with ESLint
|
|
254
|
+
- ✅ Deduplication with engine type priority
|
|
255
|
+
- ✅ Platform integration (useLLM, useCache, useAnalytics)
|
|
256
|
+
- ⏳ CLI commands
|
|
257
|
+
|
|
258
|
+
### Phase 2
|
|
259
|
+
- [ ] Full mode with LLM analyzers
|
|
260
|
+
- [ ] More heuristic engines (Ruff, golangci, Clippy)
|
|
261
|
+
- [ ] Content-hash based caching
|
|
262
|
+
- [ ] Preset system
|
|
263
|
+
|
|
264
|
+
### Phase 3
|
|
265
|
+
- [ ] LLM mode for semantic analysis
|
|
266
|
+
- [ ] Agent-powered preset generation
|
|
267
|
+
- [ ] Interactive review UI
|
|
268
|
+
- [ ] Auto-fix support
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import { ReviewRequest, ReviewResult, PresetDefinition, InputFile, IDiffProvider, BatchDiffRequest, BatchDiffResult, FileDiff, ReviewFinding } from '@kb-labs/review-contracts';
|
|
2
|
+
export { BatchDiffRequest, BatchDiffResult, DiffHunk, FileDiff } from '@kb-labs/review-contracts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @module @kb-labs/review-core/orchestrator
|
|
6
|
+
* Main orchestrator for code review operations.
|
|
7
|
+
*
|
|
8
|
+
* Coordinates heuristic engines, LLM analysis, caching, and deduplication.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Review orchestrator.
|
|
13
|
+
*
|
|
14
|
+
* Main entry point for running code reviews.
|
|
15
|
+
* Thread-safe: all state is passed through parameters, not instance variables.
|
|
16
|
+
*/
|
|
17
|
+
declare class ReviewOrchestrator {
|
|
18
|
+
/**
|
|
19
|
+
* Run code review.
|
|
20
|
+
*
|
|
21
|
+
* @param request - Review request
|
|
22
|
+
* @returns Review result with findings
|
|
23
|
+
*/
|
|
24
|
+
review(request: ReviewRequest): Promise<ReviewResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Run analysis based on review mode.
|
|
27
|
+
*/
|
|
28
|
+
private runAnalysis;
|
|
29
|
+
/**
|
|
30
|
+
* Run heuristic-only analysis (CI mode).
|
|
31
|
+
*
|
|
32
|
+
* Fast, deterministic analysis using linters via CLI.
|
|
33
|
+
* No LLM calls.
|
|
34
|
+
*/
|
|
35
|
+
private runHeuristicAnalysis;
|
|
36
|
+
/**
|
|
37
|
+
* Run full analysis (heuristic + LLM-Lite).
|
|
38
|
+
*
|
|
39
|
+
* Runs heuristic analysis (ESLint, etc.) and LLM-Lite in parallel.
|
|
40
|
+
* Combines deterministic linting with intelligent code review.
|
|
41
|
+
* Both use caching for incremental analysis.
|
|
42
|
+
*/
|
|
43
|
+
private runFullAnalysis;
|
|
44
|
+
/**
|
|
45
|
+
* Run LLM-Lite analysis (v2) with incremental caching.
|
|
46
|
+
*
|
|
47
|
+
* Uses batch tools, diff-based context, and anti-hallucination verification.
|
|
48
|
+
* Caches findings by file content hash to skip unchanged files.
|
|
49
|
+
*/
|
|
50
|
+
private runLLMAnalysis;
|
|
51
|
+
/**
|
|
52
|
+
* Resolve file patterns to absolute paths.
|
|
53
|
+
*
|
|
54
|
+
* Note: Files must be provided in request.files.
|
|
55
|
+
* Pattern resolution should happen at CLI layer using ctx.runtime.fs.glob()
|
|
56
|
+
*/
|
|
57
|
+
private resolvePatterns;
|
|
58
|
+
/**
|
|
59
|
+
* Count files to be analyzed.
|
|
60
|
+
*/
|
|
61
|
+
private countFiles;
|
|
62
|
+
/**
|
|
63
|
+
* Get list of engines that produced findings.
|
|
64
|
+
*/
|
|
65
|
+
private getEnginesUsed;
|
|
66
|
+
/**
|
|
67
|
+
* Group findings by type.
|
|
68
|
+
*/
|
|
69
|
+
private groupByType;
|
|
70
|
+
/**
|
|
71
|
+
* Generate cache key for heuristic analysis.
|
|
72
|
+
*/
|
|
73
|
+
private generateCacheKey;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Quick helper to run code review.
|
|
77
|
+
*/
|
|
78
|
+
declare function runReview(request: ReviewRequest): Promise<ReviewResult>;
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @module @kb-labs/review-core/presets/preset-loader
|
|
82
|
+
* Preset loading and resolution logic
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Preset loader
|
|
87
|
+
* Resolves presets from builtin definitions and kb.config.json
|
|
88
|
+
*/
|
|
89
|
+
declare class PresetLoader {
|
|
90
|
+
private presets;
|
|
91
|
+
private configLoaded;
|
|
92
|
+
constructor();
|
|
93
|
+
/**
|
|
94
|
+
* Load builtin presets
|
|
95
|
+
*/
|
|
96
|
+
private loadBuiltinPresets;
|
|
97
|
+
/**
|
|
98
|
+
* Resolve preset inheritance (extends) with cycle detection
|
|
99
|
+
*/
|
|
100
|
+
private resolveInheritance;
|
|
101
|
+
/**
|
|
102
|
+
* Load custom presets from kb.config.json and preset files
|
|
103
|
+
* Called lazily on first preset access
|
|
104
|
+
*/
|
|
105
|
+
private loadConfigPresets;
|
|
106
|
+
/**
|
|
107
|
+
* Auto-scan .kb/ai-review/presets/ directory for preset files
|
|
108
|
+
*/
|
|
109
|
+
private scanPresetsDirectory;
|
|
110
|
+
/**
|
|
111
|
+
* Load preset from JSON file
|
|
112
|
+
*/
|
|
113
|
+
private loadPresetFromFile;
|
|
114
|
+
/**
|
|
115
|
+
* Load atomic rule from .md file
|
|
116
|
+
*/
|
|
117
|
+
private loadAtomicRule;
|
|
118
|
+
/**
|
|
119
|
+
* Compose atomic rules into convention text
|
|
120
|
+
*/
|
|
121
|
+
private composeRules;
|
|
122
|
+
/**
|
|
123
|
+
* Apply atomic rules composition to preset
|
|
124
|
+
* Supports dynamic categories - any category name defined in atomicRules
|
|
125
|
+
*/
|
|
126
|
+
private applyAtomicRules;
|
|
127
|
+
/**
|
|
128
|
+
* Get preset by ID (with inheritance resolved)
|
|
129
|
+
*/
|
|
130
|
+
getPreset(id: string): Promise<PresetDefinition | undefined>;
|
|
131
|
+
/**
|
|
132
|
+
* Get preset by ID or throw error
|
|
133
|
+
*/
|
|
134
|
+
getPresetOrThrow(id: string): Promise<PresetDefinition>;
|
|
135
|
+
/**
|
|
136
|
+
* List all available presets
|
|
137
|
+
*/
|
|
138
|
+
listPresets(): Promise<PresetDefinition[]>;
|
|
139
|
+
/**
|
|
140
|
+
* Register custom preset
|
|
141
|
+
*/
|
|
142
|
+
registerPreset(preset: PresetDefinition): void;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Get global preset loader
|
|
146
|
+
*/
|
|
147
|
+
declare function getPresetLoader(): PresetLoader;
|
|
148
|
+
/**
|
|
149
|
+
* Load preset by ID
|
|
150
|
+
*/
|
|
151
|
+
declare function loadPreset(id: string): Promise<PresetDefinition>;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @module @kb-labs/review-core/presets/builtin-presets
|
|
155
|
+
* Built-in preset definitions
|
|
156
|
+
*/
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* All built-in presets
|
|
160
|
+
*/
|
|
161
|
+
declare const builtinPresets: PresetDefinition[];
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @module @kb-labs/review-core/git-scope
|
|
165
|
+
* Git scope resolver for diff-based reviews.
|
|
166
|
+
*
|
|
167
|
+
* Supports nested git repositories (submodules) common in monorepos.
|
|
168
|
+
*/
|
|
169
|
+
|
|
170
|
+
interface GitScopeOptions {
|
|
171
|
+
/** Working directory (root of monorepo) */
|
|
172
|
+
cwd: string;
|
|
173
|
+
/** Repository names to include (e.g., ['kb-labs-core', 'kb-labs-cli']) */
|
|
174
|
+
repos: string[];
|
|
175
|
+
/** Include staged files */
|
|
176
|
+
includeStaged?: boolean;
|
|
177
|
+
/** Include unstaged files */
|
|
178
|
+
includeUnstaged?: boolean;
|
|
179
|
+
/** Include untracked files */
|
|
180
|
+
includeUntracked?: boolean;
|
|
181
|
+
}
|
|
182
|
+
interface ScopedFiles {
|
|
183
|
+
/** Files with content ready for review */
|
|
184
|
+
files: InputFile[];
|
|
185
|
+
/** Summary of what was found */
|
|
186
|
+
summary: {
|
|
187
|
+
repos: string[];
|
|
188
|
+
staged: number;
|
|
189
|
+
unstaged: number;
|
|
190
|
+
untracked: number;
|
|
191
|
+
total: number;
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Resolve git scope to list of changed files.
|
|
196
|
+
*
|
|
197
|
+
* For each repo in scope:
|
|
198
|
+
* 1. Detect if it's a nested git repo (has .git)
|
|
199
|
+
* 2. Get git status from that repo
|
|
200
|
+
* 3. Read file contents
|
|
201
|
+
* 4. Return as InputFile[] with paths relative to root
|
|
202
|
+
*/
|
|
203
|
+
declare function resolveGitScope(options: GitScopeOptions): Promise<ScopedFiles>;
|
|
204
|
+
/**
|
|
205
|
+
* Detect all submodules/nested repos in cwd.
|
|
206
|
+
* Reads paths from .gitmodules — no hardcoded category directories.
|
|
207
|
+
* Returns relative paths as they appear in .gitmodules (e.g. "platform/kb-labs-core").
|
|
208
|
+
*/
|
|
209
|
+
declare function discoverRepos(cwd: string): Promise<string[]>;
|
|
210
|
+
/**
|
|
211
|
+
* Get all repos with uncommitted changes
|
|
212
|
+
*/
|
|
213
|
+
declare function getReposWithChanges(cwd: string): Promise<string[]>;
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* @module @kb-labs/review-core/diff-provider
|
|
217
|
+
* Git diff fetching and parsing for LLM-lite review mode.
|
|
218
|
+
*
|
|
219
|
+
* Provides diff-based context instead of full file content.
|
|
220
|
+
*/
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* DiffProvider - fetches and parses git diffs
|
|
224
|
+
*/
|
|
225
|
+
declare class DiffProvider implements IDiffProvider {
|
|
226
|
+
private git;
|
|
227
|
+
private cwd;
|
|
228
|
+
constructor(cwd: string);
|
|
229
|
+
/**
|
|
230
|
+
* Get diffs for multiple files in one call (batch operation)
|
|
231
|
+
*/
|
|
232
|
+
getDiffs(request: BatchDiffRequest): Promise<BatchDiffResult>;
|
|
233
|
+
/**
|
|
234
|
+
* Get diff for a single file
|
|
235
|
+
*/
|
|
236
|
+
getFileDiff(file: string, staged?: boolean, unstaged?: boolean, maxLines?: number): Promise<FileDiff | null>;
|
|
237
|
+
/**
|
|
238
|
+
* Parse unified diff into structured format
|
|
239
|
+
*/
|
|
240
|
+
private parseDiff;
|
|
241
|
+
/**
|
|
242
|
+
* Parse hunk content to extract line numbers
|
|
243
|
+
*/
|
|
244
|
+
private parseHunkContent;
|
|
245
|
+
/**
|
|
246
|
+
* Check if a line number is in the diff (was changed)
|
|
247
|
+
*/
|
|
248
|
+
isLineInDiff(fileDiff: FileDiff, lineNumber: number): boolean;
|
|
249
|
+
/**
|
|
250
|
+
* Get context around a specific line (for verification)
|
|
251
|
+
*/
|
|
252
|
+
getLineContext(file: string, lineNumber: number, contextLines?: number): Promise<{
|
|
253
|
+
lines: string[];
|
|
254
|
+
startLine: number;
|
|
255
|
+
} | null>;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Create a DiffProvider instance
|
|
259
|
+
*/
|
|
260
|
+
declare function createDiffProvider(cwd: string): DiffProvider;
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* @module @kb-labs/review-core/findings-cache
|
|
264
|
+
* Cache for LLM review findings based on file content hash.
|
|
265
|
+
*
|
|
266
|
+
* Enables incremental review:
|
|
267
|
+
* - Skip unchanged files (return cached findings)
|
|
268
|
+
* - Track "known issues" vs "new issues" for changed files
|
|
269
|
+
*/
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Cached finding with file hash
|
|
273
|
+
*/
|
|
274
|
+
interface CachedFinding extends ReviewFinding {
|
|
275
|
+
/** Hash of file content when finding was created */
|
|
276
|
+
contentHash: string;
|
|
277
|
+
/** Timestamp when finding was cached */
|
|
278
|
+
cachedAt: number;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Cache entry for a single file
|
|
282
|
+
*/
|
|
283
|
+
interface FileCacheEntry {
|
|
284
|
+
/** File path */
|
|
285
|
+
path: string;
|
|
286
|
+
/** Content hash */
|
|
287
|
+
contentHash: string;
|
|
288
|
+
/** Findings for this file */
|
|
289
|
+
findings: CachedFinding[];
|
|
290
|
+
/** When this entry was created */
|
|
291
|
+
createdAt: number;
|
|
292
|
+
/** When this entry was last accessed */
|
|
293
|
+
lastAccessedAt: number;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Full cache structure
|
|
297
|
+
*/
|
|
298
|
+
interface FindingsCacheData {
|
|
299
|
+
/** Version for cache format migrations */
|
|
300
|
+
version: number;
|
|
301
|
+
/** Cache entries by file path */
|
|
302
|
+
entries: Record<string, FileCacheEntry>;
|
|
303
|
+
/** Cache metadata */
|
|
304
|
+
metadata: {
|
|
305
|
+
createdAt: number;
|
|
306
|
+
lastUpdatedAt: number;
|
|
307
|
+
totalFindings: number;
|
|
308
|
+
totalFiles: number;
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Result of cache lookup
|
|
313
|
+
*/
|
|
314
|
+
interface CacheLookupResult {
|
|
315
|
+
/** Files that have valid cache (unchanged) */
|
|
316
|
+
cached: {
|
|
317
|
+
file: InputFile;
|
|
318
|
+
findings: ReviewFinding[];
|
|
319
|
+
}[];
|
|
320
|
+
/** Files that need fresh analysis (changed or not cached) */
|
|
321
|
+
uncached: InputFile[];
|
|
322
|
+
/** Statistics */
|
|
323
|
+
stats: {
|
|
324
|
+
cachedFiles: number;
|
|
325
|
+
uncachedFiles: number;
|
|
326
|
+
cachedFindings: number;
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Result of incremental comparison
|
|
331
|
+
*/
|
|
332
|
+
interface IncrementalResult {
|
|
333
|
+
/** New findings (not seen before) */
|
|
334
|
+
newFindings: ReviewFinding[];
|
|
335
|
+
/** Known findings (seen in previous review) */
|
|
336
|
+
knownFindings: ReviewFinding[];
|
|
337
|
+
/** Findings from unchanged (cached) files */
|
|
338
|
+
cachedFindings: ReviewFinding[];
|
|
339
|
+
/** Statistics */
|
|
340
|
+
stats: {
|
|
341
|
+
new: number;
|
|
342
|
+
known: number;
|
|
343
|
+
cached: number;
|
|
344
|
+
total: number;
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Generate content hash for a file.
|
|
349
|
+
*
|
|
350
|
+
* Uses SHA-256 for cryptographic strength, truncated to 16 hex chars (64 bits).
|
|
351
|
+
* This provides sufficient collision resistance for cache invalidation while
|
|
352
|
+
* keeping cache keys reasonably short.
|
|
353
|
+
*
|
|
354
|
+
* @param content - File content to hash
|
|
355
|
+
* @returns 16-character hex string (first 64 bits of SHA-256)
|
|
356
|
+
*/
|
|
357
|
+
declare function hashFileContent(content: string): string;
|
|
358
|
+
/**
|
|
359
|
+
* Generate finding signature for deduplication
|
|
360
|
+
* Two findings are "same" if they have same file, line range, and category
|
|
361
|
+
*/
|
|
362
|
+
declare function findingSignature(finding: ReviewFinding): string;
|
|
363
|
+
/**
|
|
364
|
+
* Findings cache manager
|
|
365
|
+
*/
|
|
366
|
+
declare class FindingsCache {
|
|
367
|
+
private cwd;
|
|
368
|
+
private cachePath;
|
|
369
|
+
private data;
|
|
370
|
+
constructor(cwd: string);
|
|
371
|
+
/**
|
|
372
|
+
* Load cache from disk
|
|
373
|
+
*/
|
|
374
|
+
load(): Promise<void>;
|
|
375
|
+
/**
|
|
376
|
+
* Save cache to disk
|
|
377
|
+
*/
|
|
378
|
+
save(): Promise<void>;
|
|
379
|
+
/**
|
|
380
|
+
* Look up files in cache
|
|
381
|
+
* Returns which files can use cached findings vs need fresh analysis
|
|
382
|
+
*/
|
|
383
|
+
lookup(files: InputFile[]): CacheLookupResult;
|
|
384
|
+
/**
|
|
385
|
+
* Update cache with new findings
|
|
386
|
+
*/
|
|
387
|
+
update(files: InputFile[], findings: ReviewFinding[]): void;
|
|
388
|
+
/**
|
|
389
|
+
* Compare new findings with cached to find new vs known issues
|
|
390
|
+
*/
|
|
391
|
+
compareIncremental(newFindings: ReviewFinding[], cachedFindings: ReviewFinding[]): IncrementalResult;
|
|
392
|
+
/**
|
|
393
|
+
* Clear cache for specific files or all
|
|
394
|
+
*/
|
|
395
|
+
clear(files?: string[]): void;
|
|
396
|
+
/**
|
|
397
|
+
* Get cache statistics
|
|
398
|
+
*/
|
|
399
|
+
getStats(): {
|
|
400
|
+
totalFiles: number;
|
|
401
|
+
totalFindings: number;
|
|
402
|
+
cacheAge: number;
|
|
403
|
+
};
|
|
404
|
+
private createEmptyCache;
|
|
405
|
+
private stripCacheFields;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Create findings cache instance
|
|
409
|
+
*/
|
|
410
|
+
declare function createFindingsCache(cwd: string): FindingsCache;
|
|
411
|
+
|
|
412
|
+
export { type CacheLookupResult, type CachedFinding, DiffProvider, type FileCacheEntry, FindingsCache, type FindingsCacheData, type GitScopeOptions, type IncrementalResult, PresetLoader, ReviewOrchestrator, type ScopedFiles, builtinPresets, createDiffProvider, createFindingsCache, discoverRepos, findingSignature, getPresetLoader, getReposWithChanges, hashFileContent, loadPreset, resolveGitScope, runReview };
|