@kb-labs/review-llm 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 +23 -0
- package/dist/analyzers/architecture-analyzer.d.ts +22 -0
- package/dist/analyzers/architecture-analyzer.js +226 -0
- package/dist/analyzers/architecture-analyzer.js.map +1 -0
- package/dist/analyzers/naming-analyzer.d.ts +22 -0
- package/dist/analyzers/naming-analyzer.js +200 -0
- package/dist/analyzers/naming-analyzer.js.map +1 -0
- package/dist/analyzers/security-analyzer.d.ts +35 -0
- package/dist/analyzers/security-analyzer.js +261 -0
- package/dist/analyzers/security-analyzer.js.map +1 -0
- package/dist/index.d.ts +558 -0
- package/dist/index.js +2020 -0
- package/dist/index.js.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @kb-labs/review-llm
|
|
2
|
+
|
|
3
|
+
LLM-based analysis for KB Labs AI Review
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @kb-labs/review-llm
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { ... } from '@kb-labs/review-llm';
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## API
|
|
18
|
+
|
|
19
|
+
See TypeScript types for detailed API documentation.
|
|
20
|
+
|
|
21
|
+
## License
|
|
22
|
+
|
|
23
|
+
MIT
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BaseLLMAnalyzer, ParsedFile, ReviewContext, ReviewFinding } from '@kb-labs/review-contracts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @module @kb-labs/review-llm/analyzers/architecture-analyzer
|
|
5
|
+
* Architecture patterns analyzer using LLM with tool calling
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Architecture analyzer
|
|
10
|
+
* Uses LLM with tool calling for structured findings
|
|
11
|
+
*/
|
|
12
|
+
declare class ArchitectureAnalyzer extends BaseLLMAnalyzer {
|
|
13
|
+
readonly id = "architecture";
|
|
14
|
+
readonly name = "Architecture Analysis";
|
|
15
|
+
analyze(files: ParsedFile[], context: ReviewContext): Promise<ReviewFinding[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Process LLM tool calls into ReviewFindings
|
|
18
|
+
*/
|
|
19
|
+
private processToolCalls;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { ArchitectureAnalyzer };
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { BaseLLMAnalyzer } from '@kb-labs/review-contracts';
|
|
2
|
+
import { useCache, useAnalytics, useLLM, useLogger } from '@kb-labs/sdk';
|
|
3
|
+
|
|
4
|
+
// src/analyzers/architecture-analyzer.ts
|
|
5
|
+
|
|
6
|
+
// src/prompts/architecture.prompts.ts
|
|
7
|
+
function buildSystemPrompt(context) {
|
|
8
|
+
const sections = [
|
|
9
|
+
"You are an expert code reviewer specializing in software architecture.",
|
|
10
|
+
"",
|
|
11
|
+
"**Your role:**",
|
|
12
|
+
"- Identify architecture violations and anti-patterns",
|
|
13
|
+
"- Check adherence to project conventions",
|
|
14
|
+
"- Suggest improvements with clear rationale",
|
|
15
|
+
"",
|
|
16
|
+
"**CRITICAL RULES:**",
|
|
17
|
+
"1. ONLY report issues that exist in the provided code",
|
|
18
|
+
"2. Reference line numbers that actually exist in the file",
|
|
19
|
+
"3. Quote exact code snippets when referencing issues",
|
|
20
|
+
"4. Provide concrete, actionable suggestions",
|
|
21
|
+
"5. Reference project conventions and ADRs when applicable",
|
|
22
|
+
""
|
|
23
|
+
];
|
|
24
|
+
for (const [category, content] of Object.entries(context.conventions)) {
|
|
25
|
+
sections.push(`**Project Conventions (${category}):**`);
|
|
26
|
+
sections.push(content);
|
|
27
|
+
sections.push("");
|
|
28
|
+
}
|
|
29
|
+
if (context.taskContext) {
|
|
30
|
+
sections.push("**Current Task Context:**");
|
|
31
|
+
sections.push(context.taskContext);
|
|
32
|
+
sections.push("");
|
|
33
|
+
sections.push("IMPORTANT: Evaluate architecture decisions in the context of this specific task.");
|
|
34
|
+
sections.push("");
|
|
35
|
+
}
|
|
36
|
+
if (context.repoScope && context.repoScope.length > 0) {
|
|
37
|
+
sections.push("**Repositories in Scope:**");
|
|
38
|
+
sections.push(context.repoScope.map((r) => `- ${r}`).join("\n"));
|
|
39
|
+
sections.push("");
|
|
40
|
+
sections.push("NOTE: Changes may span multiple repositories as part of one logical task.");
|
|
41
|
+
sections.push("");
|
|
42
|
+
}
|
|
43
|
+
if (context.relatedADRs.length > 0) {
|
|
44
|
+
sections.push("**Related Architecture Decisions:**");
|
|
45
|
+
for (const adr of context.relatedADRs) {
|
|
46
|
+
sections.push(`- ${adr.id}: ${adr.title} - ${adr.summary}`);
|
|
47
|
+
}
|
|
48
|
+
sections.push("");
|
|
49
|
+
}
|
|
50
|
+
if (context.examples.length > 0) {
|
|
51
|
+
sections.push("**Examples from codebase (informative, not normative):**");
|
|
52
|
+
for (const ex of context.examples.slice(0, 3)) {
|
|
53
|
+
sections.push(`- ${ex.file}: ${ex.description}`);
|
|
54
|
+
}
|
|
55
|
+
sections.push("");
|
|
56
|
+
}
|
|
57
|
+
sections.push("Use the report_architecture_finding tool to report issues.");
|
|
58
|
+
return sections.join("\n");
|
|
59
|
+
}
|
|
60
|
+
function analyzeFile(file) {
|
|
61
|
+
const lines = file.content.split("\n");
|
|
62
|
+
const numbered = lines.map((line, idx) => `${idx + 1}: ${line}`).join("\n");
|
|
63
|
+
return `Review this file for architecture issues:
|
|
64
|
+
|
|
65
|
+
File: ${file.path}
|
|
66
|
+
Language: ${file.language}
|
|
67
|
+
|
|
68
|
+
\`\`\`${file.language}
|
|
69
|
+
${numbered}
|
|
70
|
+
\`\`\`
|
|
71
|
+
|
|
72
|
+
Report any architecture violations, anti-patterns, or violations of project conventions.`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/analyzers/architecture-analyzer.ts
|
|
76
|
+
var ARCHITECTURE_FINDING_TOOL = {
|
|
77
|
+
name: "report_architecture_finding",
|
|
78
|
+
description: "Report an architecture issue or suggestion",
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: "object",
|
|
81
|
+
properties: {
|
|
82
|
+
severity: {
|
|
83
|
+
type: "string",
|
|
84
|
+
enum: ["error", "warning", "info"],
|
|
85
|
+
description: "Severity level of the finding"
|
|
86
|
+
},
|
|
87
|
+
message: {
|
|
88
|
+
type: "string",
|
|
89
|
+
description: "Clear description of the architecture issue"
|
|
90
|
+
},
|
|
91
|
+
line: {
|
|
92
|
+
type: "number",
|
|
93
|
+
description: "Line number where issue occurs (must exist in file)"
|
|
94
|
+
},
|
|
95
|
+
suggestion: {
|
|
96
|
+
type: "string",
|
|
97
|
+
description: "Concrete fix suggestion with code example"
|
|
98
|
+
},
|
|
99
|
+
rationale: {
|
|
100
|
+
type: "string",
|
|
101
|
+
description: "Why this is an issue (reference conventions/ADRs if applicable)"
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
required: ["severity", "message", "line", "suggestion", "rationale"]
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
function mapSeverity(llmSeverity) {
|
|
108
|
+
switch (llmSeverity) {
|
|
109
|
+
case "error":
|
|
110
|
+
return "high";
|
|
111
|
+
case "warning":
|
|
112
|
+
return "medium";
|
|
113
|
+
case "info":
|
|
114
|
+
return "info";
|
|
115
|
+
default:
|
|
116
|
+
return "medium";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
var ArchitectureAnalyzer = class extends BaseLLMAnalyzer {
|
|
120
|
+
id = "architecture";
|
|
121
|
+
name = "Architecture Analysis";
|
|
122
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity -- Complex LLM-based analysis with caching, error handling, and result validation
|
|
123
|
+
async analyze(files, context) {
|
|
124
|
+
const findings = [];
|
|
125
|
+
const cache = useCache();
|
|
126
|
+
const analytics = useAnalytics();
|
|
127
|
+
for (const file of files) {
|
|
128
|
+
const cacheKey = this.generateCacheKey(file, context.preset);
|
|
129
|
+
if (cache) {
|
|
130
|
+
const cached = await cache.get(cacheKey);
|
|
131
|
+
if (cached) {
|
|
132
|
+
findings.push(...cached);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const systemPrompt = buildSystemPrompt(context);
|
|
137
|
+
const userPrompt = analyzeFile(file);
|
|
138
|
+
const llm = useLLM({ tier: "medium" });
|
|
139
|
+
if (!llm?.chatWithTools) {
|
|
140
|
+
throw new Error("LLM not configured or does not support tool calling");
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const response = await llm.chatWithTools(
|
|
144
|
+
[
|
|
145
|
+
{ role: "system", content: systemPrompt },
|
|
146
|
+
{ role: "user", content: userPrompt }
|
|
147
|
+
],
|
|
148
|
+
{
|
|
149
|
+
tools: [ARCHITECTURE_FINDING_TOOL],
|
|
150
|
+
temperature: 0.2
|
|
151
|
+
// Low temp for consistency
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
if (!response || typeof response !== "object") {
|
|
155
|
+
useLogger()?.debug(`[ArchitectureAnalyzer] Invalid response from LLM for ${file.path}`);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (analytics && response.usage) {
|
|
159
|
+
await analytics.track("review.architecture.complete", {
|
|
160
|
+
file: file.path,
|
|
161
|
+
tokensUsed: (response.usage.promptTokens ?? 0) + (response.usage.completionTokens ?? 0),
|
|
162
|
+
toolCalls: response.toolCalls?.length ?? 0
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
const fileFindings = this.processToolCalls(response.toolCalls || [], file);
|
|
166
|
+
findings.push(...fileFindings);
|
|
167
|
+
if (cache) {
|
|
168
|
+
await cache.set(cacheKey, fileFindings, 864e5);
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
172
|
+
const errorName = error instanceof Error ? error.name : "UnknownError";
|
|
173
|
+
useLogger()?.debug(`[ArchitectureAnalyzer] Failed to analyze ${file.path}:`, {
|
|
174
|
+
error,
|
|
175
|
+
errorName,
|
|
176
|
+
errorMessage
|
|
177
|
+
});
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return findings;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Process LLM tool calls into ReviewFindings
|
|
185
|
+
*/
|
|
186
|
+
processToolCalls(toolCalls, file) {
|
|
187
|
+
const findings = [];
|
|
188
|
+
for (const call of toolCalls) {
|
|
189
|
+
if (call.name === "report_architecture_finding") {
|
|
190
|
+
const args = call.input;
|
|
191
|
+
const lineCount = file.content.split("\n").length;
|
|
192
|
+
const line = typeof args.line === "number" ? args.line : parseInt(args.line, 10);
|
|
193
|
+
if (isNaN(line) || line < 1 || line > lineCount) {
|
|
194
|
+
useLogger()?.debug(`[ArchitectureAnalyzer] Invalid line number ${args.line} for ${file.path}`);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const severity = mapSeverity(String(args.severity || "warning"));
|
|
198
|
+
findings.push({
|
|
199
|
+
id: this.buildFindingId(file, line, "arch"),
|
|
200
|
+
ruleId: "llm:architecture",
|
|
201
|
+
type: "architecture",
|
|
202
|
+
severity,
|
|
203
|
+
confidence: "likely",
|
|
204
|
+
// LLM findings are "likely", not "certain"
|
|
205
|
+
file: file.path,
|
|
206
|
+
line,
|
|
207
|
+
message: String(args.message || "").slice(0, 500),
|
|
208
|
+
suggestion: args.suggestion ? String(args.suggestion).slice(0, 1e3) : void 0,
|
|
209
|
+
rationale: args.rationale ? String(args.rationale).slice(0, 500) : void 0,
|
|
210
|
+
engine: "llm",
|
|
211
|
+
source: "llm-architecture",
|
|
212
|
+
// For agent mode gating
|
|
213
|
+
scope: "local",
|
|
214
|
+
// Architecture can be local or global
|
|
215
|
+
automated: false
|
|
216
|
+
// LLM suggestions need human review
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return findings;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
export { ArchitectureAnalyzer };
|
|
225
|
+
//# sourceMappingURL=architecture-analyzer.js.map
|
|
226
|
+
//# sourceMappingURL=architecture-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/prompts/architecture.prompts.ts","../../src/analyzers/architecture-analyzer.ts"],"names":[],"mappings":"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","file":"architecture-analyzer.js","sourcesContent":["/**\n * @module @kb-labs/review-llm/prompts/architecture\n * Architecture review prompts\n */\n\nimport type { ReviewContext, ParsedFile } from '@kb-labs/review-contracts';\n\n/**\n * Build system prompt with context\n */\nexport function buildSystemPrompt(context: ReviewContext): string {\n const sections: string[] = [\n 'You are an expert code reviewer specializing in software architecture.',\n '',\n '**Your role:**',\n '- Identify architecture violations and anti-patterns',\n '- Check adherence to project conventions',\n '- Suggest improvements with clear rationale',\n '',\n '**CRITICAL RULES:**',\n '1. ONLY report issues that exist in the provided code',\n '2. Reference line numbers that actually exist in the file',\n '3. Quote exact code snippets when referencing issues',\n '4. Provide concrete, actionable suggestions',\n '5. Reference project conventions and ADRs when applicable',\n '',\n ];\n\n // Add all project conventions\n for (const [category, content] of Object.entries(context.conventions)) {\n sections.push(`**Project Conventions (${category}):**`);\n sections.push(content);\n sections.push('');\n }\n\n // Add task context (if reviewing a specific task)\n if (context.taskContext) {\n sections.push('**Current Task Context:**');\n sections.push(context.taskContext);\n sections.push('');\n sections.push('IMPORTANT: Evaluate architecture decisions in the context of this specific task.');\n sections.push('');\n }\n\n // Add repo scope (if multi-repo review)\n if (context.repoScope && context.repoScope.length > 0) {\n sections.push('**Repositories in Scope:**');\n sections.push(context.repoScope.map((r) => `- ${r}`).join('\\n'));\n sections.push('');\n sections.push('NOTE: Changes may span multiple repositories as part of one logical task.');\n sections.push('');\n }\n\n // Add related ADRs\n if (context.relatedADRs.length > 0) {\n sections.push('**Related Architecture Decisions:**');\n for (const adr of context.relatedADRs) {\n sections.push(`- ${adr.id}: ${adr.title} - ${adr.summary}`);\n }\n sections.push('');\n }\n\n // Add examples (if available)\n if (context.examples.length > 0) {\n sections.push('**Examples from codebase (informative, not normative):**');\n for (const ex of context.examples.slice(0, 3)) {\n sections.push(`- ${ex.file}: ${ex.description}`);\n }\n sections.push('');\n }\n\n sections.push('Use the report_architecture_finding tool to report issues.');\n\n return sections.join('\\n');\n}\n\n/**\n * Build user prompt for file analysis\n */\nexport function analyzeFile(file: ParsedFile): string {\n const lines = file.content.split('\\n');\n const numbered = lines\n .map((line, idx) => `${idx + 1}: ${line}`)\n .join('\\n');\n\n return `Review this file for architecture issues:\\n\\n` +\n `File: ${file.path}\\n` +\n `Language: ${file.language}\\n\\n` +\n `\\`\\`\\`${file.language}\\n${numbered}\\n\\`\\`\\`\\n\\n` +\n `Report any architecture violations, anti-patterns, or violations of project conventions.`;\n}\n","/**\n * @module @kb-labs/review-llm/analyzers/architecture-analyzer\n * Architecture patterns analyzer using LLM with tool calling\n */\n\nimport type { ReviewFinding, ParsedFile, ReviewContext } from '@kb-labs/review-contracts';\nimport { BaseLLMAnalyzer } from '@kb-labs/review-contracts';\nimport { useLLM, useCache, useAnalytics, useLogger } from '@kb-labs/sdk';\nimport * as architecturePrompts from '../prompts/architecture.prompts.js';\n\n/**\n * Tool schema for architecture findings (structured output)\n */\nconst ARCHITECTURE_FINDING_TOOL = {\n name: 'report_architecture_finding',\n description: 'Report an architecture issue or suggestion',\n inputSchema: {\n type: 'object',\n properties: {\n severity: {\n type: 'string',\n enum: ['error', 'warning', 'info'],\n description: 'Severity level of the finding',\n },\n message: {\n type: 'string',\n description: 'Clear description of the architecture issue',\n },\n line: {\n type: 'number',\n description: 'Line number where issue occurs (must exist in file)',\n },\n suggestion: {\n type: 'string',\n description: 'Concrete fix suggestion with code example',\n },\n rationale: {\n type: 'string',\n description: 'Why this is an issue (reference conventions/ADRs if applicable)',\n },\n },\n required: ['severity', 'message', 'line', 'suggestion', 'rationale'],\n },\n} as const;\n\n/**\n * Valid severity values for ReviewFinding.\n * Maps to ReviewFinding['severity'] from @kb-labs/review-contracts.\n */\nconst _VALID_SEVERITIES = ['blocker', 'high', 'medium', 'low', 'info'] as const;\ntype ValidSeverity = (typeof _VALID_SEVERITIES)[number];\n\n/**\n * Map LLM severity output to internal ReviewFinding severity.\n * LLM uses tool schema with enum ['error', 'warning', 'info'],\n * but internal findings use ['blocker', 'high', 'medium', 'low', 'info'].\n */\nfunction mapSeverity(llmSeverity: string): ValidSeverity {\n switch (llmSeverity) {\n case 'error': return 'high';\n case 'warning': return 'medium';\n case 'info': return 'info';\n default: return 'medium';\n }\n}\n\n/**\n * Architecture analyzer\n * Uses LLM with tool calling for structured findings\n */\nexport class ArchitectureAnalyzer extends BaseLLMAnalyzer {\n readonly id = 'architecture';\n readonly name = 'Architecture Analysis';\n\n // eslint-disable-next-line sonarjs/cognitive-complexity -- Complex LLM-based analysis with caching, error handling, and result validation\n async analyze(files: ParsedFile[], context: ReviewContext): Promise<ReviewFinding[]> {\n const findings: ReviewFinding[] = [];\n const cache = useCache();\n const analytics = useAnalytics();\n\n for (const file of files) {\n // Check cache first (content-hash based)\n const cacheKey = this.generateCacheKey(file, context.preset);\n\n if (cache) {\n // eslint-disable-next-line no-await-in-loop -- Sequential file analysis with caching\n const cached = await cache.get<ReviewFinding[]>(cacheKey);\n if (cached) {\n findings.push(...cached);\n continue;\n }\n }\n\n // Build prompts with context\n const systemPrompt = architecturePrompts.buildSystemPrompt(context);\n const userPrompt = architecturePrompts.analyzeFile(file);\n\n // Use LLM with tool calling (structured output - better than text parsing!)\n const llm = useLLM({ tier: 'medium' }); // Architecture needs good reasoning\n if (!llm?.chatWithTools) {\n throw new Error('LLM not configured or does not support tool calling');\n }\n\n try {\n // eslint-disable-next-line no-await-in-loop -- Sequential LLM calls per file (rate limiting)\n const response = await llm.chatWithTools(\n [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ],\n {\n tools: [ARCHITECTURE_FINDING_TOOL],\n temperature: 0.2, // Low temp for consistency\n }\n );\n\n // Validate response structure\n if (!response || typeof response !== 'object') {\n useLogger()?.debug(`[ArchitectureAnalyzer] Invalid response from LLM for ${file.path}`);\n continue;\n }\n\n // Track analytics (useful for monitoring)\n if (analytics && response.usage) {\n // eslint-disable-next-line no-await-in-loop -- Analytics tracking after each file\n await analytics.track('review.architecture.complete', {\n file: file.path,\n tokensUsed: (response.usage.promptTokens ?? 0) + (response.usage.completionTokens ?? 0),\n toolCalls: response.toolCalls?.length ?? 0,\n });\n }\n\n // Process tool calls into findings (structured, no parsing!)\n const fileFindings = this.processToolCalls(response.toolCalls || [], file);\n findings.push(...fileFindings);\n\n // Cache results (24 hours)\n if (cache) {\n // eslint-disable-next-line no-await-in-loop -- Cache storage after analysis\n await cache.set(cacheKey, fileFindings, 86400000);\n }\n } catch (error) {\n // Log detailed error information for debugging\n const errorMessage = error instanceof Error ? error.message : String(error);\n const errorName = error instanceof Error ? error.name : 'UnknownError';\n\n useLogger()?.debug(`[ArchitectureAnalyzer] Failed to analyze ${file.path}:`, {\n error,\n errorName,\n errorMessage,\n });\n continue;\n }\n }\n\n return findings;\n }\n\n /**\n * Process LLM tool calls into ReviewFindings\n */\n \n private processToolCalls(toolCalls: any[], file: ParsedFile): ReviewFinding[] {\n const findings: ReviewFinding[] = [];\n\n for (const call of toolCalls) {\n if (call.name === 'report_architecture_finding') {\n \n const args = call.input as any;\n\n // Validate line number\n const lineCount = file.content.split('\\n').length;\n const line = typeof args.line === 'number' ? args.line : parseInt(args.line, 10);\n if (isNaN(line) || line < 1 || line > lineCount) {\n useLogger()?.debug(`[ArchitectureAnalyzer] Invalid line number ${args.line} for ${file.path}`);\n continue;\n }\n\n // Map and validate severity\n const severity = mapSeverity(String(args.severity || 'warning'));\n\n findings.push({\n id: this.buildFindingId(file, line, 'arch'),\n ruleId: 'llm:architecture',\n type: 'architecture',\n severity,\n confidence: 'likely', // LLM findings are \"likely\", not \"certain\"\n\n file: file.path,\n line,\n\n message: String(args.message || '').slice(0, 500),\n suggestion: args.suggestion ? String(args.suggestion).slice(0, 1000) : undefined,\n rationale: args.rationale ? String(args.rationale).slice(0, 500) : undefined,\n\n engine: 'llm',\n source: 'llm-architecture',\n\n // For agent mode gating\n scope: 'local', // Architecture can be local or global\n automated: false, // LLM suggestions need human review\n });\n }\n }\n\n return findings;\n }\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BaseLLMAnalyzer, ParsedFile, ReviewContext, ReviewFinding } from '@kb-labs/review-contracts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @module @kb-labs/review-llm/analyzers/naming-analyzer
|
|
5
|
+
* Naming conventions analyzer using LLM with tool calling
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Naming analyzer
|
|
10
|
+
* Uses LLM with tool calling for structured findings
|
|
11
|
+
*/
|
|
12
|
+
declare class NamingAnalyzer extends BaseLLMAnalyzer {
|
|
13
|
+
readonly id = "naming";
|
|
14
|
+
readonly name = "Naming Conventions";
|
|
15
|
+
analyze(files: ParsedFile[], context: ReviewContext): Promise<ReviewFinding[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Process LLM tool calls into ReviewFindings
|
|
18
|
+
*/
|
|
19
|
+
private processToolCalls;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export { NamingAnalyzer };
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { BaseLLMAnalyzer } from '@kb-labs/review-contracts';
|
|
2
|
+
import { useCache, useAnalytics, useLLM, useLogger } from '@kb-labs/sdk';
|
|
3
|
+
|
|
4
|
+
// src/analyzers/naming-analyzer.ts
|
|
5
|
+
|
|
6
|
+
// src/prompts/naming.prompts.ts
|
|
7
|
+
function buildSystemPrompt(context) {
|
|
8
|
+
const sections = [
|
|
9
|
+
"You are a code quality expert specializing in naming conventions and code clarity.",
|
|
10
|
+
"",
|
|
11
|
+
"**Your role:**",
|
|
12
|
+
"- Check adherence to naming conventions",
|
|
13
|
+
"- Identify unclear or misleading names",
|
|
14
|
+
"- Suggest improvements for readability",
|
|
15
|
+
"",
|
|
16
|
+
"**CRITICAL RULES:**",
|
|
17
|
+
"1. ONLY report names that violate project conventions",
|
|
18
|
+
"2. Reference line numbers that actually exist",
|
|
19
|
+
"3. Quote exact names when referencing issues",
|
|
20
|
+
"4. Provide concrete, better alternatives",
|
|
21
|
+
"5. Focus on clarity and consistency",
|
|
22
|
+
"",
|
|
23
|
+
"**Common issues:**",
|
|
24
|
+
"- Inconsistent casing (camelCase vs snake_case)",
|
|
25
|
+
"- Abbreviations without clear meaning",
|
|
26
|
+
"- Generic names (data, temp, obj)",
|
|
27
|
+
"- Misleading names (opposite of actual behavior)",
|
|
28
|
+
""
|
|
29
|
+
];
|
|
30
|
+
for (const [category, content] of Object.entries(context.conventions)) {
|
|
31
|
+
sections.push(`**Project Conventions (${category}):**`);
|
|
32
|
+
sections.push(content);
|
|
33
|
+
sections.push("");
|
|
34
|
+
}
|
|
35
|
+
if (context.taskContext) {
|
|
36
|
+
sections.push("**Current Task Context:**");
|
|
37
|
+
sections.push(context.taskContext);
|
|
38
|
+
sections.push("");
|
|
39
|
+
sections.push("IMPORTANT: Consider naming in the context of this specific task and its domain.");
|
|
40
|
+
sections.push("");
|
|
41
|
+
}
|
|
42
|
+
if (context.repoScope && context.repoScope.length > 0) {
|
|
43
|
+
sections.push("**Repositories in Scope:**");
|
|
44
|
+
sections.push(context.repoScope.map((r) => `- ${r}`).join("\n"));
|
|
45
|
+
sections.push("");
|
|
46
|
+
sections.push("NOTE: Ensure naming consistency across these repositories.");
|
|
47
|
+
sections.push("");
|
|
48
|
+
}
|
|
49
|
+
sections.push("Use the report_naming_finding tool to report naming issues.");
|
|
50
|
+
return sections.join("\n");
|
|
51
|
+
}
|
|
52
|
+
function analyzeFile(file) {
|
|
53
|
+
const lines = file.content.split("\n");
|
|
54
|
+
const numbered = lines.map((line, idx) => `${idx + 1}: ${line}`).join("\n");
|
|
55
|
+
return `Review this file for naming convention issues:
|
|
56
|
+
|
|
57
|
+
File: ${file.path}
|
|
58
|
+
Language: ${file.language}
|
|
59
|
+
|
|
60
|
+
\`\`\`${file.language}
|
|
61
|
+
${numbered}
|
|
62
|
+
\`\`\`
|
|
63
|
+
|
|
64
|
+
Report naming issues that reduce code clarity or violate conventions.`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/analyzers/naming-analyzer.ts
|
|
68
|
+
var NAMING_FINDING_TOOL = {
|
|
69
|
+
name: "report_naming_finding",
|
|
70
|
+
description: "Report a naming convention issue",
|
|
71
|
+
inputSchema: {
|
|
72
|
+
type: "object",
|
|
73
|
+
properties: {
|
|
74
|
+
severity: {
|
|
75
|
+
type: "string",
|
|
76
|
+
enum: ["warning", "info"],
|
|
77
|
+
description: "Severity level (naming issues are typically warning/info)"
|
|
78
|
+
},
|
|
79
|
+
currentName: {
|
|
80
|
+
type: "string",
|
|
81
|
+
description: "Current name that violates conventions"
|
|
82
|
+
},
|
|
83
|
+
suggestedName: {
|
|
84
|
+
type: "string",
|
|
85
|
+
description: "Suggested better name"
|
|
86
|
+
},
|
|
87
|
+
message: {
|
|
88
|
+
type: "string",
|
|
89
|
+
description: "Description of the naming issue"
|
|
90
|
+
},
|
|
91
|
+
line: {
|
|
92
|
+
type: "number",
|
|
93
|
+
description: "Line number where name appears"
|
|
94
|
+
},
|
|
95
|
+
rationale: {
|
|
96
|
+
type: "string",
|
|
97
|
+
description: "Why the current name is problematic"
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
required: ["severity", "currentName", "suggestedName", "message", "line", "rationale"]
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var NamingAnalyzer = class extends BaseLLMAnalyzer {
|
|
104
|
+
id = "naming";
|
|
105
|
+
name = "Naming Conventions";
|
|
106
|
+
async analyze(files, context) {
|
|
107
|
+
const findings = [];
|
|
108
|
+
const cache = useCache();
|
|
109
|
+
const analytics = useAnalytics();
|
|
110
|
+
for (const file of files) {
|
|
111
|
+
const cacheKey = this.generateCacheKey(file, context.preset);
|
|
112
|
+
if (cache) {
|
|
113
|
+
const cached = await cache.get(cacheKey);
|
|
114
|
+
if (cached) {
|
|
115
|
+
findings.push(...cached);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
const systemPrompt = buildSystemPrompt(context);
|
|
120
|
+
const userPrompt = analyzeFile(file);
|
|
121
|
+
const llm = useLLM({ tier: "small" });
|
|
122
|
+
if (!llm?.chatWithTools) {
|
|
123
|
+
throw new Error("LLM not configured or does not support tool calling");
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const response = await llm.chatWithTools(
|
|
127
|
+
[
|
|
128
|
+
{ role: "system", content: systemPrompt },
|
|
129
|
+
{ role: "user", content: userPrompt }
|
|
130
|
+
],
|
|
131
|
+
{
|
|
132
|
+
tools: [NAMING_FINDING_TOOL],
|
|
133
|
+
temperature: 0.2
|
|
134
|
+
// Low temp for consistency
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
if (analytics) {
|
|
138
|
+
await analytics.track("review.naming.complete", {
|
|
139
|
+
file: file.path,
|
|
140
|
+
tokensUsed: response.usage.promptTokens + response.usage.completionTokens,
|
|
141
|
+
toolCalls: response.toolCalls?.length ?? 0
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
const fileFindings = this.processToolCalls(response.toolCalls || [], file);
|
|
145
|
+
findings.push(...fileFindings);
|
|
146
|
+
if (cache) {
|
|
147
|
+
await cache.set(cacheKey, fileFindings, 864e5);
|
|
148
|
+
}
|
|
149
|
+
} catch (error) {
|
|
150
|
+
useLogger()?.debug(`[NamingAnalyzer] Failed to analyze ${file.path}:`, { error });
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return findings;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Process LLM tool calls into ReviewFindings
|
|
158
|
+
*/
|
|
159
|
+
processToolCalls(toolCalls, file) {
|
|
160
|
+
const findings = [];
|
|
161
|
+
for (const call of toolCalls) {
|
|
162
|
+
if (call.name === "report_naming_finding") {
|
|
163
|
+
const args = call.input;
|
|
164
|
+
const lineCount = file.content.split("\n").length;
|
|
165
|
+
const line = typeof args.line === "number" ? args.line : parseInt(args.line, 10);
|
|
166
|
+
if (isNaN(line) || line < 1 || line > lineCount) {
|
|
167
|
+
useLogger()?.debug(`[NamingAnalyzer] Invalid line number ${args.line} for ${file.path}`);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const severity = args.severity === "warning" ? "medium" : "info";
|
|
171
|
+
const currentName = String(args.currentName || "").slice(0, 100);
|
|
172
|
+
const suggestedName = String(args.suggestedName || "").slice(0, 100);
|
|
173
|
+
findings.push({
|
|
174
|
+
id: this.buildFindingId(file, line, "naming"),
|
|
175
|
+
ruleId: "llm:naming",
|
|
176
|
+
type: "style",
|
|
177
|
+
severity,
|
|
178
|
+
confidence: "heuristic",
|
|
179
|
+
// Naming is subjective, low confidence
|
|
180
|
+
file: file.path,
|
|
181
|
+
line,
|
|
182
|
+
message: String(args.message || "").slice(0, 500),
|
|
183
|
+
suggestion: `Rename '${currentName}' to '${suggestedName}'`,
|
|
184
|
+
rationale: args.rationale ? String(args.rationale).slice(0, 500) : void 0,
|
|
185
|
+
engine: "llm",
|
|
186
|
+
source: "llm-naming",
|
|
187
|
+
// For agent mode gating
|
|
188
|
+
scope: "local",
|
|
189
|
+
automated: false
|
|
190
|
+
// Naming suggestions are optional
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return findings;
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export { NamingAnalyzer };
|
|
199
|
+
//# sourceMappingURL=naming-analyzer.js.map
|
|
200
|
+
//# sourceMappingURL=naming-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/prompts/naming.prompts.ts","../../src/analyzers/naming-analyzer.ts"],"names":[],"mappings":";;;;;;AAUO,SAAS,kBAAkB,OAAA,EAAgC;AAChE,EAAA,MAAM,QAAA,GAAqB;AAAA,IACzB,oFAAA;AAAA,IACA,EAAA;AAAA,IACA,gBAAA;AAAA,IACA,yCAAA;AAAA,IACA,wCAAA;AAAA,IACA,wCAAA;AAAA,IACA,EAAA;AAAA,IACA,qBAAA;AAAA,IACA,uDAAA;AAAA,IACA,+CAAA;AAAA,IACA,8CAAA;AAAA,IACA,0CAAA;AAAA,IACA,qCAAA;AAAA,IACA,EAAA;AAAA,IACA,oBAAA;AAAA,IACA,iDAAA;AAAA,IACA,uCAAA;AAAA,IACA,mCAAA;AAAA,IACA,kDAAA;AAAA,IACA;AAAA,GACF;AAGA,EAAA,KAAA,MAAW,CAAC,UAAU,OAAO,CAAA,IAAK,OAAO,OAAA,CAAQ,OAAA,CAAQ,WAAW,CAAA,EAAG;AACrE,IAAA,QAAA,CAAS,IAAA,CAAK,CAAA,uBAAA,EAA0B,QAAQ,CAAA,IAAA,CAAM,CAAA;AACtD,IAAA,QAAA,CAAS,KAAK,OAAO,CAAA;AACrB,IAAA,QAAA,CAAS,KAAK,EAAE,CAAA;AAAA,EAClB;AAGA,EAAA,IAAI,QAAQ,WAAA,EAAa;AACvB,IAAA,QAAA,CAAS,KAAK,2BAA2B,CAAA;AACzC,IAAA,QAAA,CAAS,IAAA,CAAK,QAAQ,WAAW,CAAA;AACjC,IAAA,QAAA,CAAS,KAAK,EAAE,CAAA;AAChB,IAAA,QAAA,CAAS,KAAK,iFAAiF,CAAA;AAC/F,IAAA,QAAA,CAAS,KAAK,EAAE,CAAA;AAAA,EAClB;AAGA,EAAA,IAAI,OAAA,CAAQ,SAAA,IAAa,OAAA,CAAQ,SAAA,CAAU,SAAS,CAAA,EAAG;AACrD,IAAA,QAAA,CAAS,KAAK,4BAA4B,CAAA;AAC1C,IAAA,QAAA,CAAS,IAAA,CAAK,OAAA,CAAQ,SAAA,CAAU,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,EAAA,EAAK,CAAC,CAAA,CAAE,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAC/D,IAAA,QAAA,CAAS,KAAK,EAAE,CAAA;AAChB,IAAA,QAAA,CAAS,KAAK,4DAA4D,CAAA;AAC1E,IAAA,QAAA,CAAS,KAAK,EAAE,CAAA;AAAA,EAClB;AAEA,EAAA,QAAA,CAAS,KAAK,6DAA6D,CAAA;AAE3E,EAAA,OAAO,QAAA,CAAS,KAAK,IAAI,CAAA;AAC3B;AAKO,SAAS,YAAY,IAAA,EAA0B;AACpD,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,OAAA,CAAQ,KAAA,CAAM,IAAI,CAAA;AACrC,EAAA,MAAM,QAAA,GAAW,KAAA,CACd,GAAA,CAAI,CAAC,MAAM,GAAA,KAAQ,CAAA,EAAG,GAAA,GAAM,CAAC,CAAA,EAAA,EAAK,IAAI,CAAA,CAAE,CAAA,CACxC,KAAK,IAAI,CAAA;AAEZ,EAAA,OAAO,CAAA;;AAAA,MAAA,EACI,KAAK,IAAI;AAAA,UAAA,EACL,KAAK,QAAQ;;AAAA,MAAA,EACjB,KAAK,QAAQ;AAAA,EAAK,QAAQ;AAAA;;AAAA,qEAAA,CAAA;AAEvC;;;ACjEA,IAAM,mBAAA,GAAsB;AAAA,EAC1B,IAAA,EAAM,uBAAA;AAAA,EACN,WAAA,EAAa,kCAAA;AAAA,EACb,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,QAAA;AAAA,IACN,UAAA,EAAY;AAAA,MACV,QAAA,EAAU;AAAA,QACR,IAAA,EAAM,QAAA;AAAA,QACN,IAAA,EAAM,CAAC,SAAA,EAAW,MAAM,CAAA;AAAA,QACxB,WAAA,EAAa;AAAA,OACf;AAAA,MACA,WAAA,EAAa;AAAA,QACX,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,aAAA,EAAe;AAAA,QACb,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,OAAA,EAAS;AAAA,QACP,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,SAAA,EAAW;AAAA,QACT,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA;AACf,KACF;AAAA,IACA,UAAU,CAAC,UAAA,EAAY,eAAe,eAAA,EAAiB,SAAA,EAAW,QAAQ,WAAW;AAAA;AAEzF,CAAA;AAMO,IAAM,cAAA,GAAN,cAA6B,eAAA,CAAgB;AAAA,EACzC,EAAA,GAAK,QAAA;AAAA,EACL,IAAA,GAAO,oBAAA;AAAA,EAEhB,MAAM,OAAA,CAAQ,KAAA,EAAqB,OAAA,EAAkD;AACnF,IAAA,MAAM,WAA4B,EAAC;AACnC,IAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,IAAA,MAAM,YAAY,YAAA,EAAa;AAE/B,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AAExB,MAAA,MAAM,QAAA,GAAW,IAAA,CAAK,gBAAA,CAAiB,IAAA,EAAM,QAAQ,MAAM,CAAA;AAE3D,MAAA,IAAI,KAAA,EAAO;AAET,QAAA,MAAM,MAAA,GAAS,MAAM,KAAA,CAAM,GAAA,CAAqB,QAAQ,CAAA;AACxD,QAAA,IAAI,MAAA,EAAQ;AACV,UAAA,QAAA,CAAS,IAAA,CAAK,GAAG,MAAM,CAAA;AACvB,UAAA;AAAA,QACF;AAAA,MACF;AAGA,MAAA,MAAM,YAAA,GAA6B,kBAAkB,OAAO,CAAA;AAC5D,MAAA,MAAM,UAAA,GAA2B,YAAY,IAAI,CAAA;AAGjD,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,EAAE,IAAA,EAAM,SAAS,CAAA;AACpC,MAAA,IAAI,CAAC,KAAK,aAAA,EAAe;AACvB,QAAA,MAAM,IAAI,MAAM,qDAAqD,CAAA;AAAA,MACvE;AAEA,MAAA,IAAI;AAEF,QAAA,MAAM,QAAA,GAAW,MAAM,GAAA,CAAI,aAAA;AAAA,UACzB;AAAA,YACE,EAAE,IAAA,EAAM,QAAA,EAAU,OAAA,EAAS,YAAA,EAAa;AAAA,YACxC,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,UAAA;AAAW,WACtC;AAAA,UACA;AAAA,YACE,KAAA,EAAO,CAAC,mBAAmB,CAAA;AAAA,YAC3B,WAAA,EAAa;AAAA;AAAA;AACf,SACF;AAGA,QAAA,IAAI,SAAA,EAAW;AAEb,UAAA,MAAM,SAAA,CAAU,MAAM,wBAAA,EAA0B;AAAA,YAC9C,MAAM,IAAA,CAAK,IAAA;AAAA,YACX,UAAA,EAAY,QAAA,CAAS,KAAA,CAAM,YAAA,GAAe,SAAS,KAAA,CAAM,gBAAA;AAAA,YACzD,SAAA,EAAW,QAAA,CAAS,SAAA,EAAW,MAAA,IAAU;AAAA,WAC1C,CAAA;AAAA,QACH;AAGA,QAAA,MAAM,eAAe,IAAA,CAAK,gBAAA,CAAiB,SAAS,SAAA,IAAa,IAAI,IAAI,CAAA;AACzE,QAAA,QAAA,CAAS,IAAA,CAAK,GAAG,YAAY,CAAA;AAG7B,QAAA,IAAI,KAAA,EAAO;AAET,UAAA,MAAM,KAAA,CAAM,GAAA,CAAI,QAAA,EAAU,YAAA,EAAc,KAAQ,CAAA;AAAA,QAClD;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,SAAA,EAAU,EAAG,MAAM,CAAA,mCAAA,EAAsC,IAAA,CAAK,IAAI,CAAA,CAAA,CAAA,EAAK,EAAE,OAAO,CAAA;AAChF,QAAA;AAAA,MACF;AAAA,IACF;AAEA,IAAA,OAAO,QAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAMQ,gBAAA,CAAiB,WAAkB,IAAA,EAAmC;AAC5E,IAAA,MAAM,WAA4B,EAAC;AAEnC,IAAA,KAAA,MAAW,QAAQ,SAAA,EAAW;AAC5B,MAAA,IAAI,IAAA,CAAK,SAAS,uBAAA,EAAyB;AAEzC,QAAA,MAAM,OAAO,IAAA,CAAK,KAAA;AAGlB,QAAA,MAAM,SAAA,GAAY,IAAA,CAAK,OAAA,CAAQ,KAAA,CAAM,IAAI,CAAA,CAAE,MAAA;AAC3C,QAAA,MAAM,IAAA,GAAO,OAAO,IAAA,CAAK,IAAA,KAAS,QAAA,GAAW,KAAK,IAAA,GAAO,QAAA,CAAS,IAAA,CAAK,IAAA,EAAM,EAAE,CAAA;AAC/E,QAAA,IAAI,MAAM,IAAI,CAAA,IAAK,IAAA,GAAO,CAAA,IAAK,OAAO,SAAA,EAAW;AAC/C,UAAA,SAAA,EAAU,EAAG,MAAM,CAAA,qCAAA,EAAwC,IAAA,CAAK,IAAI,CAAA,KAAA,EAAQ,IAAA,CAAK,IAAI,CAAA,CAAE,CAAA;AACvF,UAAA;AAAA,QACF;AAGA,QAAA,MAAM,QAAA,GAAW,IAAA,CAAK,QAAA,KAAa,SAAA,GAAY,QAAA,GAAW,MAAA;AAG1D,QAAA,MAAM,WAAA,GAAc,OAAO,IAAA,CAAK,WAAA,IAAe,EAAE,CAAA,CAAE,KAAA,CAAM,GAAG,GAAG,CAAA;AAC/D,QAAA,MAAM,aAAA,GAAgB,OAAO,IAAA,CAAK,aAAA,IAAiB,EAAE,CAAA,CAAE,KAAA,CAAM,GAAG,GAAG,CAAA;AAEnE,QAAA,QAAA,CAAS,IAAA,CAAK;AAAA,UACZ,EAAA,EAAI,IAAA,CAAK,cAAA,CAAe,IAAA,EAAM,MAAM,QAAQ,CAAA;AAAA,UAC5C,MAAA,EAAQ,YAAA;AAAA,UACR,IAAA,EAAM,OAAA;AAAA,UACN,QAAA;AAAA,UACA,UAAA,EAAY,WAAA;AAAA;AAAA,UAEZ,MAAM,IAAA,CAAK,IAAA;AAAA,UACX,IAAA;AAAA,UAEA,OAAA,EAAS,OAAO,IAAA,CAAK,OAAA,IAAW,EAAE,CAAA,CAAE,KAAA,CAAM,GAAG,GAAG,CAAA;AAAA,UAChD,UAAA,EAAY,CAAA,QAAA,EAAW,WAAW,CAAA,MAAA,EAAS,aAAa,CAAA,CAAA,CAAA;AAAA,UACxD,SAAA,EAAW,IAAA,CAAK,SAAA,GAAY,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA,GAAI,MAAA;AAAA,UAEnE,MAAA,EAAQ,KAAA;AAAA,UACR,MAAA,EAAQ,YAAA;AAAA;AAAA,UAGR,KAAA,EAAO,OAAA;AAAA,UACP,SAAA,EAAW;AAAA;AAAA,SACZ,CAAA;AAAA,MACH;AAAA,IACF;AAEA,IAAA,OAAO,QAAA;AAAA,EACT;AACF","file":"naming-analyzer.js","sourcesContent":["/**\n * @module @kb-labs/review-llm/prompts/naming\n * Naming conventions review prompts\n */\n\nimport type { ReviewContext, ParsedFile } from '@kb-labs/review-contracts';\n\n/**\n * Build system prompt with context\n */\nexport function buildSystemPrompt(context: ReviewContext): string {\n const sections: string[] = [\n 'You are a code quality expert specializing in naming conventions and code clarity.',\n '',\n '**Your role:**',\n '- Check adherence to naming conventions',\n '- Identify unclear or misleading names',\n '- Suggest improvements for readability',\n '',\n '**CRITICAL RULES:**',\n '1. ONLY report names that violate project conventions',\n '2. Reference line numbers that actually exist',\n '3. Quote exact names when referencing issues',\n '4. Provide concrete, better alternatives',\n '5. Focus on clarity and consistency',\n '',\n '**Common issues:**',\n '- Inconsistent casing (camelCase vs snake_case)',\n '- Abbreviations without clear meaning',\n '- Generic names (data, temp, obj)',\n '- Misleading names (opposite of actual behavior)',\n '',\n ];\n\n // Add all project conventions\n for (const [category, content] of Object.entries(context.conventions)) {\n sections.push(`**Project Conventions (${category}):**`);\n sections.push(content);\n sections.push('');\n }\n\n // Add task context (if reviewing a specific task)\n if (context.taskContext) {\n sections.push('**Current Task Context:**');\n sections.push(context.taskContext);\n sections.push('');\n sections.push('IMPORTANT: Consider naming in the context of this specific task and its domain.');\n sections.push('');\n }\n\n // Add repo scope (if multi-repo review)\n if (context.repoScope && context.repoScope.length > 0) {\n sections.push('**Repositories in Scope:**');\n sections.push(context.repoScope.map((r) => `- ${r}`).join('\\n'));\n sections.push('');\n sections.push('NOTE: Ensure naming consistency across these repositories.');\n sections.push('');\n }\n\n sections.push('Use the report_naming_finding tool to report naming issues.');\n\n return sections.join('\\n');\n}\n\n/**\n * Build user prompt for file analysis\n */\nexport function analyzeFile(file: ParsedFile): string {\n const lines = file.content.split('\\n');\n const numbered = lines\n .map((line, idx) => `${idx + 1}: ${line}`)\n .join('\\n');\n\n return `Review this file for naming convention issues:\\n\\n` +\n `File: ${file.path}\\n` +\n `Language: ${file.language}\\n\\n` +\n `\\`\\`\\`${file.language}\\n${numbered}\\n\\`\\`\\`\\n\\n` +\n `Report naming issues that reduce code clarity or violate conventions.`;\n}\n","/**\n * @module @kb-labs/review-llm/analyzers/naming-analyzer\n * Naming conventions analyzer using LLM with tool calling\n */\n\nimport type { ReviewFinding, ParsedFile, ReviewContext } from '@kb-labs/review-contracts';\nimport { BaseLLMAnalyzer } from '@kb-labs/review-contracts';\nimport { useLLM, useCache, useAnalytics, useLogger } from '@kb-labs/sdk';\nimport * as namingPrompts from '../prompts/naming.prompts.js';\n\n/**\n * Tool schema for naming findings (structured output)\n */\nconst NAMING_FINDING_TOOL = {\n name: 'report_naming_finding',\n description: 'Report a naming convention issue',\n inputSchema: {\n type: 'object',\n properties: {\n severity: {\n type: 'string',\n enum: ['warning', 'info'],\n description: 'Severity level (naming issues are typically warning/info)',\n },\n currentName: {\n type: 'string',\n description: 'Current name that violates conventions',\n },\n suggestedName: {\n type: 'string',\n description: 'Suggested better name',\n },\n message: {\n type: 'string',\n description: 'Description of the naming issue',\n },\n line: {\n type: 'number',\n description: 'Line number where name appears',\n },\n rationale: {\n type: 'string',\n description: 'Why the current name is problematic',\n },\n },\n required: ['severity', 'currentName', 'suggestedName', 'message', 'line', 'rationale'],\n },\n} as const;\n\n/**\n * Naming analyzer\n * Uses LLM with tool calling for structured findings\n */\nexport class NamingAnalyzer extends BaseLLMAnalyzer {\n readonly id = 'naming';\n readonly name = 'Naming Conventions';\n\n async analyze(files: ParsedFile[], context: ReviewContext): Promise<ReviewFinding[]> {\n const findings: ReviewFinding[] = [];\n const cache = useCache();\n const analytics = useAnalytics();\n\n for (const file of files) {\n // Check cache first\n const cacheKey = this.generateCacheKey(file, context.preset);\n\n if (cache) {\n // eslint-disable-next-line no-await-in-loop -- Sequential file analysis with caching\n const cached = await cache.get<ReviewFinding[]>(cacheKey);\n if (cached) {\n findings.push(...cached);\n continue;\n }\n }\n\n // Build prompts\n const systemPrompt = namingPrompts.buildSystemPrompt(context);\n const userPrompt = namingPrompts.analyzeFile(file);\n\n // Use LLM with tool calling\n const llm = useLLM({ tier: 'small' }); // Naming is simpler, can use small model\n if (!llm?.chatWithTools) {\n throw new Error('LLM not configured or does not support tool calling');\n }\n\n try {\n // eslint-disable-next-line no-await-in-loop -- Sequential LLM calls per file (rate limiting)\n const response = await llm.chatWithTools(\n [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ],\n {\n tools: [NAMING_FINDING_TOOL],\n temperature: 0.2, // Low temp for consistency\n }\n );\n\n // Track analytics\n if (analytics) {\n // eslint-disable-next-line no-await-in-loop -- Analytics tracking after each file\n await analytics.track('review.naming.complete', {\n file: file.path,\n tokensUsed: response.usage.promptTokens + response.usage.completionTokens,\n toolCalls: response.toolCalls?.length ?? 0,\n });\n }\n\n // Process tool calls\n const fileFindings = this.processToolCalls(response.toolCalls || [], file);\n findings.push(...fileFindings);\n\n // Cache results (24 hours)\n if (cache) {\n // eslint-disable-next-line no-await-in-loop -- Cache storage after analysis\n await cache.set(cacheKey, fileFindings, 86400000);\n }\n } catch (error) {\n useLogger()?.debug(`[NamingAnalyzer] Failed to analyze ${file.path}:`, { error });\n continue;\n }\n }\n\n return findings;\n }\n\n /**\n * Process LLM tool calls into ReviewFindings\n */\n \n private processToolCalls(toolCalls: any[], file: ParsedFile): ReviewFinding[] {\n const findings: ReviewFinding[] = [];\n\n for (const call of toolCalls) {\n if (call.name === 'report_naming_finding') {\n \n const args = call.input as any;\n\n // Validate line number\n const lineCount = file.content.split('\\n').length;\n const line = typeof args.line === 'number' ? args.line : parseInt(args.line, 10);\n if (isNaN(line) || line < 1 || line > lineCount) {\n useLogger()?.debug(`[NamingAnalyzer] Invalid line number ${args.line} for ${file.path}`);\n continue;\n }\n\n // Validate severity (naming issues are warning/info only)\n const severity = args.severity === 'warning' ? 'medium' : 'info';\n\n // Sanitize names for display\n const currentName = String(args.currentName || '').slice(0, 100);\n const suggestedName = String(args.suggestedName || '').slice(0, 100);\n\n findings.push({\n id: this.buildFindingId(file, line, 'naming'),\n ruleId: 'llm:naming',\n type: 'style',\n severity,\n confidence: 'heuristic', // Naming is subjective, low confidence\n\n file: file.path,\n line,\n\n message: String(args.message || '').slice(0, 500),\n suggestion: `Rename '${currentName}' to '${suggestedName}'`,\n rationale: args.rationale ? String(args.rationale).slice(0, 500) : undefined,\n\n engine: 'llm',\n source: 'llm-naming',\n\n // For agent mode gating\n scope: 'local',\n automated: false, // Naming suggestions are optional\n });\n }\n }\n\n return findings;\n }\n}\n"]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { BaseLLMAnalyzer, ParsedFile, ReviewContext, ReviewFinding } from '@kb-labs/review-contracts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @module @kb-labs/review-llm/analyzers/security-analyzer
|
|
5
|
+
* Security vulnerability analyzer using LLM with tool calling
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Security analyzer
|
|
10
|
+
* Uses LLM with tool calling for structured findings
|
|
11
|
+
*/
|
|
12
|
+
declare class SecurityAnalyzer extends BaseLLMAnalyzer {
|
|
13
|
+
readonly id = "security";
|
|
14
|
+
readonly name = "Security Analysis";
|
|
15
|
+
/** Track failed files for reporting */
|
|
16
|
+
private failedFiles;
|
|
17
|
+
analyze(files: ParsedFile[], context: ReviewContext): Promise<ReviewFinding[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Analyze a single file with retry logic
|
|
20
|
+
*/
|
|
21
|
+
private analyzeFile;
|
|
22
|
+
/**
|
|
23
|
+
* Get list of files that failed analysis
|
|
24
|
+
*/
|
|
25
|
+
getFailedFiles(): Array<{
|
|
26
|
+
file: string;
|
|
27
|
+
error: string;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Process LLM tool calls into ReviewFindings
|
|
31
|
+
*/
|
|
32
|
+
private processToolCalls;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { SecurityAnalyzer };
|