wiggum-cli 0.2.6 → 0.3.1
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 +73 -60
- package/dist/ai/agents/codebase-analyst.d.ts +11 -0
- package/dist/ai/agents/codebase-analyst.d.ts.map +1 -0
- package/dist/ai/agents/codebase-analyst.js +146 -0
- package/dist/ai/agents/codebase-analyst.js.map +1 -0
- package/dist/ai/agents/index.d.ts +16 -0
- package/dist/ai/agents/index.d.ts.map +1 -0
- package/dist/ai/agents/index.js +85 -0
- package/dist/ai/agents/index.js.map +1 -0
- package/dist/ai/agents/orchestrator.d.ts +15 -0
- package/dist/ai/agents/orchestrator.d.ts.map +1 -0
- package/dist/ai/agents/orchestrator.js +181 -0
- package/dist/ai/agents/orchestrator.js.map +1 -0
- package/dist/ai/agents/stack-researcher.d.ts +15 -0
- package/dist/ai/agents/stack-researcher.d.ts.map +1 -0
- package/dist/ai/agents/stack-researcher.js +269 -0
- package/dist/ai/agents/stack-researcher.js.map +1 -0
- package/dist/ai/agents/types.d.ts +123 -0
- package/dist/ai/agents/types.d.ts.map +1 -0
- package/dist/ai/agents/types.js +6 -0
- package/dist/ai/agents/types.js.map +1 -0
- package/dist/ai/enhancer.d.ts +39 -1
- package/dist/ai/enhancer.d.ts.map +1 -1
- package/dist/ai/enhancer.js +78 -36
- package/dist/ai/enhancer.js.map +1 -1
- package/dist/ai/index.d.ts +4 -2
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +5 -1
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/prompts.d.ts +2 -2
- package/dist/ai/prompts.d.ts.map +1 -1
- package/dist/ai/prompts.js +66 -4
- package/dist/ai/prompts.js.map +1 -1
- package/dist/ai/providers.d.ts +28 -0
- package/dist/ai/providers.d.ts.map +1 -1
- package/dist/ai/providers.js +40 -0
- package/dist/ai/providers.js.map +1 -1
- package/dist/ai/tools/context7.d.ts +34 -0
- package/dist/ai/tools/context7.d.ts.map +1 -0
- package/dist/ai/tools/context7.js +135 -0
- package/dist/ai/tools/context7.js.map +1 -0
- package/dist/ai/tools/index.d.ts +7 -0
- package/dist/ai/tools/index.d.ts.map +1 -0
- package/dist/ai/tools/index.js +7 -0
- package/dist/ai/tools/index.js.map +1 -0
- package/dist/ai/tools/tavily.d.ts +27 -0
- package/dist/ai/tools/tavily.d.ts.map +1 -0
- package/dist/ai/tools/tavily.js +75 -0
- package/dist/ai/tools/tavily.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +14 -12
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts +2 -5
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +233 -154
- package/dist/commands/init.js.map +1 -1
- package/dist/utils/colors.d.ts.map +1 -1
- package/dist/utils/colors.js +10 -3
- package/dist/utils/colors.js.map +1 -1
- package/dist/utils/header.d.ts +1 -1
- package/dist/utils/header.js +3 -3
- package/dist/utils/header.js.map +1 -1
- package/dist/utils/json-repair.d.ts +14 -0
- package/dist/utils/json-repair.d.ts.map +1 -0
- package/dist/utils/json-repair.js +103 -0
- package/dist/utils/json-repair.js.map +1 -0
- package/dist/utils/tracing.d.ts +25 -0
- package/dist/utils/tracing.d.ts.map +1 -0
- package/dist/utils/tracing.js +64 -0
- package/dist/utils/tracing.js.map +1 -0
- package/package.json +5 -3
- package/src/ai/agents/codebase-analyst.ts +169 -0
- package/src/ai/agents/index.ts +147 -0
- package/src/ai/agents/orchestrator.ts +218 -0
- package/src/ai/agents/stack-researcher.ts +294 -0
- package/src/ai/agents/types.ts +132 -0
- package/src/ai/enhancer.ts +128 -38
- package/src/ai/index.ts +31 -1
- package/src/ai/prompts.ts +67 -4
- package/src/ai/providers.ts +48 -0
- package/src/ai/tools/context7.ts +167 -0
- package/src/ai/tools/index.ts +17 -0
- package/src/ai/tools/tavily.ts +101 -0
- package/src/cli.ts +14 -12
- package/src/commands/init.ts +278 -173
- package/src/utils/colors.ts +11 -3
- package/src/utils/header.ts +3 -3
- package/src/utils/json-repair.ts +113 -0
- package/src/utils/tracing.ts +76 -0
package/src/ai/enhancer.ts
CHANGED
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
* Uses AI to analyze the codebase for deeper insights
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { stepCountIs } from 'ai';
|
|
7
7
|
import type { ScanResult, DetectedStack, DetectionResult } from '../scanner/types.js';
|
|
8
8
|
import { getModel, type AIProvider, hasApiKey, getApiKeyEnvVar, isReasoningModel } from './providers.js';
|
|
9
9
|
import { SYSTEM_PROMPT, SYSTEM_PROMPT_AGENTIC, createAnalysisPrompt } from './prompts.js';
|
|
10
10
|
import { createExplorationTools } from './tools.js';
|
|
11
|
+
import { runMultiAgentAnalysis, type MultiAgentAnalysis } from './agents/index.js';
|
|
11
12
|
import { logger } from '../utils/logger.js';
|
|
13
|
+
import { parseJsonSafe } from '../utils/json-repair.js';
|
|
14
|
+
import { getTracedAI, traced } from '../utils/tracing.js';
|
|
12
15
|
|
|
13
16
|
/**
|
|
14
17
|
* Project context from AI analysis - key structure information
|
|
@@ -44,6 +47,32 @@ export interface McpRecommendations {
|
|
|
44
47
|
recommended?: string[];
|
|
45
48
|
}
|
|
46
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Technology-specific testing and debugging tools
|
|
52
|
+
*/
|
|
53
|
+
export interface TechnologyTools {
|
|
54
|
+
/** Testing commands specific to the detected technologies */
|
|
55
|
+
testing?: string[];
|
|
56
|
+
/** Debugging/inspection tools for the stack */
|
|
57
|
+
debugging?: string[];
|
|
58
|
+
/** Linting/validation tools beyond the standard ones */
|
|
59
|
+
validation?: string[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Technology-specific best practices
|
|
64
|
+
*/
|
|
65
|
+
export interface TechnologyPractices {
|
|
66
|
+
/** The primary project type (e.g., "MCP Server", "REST API", "React SPA") */
|
|
67
|
+
projectType?: string;
|
|
68
|
+
/** Practices specific to the detected technologies */
|
|
69
|
+
practices?: string[];
|
|
70
|
+
/** Anti-patterns to avoid for this stack */
|
|
71
|
+
antiPatterns?: string[];
|
|
72
|
+
/** Links to relevant documentation (optional) */
|
|
73
|
+
documentationHints?: string[];
|
|
74
|
+
}
|
|
75
|
+
|
|
47
76
|
/**
|
|
48
77
|
* AI analysis result - focused on actionable outputs
|
|
49
78
|
*/
|
|
@@ -58,6 +87,10 @@ export interface AIAnalysisResult {
|
|
|
58
87
|
mcpServers?: McpRecommendations;
|
|
59
88
|
/** Additional technologies that may have been missed */
|
|
60
89
|
possibleMissedTechnologies?: string[];
|
|
90
|
+
/** Technology-specific tools for testing/debugging */
|
|
91
|
+
technologyTools?: TechnologyTools;
|
|
92
|
+
/** Technology-specific best practices based on detected stack */
|
|
93
|
+
technologyPractices?: TechnologyPractices;
|
|
61
94
|
}
|
|
62
95
|
|
|
63
96
|
/**
|
|
@@ -79,6 +112,10 @@ export interface EnhancerOptions {
|
|
|
79
112
|
verbose?: boolean;
|
|
80
113
|
/** Use agentic mode with tools for deeper codebase exploration */
|
|
81
114
|
agentic?: boolean;
|
|
115
|
+
/** Tavily API key for web search (optional) */
|
|
116
|
+
tavilyApiKey?: string;
|
|
117
|
+
/** Context7 API key for documentation lookup (optional) */
|
|
118
|
+
context7ApiKey?: string;
|
|
82
119
|
}
|
|
83
120
|
|
|
84
121
|
/**
|
|
@@ -92,46 +129,26 @@ function parseAIResponse(text: string, verbose: boolean = false): AIAnalysisResu
|
|
|
92
129
|
return null;
|
|
93
130
|
}
|
|
94
131
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
// The AI might wrap it in markdown code blocks
|
|
98
|
-
let jsonText = text;
|
|
99
|
-
|
|
100
|
-
// Remove markdown code blocks if present
|
|
101
|
-
const jsonMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
102
|
-
if (jsonMatch) {
|
|
103
|
-
jsonText = jsonMatch[1];
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Try to find JSON object - use greedy match for the outermost braces
|
|
107
|
-
// This handles cases where there's text before/after the JSON
|
|
108
|
-
const objectMatch = jsonText.match(/\{[\s\S]*\}/);
|
|
109
|
-
if (objectMatch) {
|
|
110
|
-
jsonText = objectMatch[0];
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Try to parse JSON
|
|
114
|
-
const result = JSON.parse(jsonText) as AIAnalysisResult;
|
|
132
|
+
// Use safe JSON parser with repair capabilities
|
|
133
|
+
const result = parseJsonSafe<AIAnalysisResult>(text);
|
|
115
134
|
|
|
116
|
-
|
|
117
|
-
if (
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
return null;
|
|
135
|
+
if (!result) {
|
|
136
|
+
if (verbose) {
|
|
137
|
+
logger.warn('Failed to parse AI response as JSON');
|
|
138
|
+
logger.warn(`Response preview: ${text.substring(0, 500)}...`);
|
|
122
139
|
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
123
142
|
|
|
124
|
-
|
|
125
|
-
|
|
143
|
+
// Validate that we got the expected structure
|
|
144
|
+
if (typeof result !== 'object') {
|
|
126
145
|
if (verbose) {
|
|
127
|
-
logger.warn(
|
|
128
|
-
// Log first 500 chars of response for debugging
|
|
129
|
-
logger.warn(`Response preview: ${text.substring(0, 500)}...`);
|
|
130
|
-
} else {
|
|
131
|
-
logger.warn('Failed to parse AI response as JSON');
|
|
146
|
+
logger.warn('AI response parsed but is not an object');
|
|
132
147
|
}
|
|
133
148
|
return null;
|
|
134
149
|
}
|
|
150
|
+
|
|
151
|
+
return result;
|
|
135
152
|
}
|
|
136
153
|
|
|
137
154
|
/**
|
|
@@ -173,12 +190,16 @@ export class AIEnhancer {
|
|
|
173
190
|
private model?: string;
|
|
174
191
|
private verbose: boolean;
|
|
175
192
|
private agentic: boolean;
|
|
193
|
+
private tavilyApiKey?: string;
|
|
194
|
+
private context7ApiKey?: string;
|
|
176
195
|
|
|
177
196
|
constructor(options: EnhancerOptions = {}) {
|
|
178
197
|
this.provider = options.provider || 'anthropic';
|
|
179
198
|
this.model = options.model;
|
|
180
199
|
this.verbose = options.verbose || false;
|
|
181
200
|
this.agentic = options.agentic || false;
|
|
201
|
+
this.tavilyApiKey = options.tavilyApiKey;
|
|
202
|
+
this.context7ApiKey = options.context7ApiKey;
|
|
182
203
|
}
|
|
183
204
|
|
|
184
205
|
/**
|
|
@@ -271,6 +292,7 @@ export class AIEnhancer {
|
|
|
271
292
|
scanResult: ScanResult
|
|
272
293
|
): Promise<AIAnalysisResult | null> {
|
|
273
294
|
const prompt = createAnalysisPrompt(scanResult);
|
|
295
|
+
const { generateText } = getTracedAI();
|
|
274
296
|
|
|
275
297
|
const { text } = await generateText({
|
|
276
298
|
model,
|
|
@@ -279,20 +301,54 @@ export class AIEnhancer {
|
|
|
279
301
|
maxOutputTokens: 2000,
|
|
280
302
|
// Reasoning models don't support temperature
|
|
281
303
|
...(isReasoningModel(modelId) ? {} : { temperature: 0.3 }),
|
|
304
|
+
experimental_telemetry: {
|
|
305
|
+
isEnabled: true,
|
|
306
|
+
metadata: { phase: 'simple-analysis', projectRoot: scanResult.projectRoot },
|
|
307
|
+
},
|
|
282
308
|
});
|
|
283
309
|
|
|
284
310
|
return parseAIResponse(text);
|
|
285
311
|
}
|
|
286
312
|
|
|
287
313
|
/**
|
|
288
|
-
* Agentic enhancement mode - use
|
|
314
|
+
* Agentic enhancement mode - use multi-agent system to explore codebase
|
|
289
315
|
*/
|
|
290
316
|
private async enhanceAgentic(
|
|
291
317
|
model: ReturnType<typeof getModel>['model'],
|
|
292
318
|
modelId: string,
|
|
293
319
|
scanResult: ScanResult
|
|
320
|
+
): Promise<AIAnalysisResult | null> {
|
|
321
|
+
// Use the multi-agent system for deeper analysis
|
|
322
|
+
const multiAgentResult = await runMultiAgentAnalysis(
|
|
323
|
+
model,
|
|
324
|
+
modelId,
|
|
325
|
+
scanResult,
|
|
326
|
+
{
|
|
327
|
+
tavilyApiKey: this.tavilyApiKey,
|
|
328
|
+
context7ApiKey: this.context7ApiKey,
|
|
329
|
+
verbose: this.verbose,
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
if (!multiAgentResult) {
|
|
334
|
+
// Fall back to simple agentic mode
|
|
335
|
+
return this.enhanceLegacyAgentic(model, modelId, scanResult);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Convert MultiAgentAnalysis to AIAnalysisResult for backward compatibility
|
|
339
|
+
return convertMultiAgentToAIAnalysis(multiAgentResult);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Legacy agentic mode (fallback when multi-agent fails)
|
|
344
|
+
*/
|
|
345
|
+
private async enhanceLegacyAgentic(
|
|
346
|
+
model: ReturnType<typeof getModel>['model'],
|
|
347
|
+
modelId: string,
|
|
348
|
+
scanResult: ScanResult
|
|
294
349
|
): Promise<AIAnalysisResult | null> {
|
|
295
350
|
const tools = createExplorationTools(scanResult.projectRoot);
|
|
351
|
+
const { generateText } = getTracedAI();
|
|
296
352
|
|
|
297
353
|
const prompt = `Analyze this codebase and produce configuration for AI-assisted development.
|
|
298
354
|
|
|
@@ -325,7 +381,6 @@ When done exploring, output your final analysis as valid JSON matching this stru
|
|
|
325
381
|
}`;
|
|
326
382
|
|
|
327
383
|
// Use agentic loop - AI will call tools until it has enough info
|
|
328
|
-
// stopWhen: stepCountIs(10) allows up to 10 tool-calling steps
|
|
329
384
|
const result = await generateText({
|
|
330
385
|
model,
|
|
331
386
|
system: SYSTEM_PROMPT_AGENTIC,
|
|
@@ -333,8 +388,11 @@ When done exploring, output your final analysis as valid JSON matching this stru
|
|
|
333
388
|
tools,
|
|
334
389
|
stopWhen: stepCountIs(10),
|
|
335
390
|
maxOutputTokens: 4000,
|
|
336
|
-
// Reasoning models don't support temperature
|
|
337
391
|
...(isReasoningModel(modelId) ? {} : { temperature: 0.3 }),
|
|
392
|
+
experimental_telemetry: {
|
|
393
|
+
isEnabled: true,
|
|
394
|
+
metadata: { phase: 'legacy-agentic', projectRoot: scanResult.projectRoot },
|
|
395
|
+
},
|
|
338
396
|
});
|
|
339
397
|
|
|
340
398
|
// Try to get text from the result
|
|
@@ -346,7 +404,6 @@ When done exploring, output your final analysis as valid JSON matching this stru
|
|
|
346
404
|
logger.info(`No direct text output, checking ${result.steps?.length || 0} steps...`);
|
|
347
405
|
}
|
|
348
406
|
|
|
349
|
-
// Look through steps for text content (from last to first)
|
|
350
407
|
const steps = result.steps || [];
|
|
351
408
|
for (let i = steps.length - 1; i >= 0; i--) {
|
|
352
409
|
const step = steps[i];
|
|
@@ -368,6 +425,39 @@ When done exploring, output your final analysis as valid JSON matching this stru
|
|
|
368
425
|
}
|
|
369
426
|
}
|
|
370
427
|
|
|
428
|
+
/**
|
|
429
|
+
* Convert MultiAgentAnalysis to AIAnalysisResult for backward compatibility
|
|
430
|
+
*/
|
|
431
|
+
function convertMultiAgentToAIAnalysis(multiAgent: MultiAgentAnalysis): AIAnalysisResult {
|
|
432
|
+
const { codebaseAnalysis, stackResearch, mcpServers } = multiAgent;
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
projectContext: {
|
|
436
|
+
entryPoints: codebaseAnalysis.projectContext.entryPoints,
|
|
437
|
+
keyDirectories: codebaseAnalysis.projectContext.keyDirectories,
|
|
438
|
+
namingConventions: codebaseAnalysis.projectContext.namingConventions,
|
|
439
|
+
},
|
|
440
|
+
commands: codebaseAnalysis.commands,
|
|
441
|
+
implementationGuidelines: codebaseAnalysis.implementationGuidelines,
|
|
442
|
+
mcpServers: {
|
|
443
|
+
essential: mcpServers.essential,
|
|
444
|
+
recommended: mcpServers.recommended,
|
|
445
|
+
},
|
|
446
|
+
possibleMissedTechnologies: codebaseAnalysis.possibleMissedTechnologies,
|
|
447
|
+
technologyTools: {
|
|
448
|
+
testing: stackResearch.testingTools,
|
|
449
|
+
debugging: stackResearch.debuggingTools,
|
|
450
|
+
validation: [],
|
|
451
|
+
},
|
|
452
|
+
technologyPractices: {
|
|
453
|
+
projectType: codebaseAnalysis.projectContext.projectType,
|
|
454
|
+
practices: stackResearch.bestPractices,
|
|
455
|
+
antiPatterns: stackResearch.antiPatterns,
|
|
456
|
+
documentationHints: stackResearch.documentationHints,
|
|
457
|
+
},
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
371
461
|
/**
|
|
372
462
|
* Convenience function to enhance scan results with AI
|
|
373
463
|
*/
|
package/src/ai/index.ts
CHANGED
|
@@ -7,10 +7,17 @@
|
|
|
7
7
|
export {
|
|
8
8
|
type AIProvider,
|
|
9
9
|
type ProviderConfig,
|
|
10
|
+
type OptionalService,
|
|
10
11
|
getModel,
|
|
11
12
|
hasApiKey,
|
|
12
13
|
getAvailableProvider,
|
|
13
14
|
getApiKeyEnvVar,
|
|
15
|
+
OPTIONAL_SERVICE_ENV_VARS,
|
|
16
|
+
hasTavilyKey,
|
|
17
|
+
getTavilyKey,
|
|
18
|
+
hasContext7Key,
|
|
19
|
+
getContext7Key,
|
|
20
|
+
getOptionalServicesStatus,
|
|
14
21
|
} from './providers.js';
|
|
15
22
|
|
|
16
23
|
// Analysis prompts
|
|
@@ -29,11 +36,34 @@ export {
|
|
|
29
36
|
RIPGREP_SKILL,
|
|
30
37
|
} from './tools.js';
|
|
31
38
|
|
|
39
|
+
// New tools (Tavily, Context7)
|
|
40
|
+
export {
|
|
41
|
+
createTavilySearchTool,
|
|
42
|
+
canUseTavily,
|
|
43
|
+
createContext7Tool,
|
|
44
|
+
canUseContext7,
|
|
45
|
+
} from './tools/index.js';
|
|
46
|
+
|
|
47
|
+
// Agents
|
|
48
|
+
export {
|
|
49
|
+
runMultiAgentAnalysis,
|
|
50
|
+
runCodebaseAnalyst,
|
|
51
|
+
runStackResearcher,
|
|
52
|
+
runOrchestrator,
|
|
53
|
+
mergeAgentResults,
|
|
54
|
+
type CodebaseAnalysis,
|
|
55
|
+
type StackResearch,
|
|
56
|
+
type McpRecommendations,
|
|
57
|
+
type MultiAgentAnalysis,
|
|
58
|
+
type AgentCapabilities,
|
|
59
|
+
type AgentOptions,
|
|
60
|
+
} from './agents/index.js';
|
|
61
|
+
|
|
32
62
|
// AI enhancer
|
|
33
63
|
export {
|
|
34
64
|
type ProjectContext,
|
|
35
65
|
type DetectedCommands,
|
|
36
|
-
type McpRecommendations,
|
|
66
|
+
type McpRecommendations as McpRecommendationsLegacy,
|
|
37
67
|
type AIAnalysisResult,
|
|
38
68
|
type EnhancedScanResult,
|
|
39
69
|
type EnhancerOptions,
|
package/src/ai/prompts.ts
CHANGED
|
@@ -100,6 +100,8 @@ Your goal is to thoroughly understand the codebase structure and produce actiona
|
|
|
100
100
|
3. Search for key patterns: entry points, routes, components, tests
|
|
101
101
|
4. Identify naming conventions by examining existing files
|
|
102
102
|
5. Look for existing documentation (.md files, README)
|
|
103
|
+
6. Determine the PROJECT TYPE (e.g., MCP server, REST API, React SPA, CLI tool, library)
|
|
104
|
+
7. Based on project type, include TECHNOLOGY-SPECIFIC testing/debugging tools
|
|
103
105
|
|
|
104
106
|
## Tools Available
|
|
105
107
|
You have these tools to explore the codebase:
|
|
@@ -110,6 +112,30 @@ You have these tools to explore the codebase:
|
|
|
110
112
|
|
|
111
113
|
${RIPGREP_SKILL}
|
|
112
114
|
|
|
115
|
+
## Technology-Specific Guidance
|
|
116
|
+
|
|
117
|
+
When you detect specific project types, include their specialized tools:
|
|
118
|
+
|
|
119
|
+
**MCP Server Projects** (detected by @modelcontextprotocol dependencies):
|
|
120
|
+
- Testing: "npx @anthropic-ai/mcp-inspector" for interactive debugging
|
|
121
|
+
- Practices: Follow MCP protocol spec, validate tool schemas, handle resources properly
|
|
122
|
+
|
|
123
|
+
**REST APIs** (Express, Fastify, Hono, etc.):
|
|
124
|
+
- Testing: API testing tools (supertest, httpie, curl examples)
|
|
125
|
+
- Debugging: Request logging, OpenAPI validation
|
|
126
|
+
|
|
127
|
+
**React/Next.js Projects**:
|
|
128
|
+
- Testing: React Testing Library patterns, Storybook for components
|
|
129
|
+
- Debugging: React DevTools, component isolation
|
|
130
|
+
|
|
131
|
+
**CLI Tools**:
|
|
132
|
+
- Testing: Integration tests with actual CLI invocation
|
|
133
|
+
- Debugging: --verbose flags, debug logging patterns
|
|
134
|
+
|
|
135
|
+
**Libraries/Packages**:
|
|
136
|
+
- Testing: Unit tests with high coverage, type checking
|
|
137
|
+
- Practices: Semantic versioning, changelog maintenance
|
|
138
|
+
|
|
113
139
|
## Output Requirements
|
|
114
140
|
After exploration, output valid JSON with:
|
|
115
141
|
- projectContext: entry points, key directories, naming conventions
|
|
@@ -117,8 +143,11 @@ After exploration, output valid JSON with:
|
|
|
117
143
|
- implementationGuidelines: short actionable rules (5-10 words each, max 7)
|
|
118
144
|
- mcpServers: essential and recommended servers
|
|
119
145
|
- possibleMissedTechnologies: technologies that might be in use
|
|
146
|
+
- technologyTools: testing, debugging, and validation tools specific to this project type
|
|
147
|
+
- technologyPractices: projectType, practices, antiPatterns, documentationHints
|
|
120
148
|
|
|
121
|
-
Be concise. Focus on WHAT TO DO, not what exists
|
|
149
|
+
Be concise. Focus on WHAT TO DO, not what exists.
|
|
150
|
+
Include SPECIFIC testing/debugging commands for the detected project type.`;
|
|
122
151
|
|
|
123
152
|
/**
|
|
124
153
|
* System prompt for codebase analysis (simple mode - no tools)
|
|
@@ -133,7 +162,16 @@ Rules:
|
|
|
133
162
|
- Focus on WHAT TO DO, not what exists
|
|
134
163
|
- Include specific file paths and commands
|
|
135
164
|
- Max 5-7 items per array
|
|
136
|
-
- No explanations, just actionable rules
|
|
165
|
+
- No explanations, just actionable rules
|
|
166
|
+
- CRITICAL: Include technology-specific testing and debugging tools
|
|
167
|
+
- Identify the PROJECT TYPE and provide stack-specific practices
|
|
168
|
+
|
|
169
|
+
Technology-specific tools to consider:
|
|
170
|
+
- MCP servers: "npx @anthropic-ai/mcp-inspector" for testing
|
|
171
|
+
- REST APIs: supertest, curl examples, OpenAPI validation
|
|
172
|
+
- React apps: React Testing Library, Storybook, DevTools
|
|
173
|
+
- CLI tools: integration tests, --verbose flags
|
|
174
|
+
- Libraries: high coverage unit tests, semantic versioning`;
|
|
137
175
|
|
|
138
176
|
/**
|
|
139
177
|
* Create the codebase analysis prompt
|
|
@@ -175,16 +213,41 @@ Respond with this JSON structure (keep values SHORT - 5-10 words max per item):
|
|
|
175
213
|
"essential": ["filesystem", "git"],
|
|
176
214
|
"recommended": ["docker", "postgres"]
|
|
177
215
|
},
|
|
178
|
-
"possibleMissedTechnologies": ["Redis", "WebSockets"]
|
|
216
|
+
"possibleMissedTechnologies": ["Redis", "WebSockets"],
|
|
217
|
+
"technologyTools": {
|
|
218
|
+
"testing": ["npx @anthropic-ai/mcp-inspector", "node test/test-*.js"],
|
|
219
|
+
"debugging": ["--verbose flag", "DEBUG=* env var"],
|
|
220
|
+
"validation": ["npx tsc --noEmit", "npm run lint"]
|
|
221
|
+
},
|
|
222
|
+
"technologyPractices": {
|
|
223
|
+
"projectType": "MCP Server",
|
|
224
|
+
"practices": [
|
|
225
|
+
"Validate tool input schemas with Zod",
|
|
226
|
+
"Return structured JSON from tools",
|
|
227
|
+
"Handle errors with proper MCP error codes"
|
|
228
|
+
],
|
|
229
|
+
"antiPatterns": [
|
|
230
|
+
"Don't expose internal errors to clients",
|
|
231
|
+
"Avoid blocking operations in tool handlers"
|
|
232
|
+
],
|
|
233
|
+
"documentationHints": [
|
|
234
|
+
"MCP spec: modelcontextprotocol.io/docs",
|
|
235
|
+
"Inspector: modelcontextprotocol.io/docs/tools/inspector"
|
|
236
|
+
]
|
|
237
|
+
}
|
|
179
238
|
}
|
|
180
239
|
|
|
181
|
-
|
|
240
|
+
CRITICAL:
|
|
241
|
+
- Identify the PROJECT TYPE first (MCP Server, REST API, React SPA, CLI, Library, etc.)
|
|
242
|
+
- Include technology-specific testing tools (e.g., MCP Inspector for MCP projects)
|
|
243
|
+
- Include technology-specific debugging approaches
|
|
182
244
|
- implementationGuidelines should be short rules (not analysis prompts)
|
|
183
245
|
- Include actual file paths from this project
|
|
184
246
|
- Infer commands from package.json patterns
|
|
185
247
|
- Max 5-7 items per array`;
|
|
186
248
|
}
|
|
187
249
|
|
|
250
|
+
|
|
188
251
|
/**
|
|
189
252
|
* Prompt for validating and improving scanner results
|
|
190
253
|
*/
|
package/src/ai/providers.ts
CHANGED
|
@@ -30,6 +30,16 @@ const API_KEY_ENV_VARS: Record<AIProvider, string> = {
|
|
|
30
30
|
openrouter: 'OPENROUTER_API_KEY',
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Environment variable names for optional services
|
|
35
|
+
*/
|
|
36
|
+
export const OPTIONAL_SERVICE_ENV_VARS = {
|
|
37
|
+
tavily: 'TAVILY_API_KEY',
|
|
38
|
+
context7: 'CONTEXT7_API_KEY',
|
|
39
|
+
} as const;
|
|
40
|
+
|
|
41
|
+
export type OptionalService = keyof typeof OPTIONAL_SERVICE_ENV_VARS;
|
|
42
|
+
|
|
33
43
|
/**
|
|
34
44
|
* Model option with label and value
|
|
35
45
|
*/
|
|
@@ -184,3 +194,41 @@ const REASONING_MODELS = [
|
|
|
184
194
|
export function isReasoningModel(modelId: string): boolean {
|
|
185
195
|
return REASONING_MODELS.some(m => modelId.startsWith(m));
|
|
186
196
|
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Check if Tavily API key is available
|
|
200
|
+
*/
|
|
201
|
+
export function hasTavilyKey(): boolean {
|
|
202
|
+
return !!process.env[OPTIONAL_SERVICE_ENV_VARS.tavily];
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get the Tavily API key
|
|
207
|
+
*/
|
|
208
|
+
export function getTavilyKey(): string | undefined {
|
|
209
|
+
return process.env[OPTIONAL_SERVICE_ENV_VARS.tavily];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Check if Context7 API key is available
|
|
214
|
+
*/
|
|
215
|
+
export function hasContext7Key(): boolean {
|
|
216
|
+
return !!process.env[OPTIONAL_SERVICE_ENV_VARS.context7];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Get the Context7 API key
|
|
221
|
+
*/
|
|
222
|
+
export function getContext7Key(): string | undefined {
|
|
223
|
+
return process.env[OPTIONAL_SERVICE_ENV_VARS.context7];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Get the status of all optional services
|
|
228
|
+
*/
|
|
229
|
+
export function getOptionalServicesStatus(): Record<OptionalService, boolean> {
|
|
230
|
+
return {
|
|
231
|
+
tavily: hasTavilyKey(),
|
|
232
|
+
context7: hasContext7Key(),
|
|
233
|
+
};
|
|
234
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context7 Documentation Lookup Tool
|
|
3
|
+
* Enables documentation lookup for libraries and frameworks
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { tool, zodSchema } from 'ai';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Context7 library info
|
|
11
|
+
*/
|
|
12
|
+
export interface Context7Library {
|
|
13
|
+
id: string;
|
|
14
|
+
name: string;
|
|
15
|
+
description?: string;
|
|
16
|
+
codeSnippetCount?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Context7 documentation result
|
|
21
|
+
*/
|
|
22
|
+
export interface Context7DocResult {
|
|
23
|
+
title: string;
|
|
24
|
+
content: string;
|
|
25
|
+
codeExamples?: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a Context7 documentation lookup tool
|
|
30
|
+
* @param apiKey - Context7 API key
|
|
31
|
+
*/
|
|
32
|
+
export function createContext7Tool(apiKey: string) {
|
|
33
|
+
return tool({
|
|
34
|
+
description: `Look up documentation for libraries and frameworks.
|
|
35
|
+
Use this to find:
|
|
36
|
+
- API documentation for specific functions
|
|
37
|
+
- Usage examples and patterns
|
|
38
|
+
- Configuration options
|
|
39
|
+
- Best practices from official docs`,
|
|
40
|
+
inputSchema: zodSchema(z.object({
|
|
41
|
+
library: z.string().describe('Library name (e.g., "react", "express", "prisma")'),
|
|
42
|
+
query: z.string().describe('What you want to find in the documentation'),
|
|
43
|
+
})),
|
|
44
|
+
execute: async ({ library, query }) => {
|
|
45
|
+
try {
|
|
46
|
+
// First, resolve the library ID
|
|
47
|
+
const resolveResponse = await fetch('https://api.context7.com/v1/resolve-library-id', {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
headers: {
|
|
50
|
+
'Content-Type': 'application/json',
|
|
51
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
52
|
+
},
|
|
53
|
+
body: JSON.stringify({
|
|
54
|
+
libraryName: library,
|
|
55
|
+
query: query,
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (!resolveResponse.ok) {
|
|
60
|
+
// Try alternative endpoint structure
|
|
61
|
+
return await fallbackDocLookup(apiKey, library, query);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const resolveData = await resolveResponse.json() as { libraryId?: string; libraries?: Context7Library[] };
|
|
65
|
+
const libraryId = resolveData.libraryId || resolveData.libraries?.[0]?.id;
|
|
66
|
+
|
|
67
|
+
if (!libraryId) {
|
|
68
|
+
return `No documentation found for "${library}". Try a different library name.`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Query the documentation
|
|
72
|
+
const queryResponse = await fetch('https://api.context7.com/v1/query-docs', {
|
|
73
|
+
method: 'POST',
|
|
74
|
+
headers: {
|
|
75
|
+
'Content-Type': 'application/json',
|
|
76
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
77
|
+
},
|
|
78
|
+
body: JSON.stringify({
|
|
79
|
+
libraryId,
|
|
80
|
+
query,
|
|
81
|
+
}),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (!queryResponse.ok) {
|
|
85
|
+
const errorText = await queryResponse.text();
|
|
86
|
+
return `Documentation lookup failed: ${queryResponse.status} - ${errorText}`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const docsData = await queryResponse.json() as { results?: Context7DocResult[] };
|
|
90
|
+
|
|
91
|
+
// Format results
|
|
92
|
+
const results: string[] = [];
|
|
93
|
+
results.push(`Documentation for ${library}:`);
|
|
94
|
+
results.push('');
|
|
95
|
+
|
|
96
|
+
if (docsData.results && docsData.results.length > 0) {
|
|
97
|
+
for (const doc of docsData.results.slice(0, 3)) {
|
|
98
|
+
results.push(`## ${doc.title}`);
|
|
99
|
+
results.push(doc.content.substring(0, 500));
|
|
100
|
+
if (doc.codeExamples && doc.codeExamples.length > 0) {
|
|
101
|
+
results.push('');
|
|
102
|
+
results.push('Example:');
|
|
103
|
+
results.push('```');
|
|
104
|
+
results.push(doc.codeExamples[0].substring(0, 300));
|
|
105
|
+
results.push('```');
|
|
106
|
+
}
|
|
107
|
+
results.push('');
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
results.push(`No specific documentation found for query: "${query}"`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return results.join('\n');
|
|
114
|
+
} catch (error) {
|
|
115
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
116
|
+
return `Documentation lookup error: ${errMsg}`;
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Fallback documentation lookup using alternative approach
|
|
124
|
+
*/
|
|
125
|
+
async function fallbackDocLookup(apiKey: string, library: string, query: string): Promise<string> {
|
|
126
|
+
try {
|
|
127
|
+
// Try a simpler query format
|
|
128
|
+
const response = await fetch(`https://api.context7.com/v1/search`, {
|
|
129
|
+
method: 'POST',
|
|
130
|
+
headers: {
|
|
131
|
+
'Content-Type': 'application/json',
|
|
132
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
133
|
+
},
|
|
134
|
+
body: JSON.stringify({
|
|
135
|
+
query: `${library} ${query}`,
|
|
136
|
+
limit: 5,
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (!response.ok) {
|
|
141
|
+
return `Unable to find documentation for "${library}". The Context7 API may not support this library or the request format has changed.`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const data = await response.json() as { results?: Array<{ title: string; content: string }> };
|
|
145
|
+
|
|
146
|
+
if (data.results && data.results.length > 0) {
|
|
147
|
+
const results: string[] = [`Documentation for ${library}:`, ''];
|
|
148
|
+
for (const item of data.results.slice(0, 3)) {
|
|
149
|
+
results.push(`## ${item.title}`);
|
|
150
|
+
results.push(item.content.substring(0, 400));
|
|
151
|
+
results.push('');
|
|
152
|
+
}
|
|
153
|
+
return results.join('\n');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return `No documentation found for "${library}" with query "${query}"`;
|
|
157
|
+
} catch {
|
|
158
|
+
return `Documentation lookup for "${library}" failed. Please check your Context7 API key.`;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Check if Context7 can be used
|
|
164
|
+
*/
|
|
165
|
+
export function canUseContext7(apiKey?: string): boolean {
|
|
166
|
+
return !!apiKey && apiKey.length > 0;
|
|
167
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Tools Index
|
|
3
|
+
* Exports all tools for agent use
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
createTavilySearchTool,
|
|
8
|
+
canUseTavily,
|
|
9
|
+
type TavilySearchResult,
|
|
10
|
+
} from './tavily.js';
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
createContext7Tool,
|
|
14
|
+
canUseContext7,
|
|
15
|
+
type Context7Library,
|
|
16
|
+
type Context7DocResult,
|
|
17
|
+
} from './context7.js';
|