truss-code-review-mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +60 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +88 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/ai-review.d.ts +21 -0
- package/dist/lib/ai-review.d.ts.map +1 -0
- package/dist/lib/ai-review.js +185 -0
- package/dist/lib/ai-review.js.map +1 -0
- package/dist/lib/complexity.d.ts +20 -0
- package/dist/lib/complexity.d.ts.map +1 -0
- package/dist/lib/complexity.js +272 -0
- package/dist/lib/complexity.js.map +1 -0
- package/dist/lib/license.d.ts +15 -0
- package/dist/lib/license.d.ts.map +1 -0
- package/dist/lib/license.js +72 -0
- package/dist/lib/license.js.map +1 -0
- package/dist/lib/patterns.d.ts +27 -0
- package/dist/lib/patterns.d.ts.map +1 -0
- package/dist/lib/patterns.js +102 -0
- package/dist/lib/patterns.js.map +1 -0
- package/dist/tools/check-complexity.d.ts +6 -0
- package/dist/tools/check-complexity.d.ts.map +1 -0
- package/dist/tools/check-complexity.js +34 -0
- package/dist/tools/check-complexity.js.map +1 -0
- package/dist/tools/deep-review.d.ts +6 -0
- package/dist/tools/deep-review.d.ts.map +1 -0
- package/dist/tools/deep-review.js +58 -0
- package/dist/tools/deep-review.js.map +1 -0
- package/dist/tools/detect-antipatterns.d.ts +6 -0
- package/dist/tools/detect-antipatterns.d.ts.map +1 -0
- package/dist/tools/detect-antipatterns.js +44 -0
- package/dist/tools/detect-antipatterns.js.map +1 -0
- package/dist/tools/explain-code.d.ts +6 -0
- package/dist/tools/explain-code.d.ts.map +1 -0
- package/dist/tools/explain-code.js +56 -0
- package/dist/tools/explain-code.js.map +1 -0
- package/dist/tools/optimize-code.d.ts +6 -0
- package/dist/tools/optimize-code.d.ts.map +1 -0
- package/dist/tools/optimize-code.js +57 -0
- package/dist/tools/optimize-code.js.map +1 -0
- package/dist/tools/review-diff.d.ts +8 -0
- package/dist/tools/review-diff.d.ts.map +1 -0
- package/dist/tools/review-diff.js +333 -0
- package/dist/tools/review-diff.js.map +1 -0
- package/dist/tools/security-review.d.ts +6 -0
- package/dist/tools/security-review.d.ts.map +1 -0
- package/dist/tools/security-review.js +57 -0
- package/dist/tools/security-review.js.map +1 -0
- package/dist/tools/suggest-tests.d.ts +6 -0
- package/dist/tools/suggest-tests.d.ts.map +1 -0
- package/dist/tools/suggest-tests.js +78 -0
- package/dist/tools/suggest-tests.js.map +1 -0
- package/evals/eval-complexity.ts +202 -0
- package/evals/eval-review.ts +196 -0
- package/evals/run-evals.ts +51 -0
- package/glama.json +4 -0
- package/package.json +36 -0
- package/smithery.yaml +15 -0
- package/src/data/antipatterns/go.json +98 -0
- package/src/data/antipatterns/javascript.json +122 -0
- package/src/data/antipatterns/python.json +98 -0
- package/src/index.ts +109 -0
- package/src/lib/ai-review.ts +220 -0
- package/src/lib/complexity.ts +284 -0
- package/src/lib/license.ts +95 -0
- package/src/lib/patterns.ts +131 -0
- package/src/tools/check-complexity.ts +43 -0
- package/src/tools/deep-review.ts +67 -0
- package/src/tools/detect-antipatterns.ts +54 -0
- package/src/tools/explain-code.ts +65 -0
- package/src/tools/optimize-code.ts +66 -0
- package/src/tools/review-diff.ts +374 -0
- package/src/tools/security-review.ts +66 -0
- package/src/tools/suggest-tests.ts +90 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cyclomatic complexity calculator.
|
|
3
|
+
* Uses regex-based analysis (not AST) for JS/TS, Python, Go.
|
|
4
|
+
* Counts decision points: if, else if, for, while, switch case, &&, ||, ternary, catch, etc.
|
|
5
|
+
*/
|
|
6
|
+
function rateComplexity(complexity) {
|
|
7
|
+
if (complexity <= 5)
|
|
8
|
+
return 'simple';
|
|
9
|
+
if (complexity <= 10)
|
|
10
|
+
return 'moderate';
|
|
11
|
+
if (complexity <= 20)
|
|
12
|
+
return 'complex';
|
|
13
|
+
return 'very_complex';
|
|
14
|
+
}
|
|
15
|
+
// Count decision points in a block of code
|
|
16
|
+
function countDecisionPoints(code, language) {
|
|
17
|
+
let count = 0;
|
|
18
|
+
const lines = code.split('\n');
|
|
19
|
+
for (const line of lines) {
|
|
20
|
+
const trimmed = line.trim();
|
|
21
|
+
// Skip comments
|
|
22
|
+
if (language === 'python') {
|
|
23
|
+
if (trimmed.startsWith('#'))
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
if (trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*'))
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
// Count branching keywords
|
|
31
|
+
if (language === 'python') {
|
|
32
|
+
// Python decision points
|
|
33
|
+
if (/\bif\b/.test(trimmed) && !trimmed.startsWith('#'))
|
|
34
|
+
count++;
|
|
35
|
+
if (/\belif\b/.test(trimmed))
|
|
36
|
+
count++;
|
|
37
|
+
if (/\bfor\b/.test(trimmed))
|
|
38
|
+
count++;
|
|
39
|
+
if (/\bwhile\b/.test(trimmed))
|
|
40
|
+
count++;
|
|
41
|
+
if (/\bexcept\b/.test(trimmed))
|
|
42
|
+
count++;
|
|
43
|
+
// Boolean operators in conditions
|
|
44
|
+
const andMatches = trimmed.match(/\band\b/g);
|
|
45
|
+
if (andMatches)
|
|
46
|
+
count += andMatches.length;
|
|
47
|
+
const orMatches = trimmed.match(/\bor\b/g);
|
|
48
|
+
if (orMatches)
|
|
49
|
+
count += orMatches.length;
|
|
50
|
+
// Ternary (x if condition else y)
|
|
51
|
+
if (/\bif\b.*\belse\b/.test(trimmed) && !trimmed.startsWith('if') && !trimmed.startsWith('elif'))
|
|
52
|
+
count++;
|
|
53
|
+
}
|
|
54
|
+
else if (language === 'go') {
|
|
55
|
+
// Go decision points — count 'else if' separately from plain 'if'
|
|
56
|
+
if (/\belse\s+if\b/.test(trimmed)) {
|
|
57
|
+
count++; // else if
|
|
58
|
+
}
|
|
59
|
+
else if (/\bif\b/.test(trimmed)) {
|
|
60
|
+
count++; // plain if
|
|
61
|
+
}
|
|
62
|
+
if (/\bfor\b/.test(trimmed))
|
|
63
|
+
count++;
|
|
64
|
+
if (/\bcase\b/.test(trimmed) && !trimmed.startsWith('//'))
|
|
65
|
+
count++;
|
|
66
|
+
// Catch (Go doesn't have catch, but has recover pattern)
|
|
67
|
+
// Boolean operators
|
|
68
|
+
const andMatches = trimmed.match(/&&/g);
|
|
69
|
+
if (andMatches)
|
|
70
|
+
count += andMatches.length;
|
|
71
|
+
const orMatches = trimmed.match(/\|\|/g);
|
|
72
|
+
if (orMatches)
|
|
73
|
+
count += orMatches.length;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
// JavaScript / TypeScript decision points
|
|
77
|
+
// Count 'else if' separately — don't double-count with plain 'if'
|
|
78
|
+
if (/\belse\s+if\s*\(/.test(trimmed)) {
|
|
79
|
+
count++; // else if
|
|
80
|
+
}
|
|
81
|
+
else if (/\bif\s*\(/.test(trimmed)) {
|
|
82
|
+
count++; // plain if
|
|
83
|
+
}
|
|
84
|
+
if (/\bfor\s*\(/.test(trimmed))
|
|
85
|
+
count++;
|
|
86
|
+
if (/\bwhile\s*\(/.test(trimmed))
|
|
87
|
+
count++;
|
|
88
|
+
if (/\bcase\s+/.test(trimmed))
|
|
89
|
+
count++;
|
|
90
|
+
if (/\bcatch\s*\(/.test(trimmed))
|
|
91
|
+
count++;
|
|
92
|
+
// Ternary operator (? :) — count ? that aren't part of optional chaining (?.)
|
|
93
|
+
const ternaryMatches = trimmed.match(/\?\s*(?!\.)/g);
|
|
94
|
+
if (ternaryMatches)
|
|
95
|
+
count += ternaryMatches.length;
|
|
96
|
+
// Boolean operators
|
|
97
|
+
const andMatches = trimmed.match(/&&/g);
|
|
98
|
+
if (andMatches)
|
|
99
|
+
count += andMatches.length;
|
|
100
|
+
const orMatches = trimmed.match(/\|\|/g);
|
|
101
|
+
if (orMatches)
|
|
102
|
+
count += orMatches.length;
|
|
103
|
+
// Nullish coalescing doesn't count as a decision point
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return count;
|
|
107
|
+
}
|
|
108
|
+
// Extract functions from JavaScript/TypeScript
|
|
109
|
+
function extractJsFunctions(code) {
|
|
110
|
+
const lines = code.split('\n');
|
|
111
|
+
const functions = [];
|
|
112
|
+
const funcPatterns = [
|
|
113
|
+
// function declarations: function name(...)
|
|
114
|
+
/(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*\(/,
|
|
115
|
+
// arrow functions assigned: const name = (...) =>
|
|
116
|
+
/(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>/,
|
|
117
|
+
// method definitions: name(...) { or async name(...)
|
|
118
|
+
/^\s*(?:async\s+)?(\w+)\s*\([^)]*\)\s*(?::\s*\w+[<>\[\],\s]*)?\s*\{/,
|
|
119
|
+
// class method: public/private/protected name(
|
|
120
|
+
/^\s*(?:public|private|protected|static|readonly|async|get|set)\s+(\w+)\s*\(/,
|
|
121
|
+
];
|
|
122
|
+
for (let i = 0; i < lines.length; i++) {
|
|
123
|
+
const line = lines[i];
|
|
124
|
+
for (const pattern of funcPatterns) {
|
|
125
|
+
const match = line.match(pattern);
|
|
126
|
+
if (match && match[1] && match[1] !== 'if' && match[1] !== 'for' && match[1] !== 'while' && match[1] !== 'switch') {
|
|
127
|
+
// Find the function body by tracking braces
|
|
128
|
+
let braceCount = 0;
|
|
129
|
+
let started = false;
|
|
130
|
+
let endLine = i;
|
|
131
|
+
for (let j = i; j < lines.length; j++) {
|
|
132
|
+
for (const ch of lines[j]) {
|
|
133
|
+
if (ch === '{') {
|
|
134
|
+
braceCount++;
|
|
135
|
+
started = true;
|
|
136
|
+
}
|
|
137
|
+
else if (ch === '}') {
|
|
138
|
+
braceCount--;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
if (started && braceCount <= 0) {
|
|
142
|
+
endLine = j;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// For arrow functions without braces, body is just the line
|
|
147
|
+
if (!started) {
|
|
148
|
+
endLine = i;
|
|
149
|
+
// Check next few lines for the expression
|
|
150
|
+
for (let j = i; j < Math.min(i + 5, lines.length); j++) {
|
|
151
|
+
if (lines[j].includes(';') || lines[j].trim() === '') {
|
|
152
|
+
endLine = j;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
endLine = j;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const body = lines.slice(i, endLine + 1).join('\n');
|
|
159
|
+
functions.push({ name: match[1], startLine: i + 1, body, endLine: endLine + 1 });
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return functions;
|
|
165
|
+
}
|
|
166
|
+
// Extract functions from Python
|
|
167
|
+
function extractPythonFunctions(code) {
|
|
168
|
+
const lines = code.split('\n');
|
|
169
|
+
const functions = [];
|
|
170
|
+
for (let i = 0; i < lines.length; i++) {
|
|
171
|
+
const match = lines[i].match(/^(\s*)(?:async\s+)?def\s+(\w+)\s*\(/);
|
|
172
|
+
if (match) {
|
|
173
|
+
const indent = match[1].length;
|
|
174
|
+
const name = match[2];
|
|
175
|
+
let endLine = i;
|
|
176
|
+
// Find end of function by indentation
|
|
177
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
178
|
+
const trimmed = lines[j].trim();
|
|
179
|
+
if (trimmed === '' || trimmed.startsWith('#')) {
|
|
180
|
+
endLine = j;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
const lineIndent = lines[j].match(/^(\s*)/)?.[1].length ?? 0;
|
|
184
|
+
if (lineIndent <= indent && trimmed !== '') {
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
endLine = j;
|
|
188
|
+
}
|
|
189
|
+
const body = lines.slice(i, endLine + 1).join('\n');
|
|
190
|
+
functions.push({ name, startLine: i + 1, body, endLine: endLine + 1 });
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return functions;
|
|
194
|
+
}
|
|
195
|
+
// Extract functions from Go
|
|
196
|
+
function extractGoFunctions(code) {
|
|
197
|
+
const lines = code.split('\n');
|
|
198
|
+
const functions = [];
|
|
199
|
+
for (let i = 0; i < lines.length; i++) {
|
|
200
|
+
const match = lines[i].match(/^func\s+(?:\(\s*\w+\s+\*?\w+\s*\)\s+)?(\w+)\s*\(/);
|
|
201
|
+
if (match) {
|
|
202
|
+
const name = match[1];
|
|
203
|
+
let braceCount = 0;
|
|
204
|
+
let started = false;
|
|
205
|
+
let endLine = i;
|
|
206
|
+
for (let j = i; j < lines.length; j++) {
|
|
207
|
+
for (const ch of lines[j]) {
|
|
208
|
+
if (ch === '{') {
|
|
209
|
+
braceCount++;
|
|
210
|
+
started = true;
|
|
211
|
+
}
|
|
212
|
+
else if (ch === '}') {
|
|
213
|
+
braceCount--;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (started && braceCount <= 0) {
|
|
217
|
+
endLine = j;
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const body = lines.slice(i, endLine + 1).join('\n');
|
|
222
|
+
functions.push({ name, startLine: i + 1, body, endLine: endLine + 1 });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return functions;
|
|
226
|
+
}
|
|
227
|
+
export function calculateComplexity(code, language) {
|
|
228
|
+
const lang = language.toLowerCase().replace('typescript', 'javascript').replace('ts', 'javascript').replace('jsx', 'javascript').replace('tsx', 'javascript');
|
|
229
|
+
let extractedFunctions;
|
|
230
|
+
if (lang === 'python' || lang === 'py') {
|
|
231
|
+
extractedFunctions = extractPythonFunctions(code);
|
|
232
|
+
}
|
|
233
|
+
else if (lang === 'go' || lang === 'golang') {
|
|
234
|
+
extractedFunctions = extractGoFunctions(code);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
// Default to JS/TS extraction
|
|
238
|
+
extractedFunctions = extractJsFunctions(code);
|
|
239
|
+
}
|
|
240
|
+
const effectiveLang = (lang === 'py') ? 'python' : (lang === 'golang') ? 'go' : lang;
|
|
241
|
+
const functions = extractedFunctions.map(fn => {
|
|
242
|
+
// Base complexity is 1 (the function itself is one path)
|
|
243
|
+
const complexity = 1 + countDecisionPoints(fn.body, effectiveLang);
|
|
244
|
+
return {
|
|
245
|
+
name: fn.name,
|
|
246
|
+
startLine: fn.startLine,
|
|
247
|
+
endLine: fn.endLine,
|
|
248
|
+
complexity,
|
|
249
|
+
rating: rateComplexity(complexity),
|
|
250
|
+
};
|
|
251
|
+
});
|
|
252
|
+
// If no functions found, analyze the entire code as one unit
|
|
253
|
+
if (functions.length === 0) {
|
|
254
|
+
const complexity = 1 + countDecisionPoints(code, effectiveLang);
|
|
255
|
+
functions.push({
|
|
256
|
+
name: '<module>',
|
|
257
|
+
startLine: 1,
|
|
258
|
+
endLine: code.split('\n').length,
|
|
259
|
+
complexity,
|
|
260
|
+
rating: rateComplexity(complexity),
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
const overall = functions.reduce((sum, f) => sum + f.complexity, 0);
|
|
264
|
+
const averagePerFunction = functions.length > 0 ? Math.round((overall / functions.length) * 10) / 10 : 0;
|
|
265
|
+
return {
|
|
266
|
+
functions,
|
|
267
|
+
overall,
|
|
268
|
+
averagePerFunction,
|
|
269
|
+
rating: rateComplexity(averagePerFunction),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=complexity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"complexity.js","sourceRoot":"","sources":["../../src/lib/complexity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TRUSS License validation.
|
|
3
|
+
* Free tier: no key needed.
|
|
4
|
+
* Pro tier: requires TRUSS_LICENSE_KEY env var.
|
|
5
|
+
*/
|
|
6
|
+
export interface LicenseStatus {
|
|
7
|
+
tier: 'free' | 'pro';
|
|
8
|
+
valid: boolean;
|
|
9
|
+
expiresAt: string | null;
|
|
10
|
+
}
|
|
11
|
+
export declare function getLicenseStatus(): Promise<LicenseStatus>;
|
|
12
|
+
export declare function requirePro(): Promise<void>;
|
|
13
|
+
export declare function hasAiKey(): boolean;
|
|
14
|
+
export declare function requireAiKey(): void;
|
|
15
|
+
//# sourceMappingURL=license.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"license.d.ts","sourceRoot":"","sources":["../../src/lib/license.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAqCD,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC,CAyB/D;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAQhD;AAED,wBAAgB,QAAQ,IAAI,OAAO,CAElC;AAED,wBAAgB,YAAY,IAAI,IAAI,CAMnC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TRUSS License validation.
|
|
3
|
+
* Free tier: no key needed.
|
|
4
|
+
* Pro tier: requires TRUSS_LICENSE_KEY env var.
|
|
5
|
+
*/
|
|
6
|
+
const TRUSS_API_BASE = 'https://api.truss.dev/v1/license';
|
|
7
|
+
function isValidKeyFormat(key) {
|
|
8
|
+
return /^truss_[0-9a-f]{32}$/.test(key);
|
|
9
|
+
}
|
|
10
|
+
let cachedStatus = null;
|
|
11
|
+
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
12
|
+
async function validateRemote(key) {
|
|
13
|
+
try {
|
|
14
|
+
const response = await fetch(`${TRUSS_API_BASE}/validate/${key}`, {
|
|
15
|
+
method: 'GET',
|
|
16
|
+
headers: {
|
|
17
|
+
'Accept': 'application/json',
|
|
18
|
+
'User-Agent': 'truss-code-review-mcp/1.0.0',
|
|
19
|
+
},
|
|
20
|
+
signal: AbortSignal.timeout(5000),
|
|
21
|
+
});
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
return { valid: false, expiresAt: null };
|
|
24
|
+
}
|
|
25
|
+
const body = await response.json();
|
|
26
|
+
return {
|
|
27
|
+
valid: body.valid === true,
|
|
28
|
+
expiresAt: body.expires_at ?? null,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// Network error — fall back to format check
|
|
33
|
+
return { valid: isValidKeyFormat(key), expiresAt: null };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function getLicenseStatus() {
|
|
37
|
+
const key = process.env.TRUSS_LICENSE_KEY ?? '';
|
|
38
|
+
if (!key) {
|
|
39
|
+
return { tier: 'free', valid: true, expiresAt: null };
|
|
40
|
+
}
|
|
41
|
+
if (!isValidKeyFormat(key)) {
|
|
42
|
+
return { tier: 'free', valid: false, expiresAt: null };
|
|
43
|
+
}
|
|
44
|
+
// Check in-memory cache
|
|
45
|
+
if (cachedStatus && cachedStatus.key === key && Date.now() - cachedStatus.timestamp < CACHE_TTL_MS) {
|
|
46
|
+
return cachedStatus.status;
|
|
47
|
+
}
|
|
48
|
+
const result = await validateRemote(key);
|
|
49
|
+
const status = {
|
|
50
|
+
tier: result.valid ? 'pro' : 'free',
|
|
51
|
+
valid: result.valid,
|
|
52
|
+
expiresAt: result.expiresAt,
|
|
53
|
+
};
|
|
54
|
+
cachedStatus = { key, status, timestamp: Date.now() };
|
|
55
|
+
return status;
|
|
56
|
+
}
|
|
57
|
+
export async function requirePro() {
|
|
58
|
+
const status = await getLicenseStatus();
|
|
59
|
+
if (status.tier !== 'pro') {
|
|
60
|
+
throw new Error('This feature requires a TRUSS Pro license ($25/mo). ' +
|
|
61
|
+
'Get yours at https://truss.dev/pricing and set TRUSS_LICENSE_KEY.');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function hasAiKey() {
|
|
65
|
+
return !!(process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY);
|
|
66
|
+
}
|
|
67
|
+
export function requireAiKey() {
|
|
68
|
+
if (!hasAiKey()) {
|
|
69
|
+
throw new Error('AI-powered features require an API key. Set ANTHROPIC_API_KEY or OPENAI_API_KEY in your environment.');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=license.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"license.js","sourceRoot":"","sources":["../../src/lib/license.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anti-pattern detection engine.
|
|
3
|
+
* Loads pattern databases per language and scans code.
|
|
4
|
+
*/
|
|
5
|
+
export interface AntiPattern {
|
|
6
|
+
id: string;
|
|
7
|
+
pattern: string;
|
|
8
|
+
name: string;
|
|
9
|
+
description: string;
|
|
10
|
+
severity: 'info' | 'warning' | 'error';
|
|
11
|
+
fix: string;
|
|
12
|
+
}
|
|
13
|
+
export interface PatternMatch {
|
|
14
|
+
pattern: string;
|
|
15
|
+
description: string;
|
|
16
|
+
location: {
|
|
17
|
+
line: number;
|
|
18
|
+
column: number;
|
|
19
|
+
text: string;
|
|
20
|
+
};
|
|
21
|
+
severity: 'info' | 'warning' | 'error';
|
|
22
|
+
fix: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function detectAntiPatterns(code: string, language: string): PatternMatch[];
|
|
25
|
+
export declare function getSupportedLanguages(): string[];
|
|
26
|
+
export declare function isLanguageSupported(language: string): boolean;
|
|
27
|
+
//# sourceMappingURL=patterns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../src/lib/patterns.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACvC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACvC,GAAG,EAAE,MAAM,CAAC;CACb;AAqCD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,YAAY,EAAE,CA2DjF;AAED,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE7D"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anti-pattern detection engine.
|
|
3
|
+
* Loads pattern databases per language and scans code.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
import { join, dirname } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
// Language aliases
|
|
10
|
+
const LANGUAGE_MAP = {
|
|
11
|
+
'javascript': 'javascript',
|
|
12
|
+
'js': 'javascript',
|
|
13
|
+
'jsx': 'javascript',
|
|
14
|
+
'typescript': 'javascript',
|
|
15
|
+
'ts': 'javascript',
|
|
16
|
+
'tsx': 'javascript',
|
|
17
|
+
'python': 'python',
|
|
18
|
+
'py': 'python',
|
|
19
|
+
'go': 'go',
|
|
20
|
+
'golang': 'go',
|
|
21
|
+
};
|
|
22
|
+
const patternCache = new Map();
|
|
23
|
+
function loadPatterns(language) {
|
|
24
|
+
const lang = LANGUAGE_MAP[language.toLowerCase()];
|
|
25
|
+
if (!lang)
|
|
26
|
+
return [];
|
|
27
|
+
if (patternCache.has(lang)) {
|
|
28
|
+
return patternCache.get(lang);
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const filePath = join(__dirname, '..', 'data', 'antipatterns', `${lang}.json`);
|
|
32
|
+
const raw = readFileSync(filePath, 'utf-8');
|
|
33
|
+
const patterns = JSON.parse(raw);
|
|
34
|
+
patternCache.set(lang, patterns);
|
|
35
|
+
return patterns;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function detectAntiPatterns(code, language) {
|
|
42
|
+
const patterns = loadPatterns(language);
|
|
43
|
+
if (patterns.length === 0) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
const lines = code.split('\n');
|
|
47
|
+
const matches = [];
|
|
48
|
+
for (const ap of patterns) {
|
|
49
|
+
let regex;
|
|
50
|
+
try {
|
|
51
|
+
regex = new RegExp(ap.pattern, 'gm');
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Invalid regex in pattern database — skip
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
// Test each line individually for line-level patterns
|
|
58
|
+
for (let i = 0; i < lines.length; i++) {
|
|
59
|
+
const line = lines[i];
|
|
60
|
+
const trimmed = line.trim();
|
|
61
|
+
// Skip empty lines and simple comments
|
|
62
|
+
if (!trimmed)
|
|
63
|
+
continue;
|
|
64
|
+
// Reset regex state
|
|
65
|
+
regex.lastIndex = 0;
|
|
66
|
+
let match;
|
|
67
|
+
while ((match = regex.exec(line)) !== null) {
|
|
68
|
+
matches.push({
|
|
69
|
+
pattern: ap.name,
|
|
70
|
+
description: ap.description,
|
|
71
|
+
location: {
|
|
72
|
+
line: i + 1,
|
|
73
|
+
column: match.index + 1,
|
|
74
|
+
text: trimmed,
|
|
75
|
+
},
|
|
76
|
+
severity: ap.severity,
|
|
77
|
+
fix: ap.fix,
|
|
78
|
+
});
|
|
79
|
+
// Avoid infinite loops on zero-length matches
|
|
80
|
+
if (match.index === regex.lastIndex) {
|
|
81
|
+
regex.lastIndex++;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Deduplicate: same pattern + same line = keep only first
|
|
87
|
+
const seen = new Set();
|
|
88
|
+
return matches.filter(m => {
|
|
89
|
+
const key = `${m.pattern}:${m.location.line}`;
|
|
90
|
+
if (seen.has(key))
|
|
91
|
+
return false;
|
|
92
|
+
seen.add(key);
|
|
93
|
+
return true;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
export function getSupportedLanguages() {
|
|
97
|
+
return [...new Set(Object.values(LANGUAGE_MAP))];
|
|
98
|
+
}
|
|
99
|
+
export function isLanguageSupported(language) {
|
|
100
|
+
return language.toLowerCase() in LANGUAGE_MAP;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/lib/patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* check_complexity — Cyclomatic complexity calculator (Free tier)
|
|
3
|
+
*/
|
|
4
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
+
export declare function registerCheckComplexity(server: McpServer): void;
|
|
6
|
+
//# sourceMappingURL=check-complexity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-complexity.d.ts","sourceRoot":"","sources":["../../src/tools/check-complexity.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIpE,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAkC/D"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* check_complexity — Cyclomatic complexity calculator (Free tier)
|
|
3
|
+
*/
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { calculateComplexity } from '../lib/complexity.js';
|
|
6
|
+
export function registerCheckComplexity(server) {
|
|
7
|
+
server.tool('check_complexity', 'Calculate cyclomatic complexity of functions/methods. Counts decision points: if, else if, for, while, switch case, &&, ||, ternary, catch. Supports JS/TS, Python, Go. Free tier.', {
|
|
8
|
+
code: z.string().describe('Source code to analyze'),
|
|
9
|
+
language: z.string().describe('Programming language (javascript, typescript, python, go)'),
|
|
10
|
+
}, async ({ code, language }) => {
|
|
11
|
+
const result = calculateComplexity(code, language);
|
|
12
|
+
const output = {
|
|
13
|
+
functions: result.functions.map(f => ({
|
|
14
|
+
name: f.name,
|
|
15
|
+
lines: `${f.startLine}-${f.endLine}`,
|
|
16
|
+
complexity: f.complexity,
|
|
17
|
+
rating: f.rating,
|
|
18
|
+
})),
|
|
19
|
+
overall: result.overall,
|
|
20
|
+
average_per_function: result.averagePerFunction,
|
|
21
|
+
rating: result.rating,
|
|
22
|
+
thresholds: {
|
|
23
|
+
simple: '1-5',
|
|
24
|
+
moderate: '6-10',
|
|
25
|
+
complex: '11-20',
|
|
26
|
+
very_complex: '21+',
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
content: [{ type: 'text', text: JSON.stringify(output, null, 2) }],
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=check-complexity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-complexity.js","sourceRoot":"","sources":["../../src/tools/check-complexity.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,UAAU,uBAAuB,CAAC,MAAiB;IACvD,MAAM,CAAC,IAAI,CACT,kBAAkB,EAClB,oLAAoL,EACpL;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wBAAwB,CAAC;QACnD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,2DAA2D,CAAC;KAC3F,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE;QAC3B,MAAM,MAAM,GAAG,mBAAmB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAEnD,MAAM,MAAM,GAAG;YACb,SAAS,EAAE,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBACpC,IAAI,EAAE,CAAC,CAAC,IAAI;gBACZ,KAAK,EAAE,GAAG,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,OAAO,EAAE;gBACpC,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,MAAM,EAAE,CAAC,CAAC,MAAM;aACjB,CAAC,CAAC;YACH,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,oBAAoB,EAAE,MAAM,CAAC,kBAAkB;YAC/C,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,UAAU,EAAE;gBACV,MAAM,EAAE,KAAK;gBACb,QAAQ,EAAE,MAAM;gBAChB,OAAO,EAAE,OAAO;gBAChB,YAAY,EAAE,KAAK;aACpB;SACF,CAAC;QAEF,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;SAC5E,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deep-review.d.ts","sourceRoot":"","sources":["../../src/tools/deep-review.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKpE,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAyD1D"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* deep_review — AI-powered comprehensive code review (Pro tier)
|
|
3
|
+
*/
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { requirePro, requireAiKey } from '../lib/license.js';
|
|
6
|
+
import { aiReview, DEEP_REVIEW_SYSTEM } from '../lib/ai-review.js';
|
|
7
|
+
export function registerDeepReview(server) {
|
|
8
|
+
server.tool('deep_review', 'AI-powered comprehensive code review covering architecture, security, performance, error handling, and maintainability. Returns scored findings with remediation. Pro tier — requires TRUSS_LICENSE_KEY + ANTHROPIC_API_KEY or OPENAI_API_KEY.', {
|
|
9
|
+
code: z.string().describe('Source code to review'),
|
|
10
|
+
language: z.string().describe('Programming language'),
|
|
11
|
+
context: z.string().optional().describe('Additional context (e.g., "this is a REST API handler", "performance-critical path")'),
|
|
12
|
+
}, async ({ code, language, context }) => {
|
|
13
|
+
await requirePro();
|
|
14
|
+
requireAiKey();
|
|
15
|
+
const userPrompt = [
|
|
16
|
+
`Language: ${language}`,
|
|
17
|
+
context ? `Context: ${context}` : '',
|
|
18
|
+
'',
|
|
19
|
+
'```',
|
|
20
|
+
code,
|
|
21
|
+
'```',
|
|
22
|
+
].filter(Boolean).join('\n');
|
|
23
|
+
try {
|
|
24
|
+
const result = await aiReview(DEEP_REVIEW_SYSTEM, userPrompt);
|
|
25
|
+
let parsed;
|
|
26
|
+
try {
|
|
27
|
+
parsed = JSON.parse(result.content);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
parsed = { raw_review: result.content };
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
content: [{
|
|
34
|
+
type: 'text',
|
|
35
|
+
text: JSON.stringify({
|
|
36
|
+
review: parsed,
|
|
37
|
+
meta: {
|
|
38
|
+
model: result.model,
|
|
39
|
+
provider: result.provider,
|
|
40
|
+
tokens: result.tokensUsed,
|
|
41
|
+
},
|
|
42
|
+
}, null, 2),
|
|
43
|
+
}],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
return {
|
|
48
|
+
content: [{
|
|
49
|
+
type: 'text',
|
|
50
|
+
text: JSON.stringify({
|
|
51
|
+
error: err instanceof Error ? err.message : String(err),
|
|
52
|
+
}),
|
|
53
|
+
}],
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=deep-review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deep-review.js","sourceRoot":"","sources":["../../src/tools/deep-review.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEnE,MAAM,UAAU,kBAAkB,CAAC,MAAiB;IAClD,MAAM,CAAC,IAAI,CACT,aAAa,EACb,gPAAgP,EAChP;QACE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,uBAAuB,CAAC;QAClD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;QACrD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sFAAsF,CAAC;KAChI,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,EAAE;QACpC,MAAM,UAAU,EAAE,CAAC;QACnB,YAAY,EAAE,CAAC;QAEf,MAAM,UAAU,GAAG;YACjB,aAAa,QAAQ,EAAE;YACvB,OAAO,CAAC,CAAC,CAAC,YAAY,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE;YACpC,EAAE;YACF,KAAK;YACL,IAAI;YACJ,KAAK;SACN,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE7B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;YAE9D,IAAI,MAAe,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACtC,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;YAC1C,CAAC;YAED,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,MAAM,EAAE,MAAM;4BACd,IAAI,EAAE;gCACJ,KAAK,EAAE,MAAM,CAAC,KAAK;gCACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gCACzB,MAAM,EAAE,MAAM,CAAC,UAAU;6BAC1B;yBACF,EAAE,IAAI,EAAE,CAAC,CAAC;qBACZ,CAAC;aACH,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO;gBACL,OAAO,EAAE,CAAC;wBACR,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;yBACxD,CAAC;qBACH,CAAC;aACH,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|