wiggum-cli 0.2.6 → 0.3.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.
Files changed (79) hide show
  1. package/README.md +73 -60
  2. package/dist/ai/agents/codebase-analyst.d.ts +11 -0
  3. package/dist/ai/agents/codebase-analyst.d.ts.map +1 -0
  4. package/dist/ai/agents/codebase-analyst.js +150 -0
  5. package/dist/ai/agents/codebase-analyst.js.map +1 -0
  6. package/dist/ai/agents/index.d.ts +16 -0
  7. package/dist/ai/agents/index.d.ts.map +1 -0
  8. package/dist/ai/agents/index.js +85 -0
  9. package/dist/ai/agents/index.js.map +1 -0
  10. package/dist/ai/agents/orchestrator.d.ts +15 -0
  11. package/dist/ai/agents/orchestrator.d.ts.map +1 -0
  12. package/dist/ai/agents/orchestrator.js +187 -0
  13. package/dist/ai/agents/orchestrator.js.map +1 -0
  14. package/dist/ai/agents/stack-researcher.d.ts +15 -0
  15. package/dist/ai/agents/stack-researcher.d.ts.map +1 -0
  16. package/dist/ai/agents/stack-researcher.js +274 -0
  17. package/dist/ai/agents/stack-researcher.js.map +1 -0
  18. package/dist/ai/agents/types.d.ts +123 -0
  19. package/dist/ai/agents/types.d.ts.map +1 -0
  20. package/dist/ai/agents/types.js +6 -0
  21. package/dist/ai/agents/types.js.map +1 -0
  22. package/dist/ai/enhancer.d.ts +39 -1
  23. package/dist/ai/enhancer.d.ts.map +1 -1
  24. package/dist/ai/enhancer.js +54 -4
  25. package/dist/ai/enhancer.js.map +1 -1
  26. package/dist/ai/index.d.ts +4 -2
  27. package/dist/ai/index.d.ts.map +1 -1
  28. package/dist/ai/index.js +5 -1
  29. package/dist/ai/index.js.map +1 -1
  30. package/dist/ai/prompts.d.ts +2 -2
  31. package/dist/ai/prompts.d.ts.map +1 -1
  32. package/dist/ai/prompts.js +66 -4
  33. package/dist/ai/prompts.js.map +1 -1
  34. package/dist/ai/providers.d.ts +28 -0
  35. package/dist/ai/providers.d.ts.map +1 -1
  36. package/dist/ai/providers.js +40 -0
  37. package/dist/ai/providers.js.map +1 -1
  38. package/dist/ai/tools/context7.d.ts +34 -0
  39. package/dist/ai/tools/context7.d.ts.map +1 -0
  40. package/dist/ai/tools/context7.js +135 -0
  41. package/dist/ai/tools/context7.js.map +1 -0
  42. package/dist/ai/tools/index.d.ts +7 -0
  43. package/dist/ai/tools/index.d.ts.map +1 -0
  44. package/dist/ai/tools/index.js +7 -0
  45. package/dist/ai/tools/index.js.map +1 -0
  46. package/dist/ai/tools/tavily.d.ts +27 -0
  47. package/dist/ai/tools/tavily.d.ts.map +1 -0
  48. package/dist/ai/tools/tavily.js +75 -0
  49. package/dist/ai/tools/tavily.js.map +1 -0
  50. package/dist/cli.d.ts.map +1 -1
  51. package/dist/cli.js +14 -12
  52. package/dist/cli.js.map +1 -1
  53. package/dist/commands/init.d.ts +2 -5
  54. package/dist/commands/init.d.ts.map +1 -1
  55. package/dist/commands/init.js +233 -154
  56. package/dist/commands/init.js.map +1 -1
  57. package/dist/utils/colors.d.ts.map +1 -1
  58. package/dist/utils/colors.js +10 -3
  59. package/dist/utils/colors.js.map +1 -1
  60. package/dist/utils/header.d.ts +1 -1
  61. package/dist/utils/header.js +3 -3
  62. package/dist/utils/header.js.map +1 -1
  63. package/package.json +3 -3
  64. package/src/ai/agents/codebase-analyst.ts +172 -0
  65. package/src/ai/agents/index.ts +147 -0
  66. package/src/ai/agents/orchestrator.ts +222 -0
  67. package/src/ai/agents/stack-researcher.ts +298 -0
  68. package/src/ai/agents/types.ts +132 -0
  69. package/src/ai/enhancer.ts +102 -4
  70. package/src/ai/index.ts +31 -1
  71. package/src/ai/prompts.ts +67 -4
  72. package/src/ai/providers.ts +48 -0
  73. package/src/ai/tools/context7.ts +167 -0
  74. package/src/ai/tools/index.ts +17 -0
  75. package/src/ai/tools/tavily.ts +101 -0
  76. package/src/cli.ts +14 -12
  77. package/src/commands/init.ts +278 -173
  78. package/src/utils/colors.ts +11 -3
  79. package/src/utils/header.ts +3 -3
@@ -0,0 +1,298 @@
1
+ /**
2
+ * Stack Researcher Agent
3
+ * Researches best practices and tools for the detected stack
4
+ * Gracefully degrades when optional services are unavailable
5
+ */
6
+
7
+ import { generateText, stepCountIs, type LanguageModel, type Tool } from 'ai';
8
+ import type { StackResearch, StackResearcherInput, AgentCapabilities } from './types.js';
9
+ import type { DetectedStack } from '../../scanner/types.js';
10
+ import { createTavilySearchTool } from '../tools/tavily.js';
11
+ import { createContext7Tool } from '../tools/context7.js';
12
+ import { isReasoningModel } from '../providers.js';
13
+ import { logger } from '../../utils/logger.js';
14
+
15
+ /**
16
+ * System prompt for Stack Researcher with tools
17
+ */
18
+ const STACK_RESEARCHER_WITH_TOOLS_PROMPT = `You are a Stack Researcher agent with access to web search and documentation lookup tools.
19
+
20
+ ## Your Mission
21
+ Research the detected technology stack to find:
22
+ 1. Current best practices
23
+ 2. Common anti-patterns to avoid
24
+ 3. Testing tools and patterns
25
+ 4. Debugging approaches
26
+ 5. Useful documentation links
27
+
28
+ ## Tools Available
29
+ - tavilySearch: Search the web for current best practices and patterns
30
+ - context7Lookup: Look up library documentation
31
+
32
+ ## Research Strategy
33
+ 1. Search for "[technology] best practices 2024"
34
+ 2. Search for "[project type] testing patterns"
35
+ 3. Look up documentation for key dependencies
36
+ 4. Search for "[framework] anti-patterns"
37
+
38
+ ## Output Format
39
+ After research, output ONLY valid JSON with this structure:
40
+ {
41
+ "bestPractices": [
42
+ "Use TypeScript strict mode",
43
+ "Implement proper error boundaries"
44
+ ],
45
+ "antiPatterns": [
46
+ "Don't use any type",
47
+ "Avoid prop drilling"
48
+ ],
49
+ "testingTools": [
50
+ "npx vitest",
51
+ "npx playwright test"
52
+ ],
53
+ "debuggingTools": [
54
+ "React DevTools",
55
+ "DEBUG=* npm run dev"
56
+ ],
57
+ "documentationHints": [
58
+ "React docs: react.dev",
59
+ "Vitest: vitest.dev"
60
+ ],
61
+ "researchMode": "full"
62
+ }
63
+
64
+ Keep each item concise (5-15 words max). Max 5 items per array.`;
65
+
66
+ /**
67
+ * System prompt for Stack Researcher without tools (knowledge-only)
68
+ */
69
+ const STACK_RESEARCHER_KNOWLEDGE_ONLY_PROMPT = `You are a Stack Researcher agent. You don't have web access, so rely on your training knowledge.
70
+
71
+ ## Your Mission
72
+ Based on your knowledge, provide:
73
+ 1. Best practices for the detected technologies
74
+ 2. Common anti-patterns to avoid
75
+ 3. Testing tools commonly used with this stack
76
+ 4. Debugging approaches
77
+ 5. Documentation hints
78
+
79
+ ## Important Notes
80
+ - Be explicit about what you're confident about vs uncertain
81
+ - Focus on well-established practices from your training
82
+ - Mention if something might be outdated
83
+
84
+ ## Output Format
85
+ Output ONLY valid JSON with this structure:
86
+ {
87
+ "bestPractices": [
88
+ "Use TypeScript strict mode",
89
+ "Implement proper error boundaries"
90
+ ],
91
+ "antiPatterns": [
92
+ "Don't use any type",
93
+ "Avoid prop drilling"
94
+ ],
95
+ "testingTools": [
96
+ "npm test",
97
+ "npx vitest"
98
+ ],
99
+ "debuggingTools": [
100
+ "console.log debugging",
101
+ "Node.js inspector"
102
+ ],
103
+ "documentationHints": [
104
+ "Check official docs for updates",
105
+ "Framework docs: [URL]"
106
+ ],
107
+ "researchMode": "knowledge-only"
108
+ }
109
+
110
+ Keep each item concise (5-15 words max). Max 5 items per array.
111
+ Note: Research mode should reflect that you're using training knowledge only.`;
112
+
113
+ /**
114
+ * Create the research prompt based on stack and project type
115
+ */
116
+ function createResearchPrompt(stack: DetectedStack, projectType: string, hasTools: boolean): string {
117
+ const technologies: string[] = [];
118
+
119
+ // Collect all detected technologies
120
+ if (stack.framework) technologies.push(stack.framework.name);
121
+ if (stack.testing?.unit) technologies.push(stack.testing.unit.name);
122
+ if (stack.testing?.e2e) technologies.push(stack.testing.e2e.name);
123
+ if (stack.orm) technologies.push(stack.orm.name);
124
+ if (stack.database) technologies.push(stack.database.name);
125
+ if (stack.stateManagement) technologies.push(stack.stateManagement.name);
126
+ if (stack.auth) technologies.push(stack.auth.name);
127
+ if (stack.mcp?.isProject) technologies.push('MCP Server');
128
+
129
+ const techList = technologies.length > 0 ? technologies.join(', ') : 'Unknown stack';
130
+
131
+ if (hasTools) {
132
+ return `Research best practices for this stack:
133
+
134
+ Project Type: ${projectType}
135
+ Technologies: ${techList}
136
+
137
+ Use the available tools to search for:
138
+ 1. Current best practices for ${projectType} projects
139
+ 2. Testing patterns for ${techList}
140
+ 3. Common anti-patterns to avoid
141
+
142
+ Then produce your research findings as JSON.`;
143
+ }
144
+
145
+ return `Based on your knowledge, provide best practices for this stack:
146
+
147
+ Project Type: ${projectType}
148
+ Technologies: ${techList}
149
+
150
+ Provide:
151
+ 1. Best practices for ${projectType} projects
152
+ 2. Testing tools commonly used with ${techList}
153
+ 3. Anti-patterns to avoid
154
+ 4. Debugging approaches
155
+
156
+ Output your findings as JSON. Be clear this is based on training knowledge.`;
157
+ }
158
+
159
+ /**
160
+ * Determine research mode based on capabilities
161
+ */
162
+ function getResearchMode(capabilities: AgentCapabilities): StackResearch['researchMode'] {
163
+ if (capabilities.hasTavily && capabilities.hasContext7) return 'full';
164
+ if (capabilities.hasTavily) return 'web-only';
165
+ if (capabilities.hasContext7) return 'docs-only';
166
+ return 'knowledge-only';
167
+ }
168
+
169
+ /**
170
+ * Run the Stack Researcher agent
171
+ */
172
+ export async function runStackResearcher(
173
+ model: LanguageModel,
174
+ modelId: string,
175
+ input: StackResearcherInput,
176
+ options: { tavilyApiKey?: string; context7ApiKey?: string },
177
+ verbose: boolean = false
178
+ ): Promise<StackResearch | null> {
179
+ const tools: Record<string, Tool> = {};
180
+
181
+ // Add tools based on available keys
182
+ if (options.tavilyApiKey) {
183
+ tools.tavilySearch = createTavilySearchTool(options.tavilyApiKey);
184
+ }
185
+ if (options.context7ApiKey) {
186
+ tools.context7Lookup = createContext7Tool(options.context7ApiKey);
187
+ }
188
+
189
+ const hasTools = Object.keys(tools).length > 0;
190
+ const researchMode = getResearchMode(input.capabilities);
191
+
192
+ if (verbose) {
193
+ logger.info(`Stack Researcher running in ${researchMode} mode`);
194
+ }
195
+
196
+ const systemPrompt = hasTools
197
+ ? STACK_RESEARCHER_WITH_TOOLS_PROMPT
198
+ : STACK_RESEARCHER_KNOWLEDGE_ONLY_PROMPT;
199
+
200
+ const prompt = createResearchPrompt(input.stack, input.projectType, hasTools);
201
+
202
+ try {
203
+ const result = await generateText({
204
+ model,
205
+ system: systemPrompt,
206
+ prompt,
207
+ ...(hasTools ? { tools, stopWhen: stepCountIs(8) } : {}),
208
+ maxOutputTokens: 2000,
209
+ ...(isReasoningModel(modelId) ? {} : { temperature: 0.3 }),
210
+ });
211
+
212
+ // Parse the response
213
+ const research = parseStackResearch(result.text, result.steps, researchMode, verbose);
214
+ return research;
215
+ } catch (error) {
216
+ if (verbose) {
217
+ logger.error(`Stack Researcher error: ${error instanceof Error ? error.message : String(error)}`);
218
+ }
219
+ return null;
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Parse the stack research from agent response
225
+ */
226
+ function parseStackResearch(
227
+ text: string,
228
+ steps: Array<{ text?: string }> | undefined,
229
+ researchMode: StackResearch['researchMode'],
230
+ verbose: boolean
231
+ ): StackResearch | null {
232
+ // Try to get text from the result or steps
233
+ let textToParse = text;
234
+
235
+ if (!textToParse || textToParse.trim() === '') {
236
+ const stepsList = steps || [];
237
+ for (let i = stepsList.length - 1; i >= 0; i--) {
238
+ const step = stepsList[i];
239
+ if (step.text && step.text.trim() !== '') {
240
+ textToParse = step.text;
241
+ break;
242
+ }
243
+ }
244
+ }
245
+
246
+ if (!textToParse || textToParse.trim() === '') {
247
+ if (verbose) {
248
+ logger.warn('Stack Researcher: No text output found');
249
+ }
250
+ return getDefaultStackResearch(researchMode);
251
+ }
252
+
253
+ try {
254
+ // Remove markdown code blocks if present
255
+ let jsonText = textToParse;
256
+ const jsonMatch = textToParse.match(/```(?:json)?\s*([\s\S]*?)```/);
257
+ if (jsonMatch) {
258
+ jsonText = jsonMatch[1];
259
+ }
260
+
261
+ // Find JSON object
262
+ const objectMatch = jsonText.match(/\{[\s\S]*\}/);
263
+ if (objectMatch) {
264
+ jsonText = objectMatch[0];
265
+ }
266
+
267
+ const parsed = JSON.parse(jsonText) as Partial<StackResearch>;
268
+
269
+ // Build result with defaults for missing fields
270
+ return {
271
+ bestPractices: parsed.bestPractices || [],
272
+ antiPatterns: parsed.antiPatterns || [],
273
+ testingTools: parsed.testingTools || [],
274
+ debuggingTools: parsed.debuggingTools || [],
275
+ documentationHints: parsed.documentationHints || [],
276
+ researchMode: researchMode,
277
+ };
278
+ } catch (error) {
279
+ if (verbose) {
280
+ logger.warn(`Stack Researcher: Failed to parse JSON - ${error instanceof Error ? error.message : String(error)}`);
281
+ }
282
+ return getDefaultStackResearch(researchMode);
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Get default stack research when parsing fails
288
+ */
289
+ function getDefaultStackResearch(researchMode: StackResearch['researchMode']): StackResearch {
290
+ return {
291
+ bestPractices: ['Follow project conventions', 'Write tests for new code'],
292
+ antiPatterns: ['Avoid skipping tests', 'Don\'t ignore type errors'],
293
+ testingTools: ['npm test'],
294
+ debuggingTools: ['console.log', 'debugger statement'],
295
+ documentationHints: ['Check package.json for dependencies'],
296
+ researchMode,
297
+ };
298
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Agent Types and Interfaces
3
+ * Defines the structure for multi-agent analysis
4
+ */
5
+
6
+ import type { ScanResult, DetectedStack } from '../../scanner/types.js';
7
+
8
+ /**
9
+ * Codebase analysis result from the Codebase Analyst agent
10
+ */
11
+ export interface CodebaseAnalysis {
12
+ /** Project structure and context */
13
+ projectContext: {
14
+ /** Key entry point files */
15
+ entryPoints: string[];
16
+ /** Important directories and their purposes */
17
+ keyDirectories: Record<string, string>;
18
+ /** Naming conventions used */
19
+ namingConventions?: string;
20
+ /** The primary project type (MCP Server, REST API, React SPA, CLI, Library) */
21
+ projectType: string;
22
+ };
23
+ /** Detected commands from package.json */
24
+ commands: {
25
+ test?: string;
26
+ lint?: string;
27
+ typecheck?: string;
28
+ build?: string;
29
+ dev?: string;
30
+ format?: string;
31
+ };
32
+ /** Short, actionable implementation guidelines */
33
+ implementationGuidelines: string[];
34
+ /** Additional technologies that may have been missed */
35
+ possibleMissedTechnologies?: string[];
36
+ }
37
+
38
+ /**
39
+ * Stack research result from the Stack Researcher agent
40
+ */
41
+ export interface StackResearch {
42
+ /** Best practices for the detected stack */
43
+ bestPractices: string[];
44
+ /** Anti-patterns to avoid */
45
+ antiPatterns: string[];
46
+ /** Technology-specific testing tools */
47
+ testingTools: string[];
48
+ /** Technology-specific debugging tools */
49
+ debuggingTools: string[];
50
+ /** Documentation hints and links */
51
+ documentationHints: string[];
52
+ /** Whether research was performed with tools or knowledge-only */
53
+ researchMode: 'full' | 'web-only' | 'docs-only' | 'knowledge-only';
54
+ }
55
+
56
+ /**
57
+ * MCP server recommendations
58
+ */
59
+ export interface McpRecommendations {
60
+ /** Essential MCP servers for this stack */
61
+ essential: string[];
62
+ /** Recommended but optional MCP servers */
63
+ recommended: string[];
64
+ }
65
+
66
+ /**
67
+ * Combined result from all agents
68
+ */
69
+ export interface MultiAgentAnalysis {
70
+ /** Codebase analysis from the Codebase Analyst */
71
+ codebaseAnalysis: CodebaseAnalysis;
72
+ /** Stack research from the Stack Researcher */
73
+ stackResearch: StackResearch;
74
+ /** MCP server recommendations (merged from both agents) */
75
+ mcpServers: McpRecommendations;
76
+ }
77
+
78
+ /**
79
+ * Agent capabilities based on available API keys
80
+ */
81
+ export interface AgentCapabilities {
82
+ /** Tavily web search available */
83
+ hasTavily: boolean;
84
+ /** Context7 documentation lookup available */
85
+ hasContext7: boolean;
86
+ }
87
+
88
+ /**
89
+ * Options for running agents
90
+ */
91
+ export interface AgentOptions {
92
+ /** Tavily API key (optional) */
93
+ tavilyApiKey?: string;
94
+ /** Context7 API key (optional) */
95
+ context7ApiKey?: string;
96
+ /** Enable verbose logging */
97
+ verbose?: boolean;
98
+ }
99
+
100
+ /**
101
+ * Input for the Codebase Analyst agent
102
+ */
103
+ export interface CodebaseAnalystInput {
104
+ /** The scan result from the scanner */
105
+ scanResult: ScanResult;
106
+ /** Project root directory */
107
+ projectRoot: string;
108
+ }
109
+
110
+ /**
111
+ * Input for the Stack Researcher agent
112
+ */
113
+ export interface StackResearcherInput {
114
+ /** The detected stack */
115
+ stack: DetectedStack;
116
+ /** The identified project type */
117
+ projectType: string;
118
+ /** Agent capabilities */
119
+ capabilities: AgentCapabilities;
120
+ }
121
+
122
+ /**
123
+ * Input for the Orchestrator agent
124
+ */
125
+ export interface OrchestratorInput {
126
+ /** Codebase analysis result */
127
+ codebaseAnalysis: CodebaseAnalysis;
128
+ /** Stack research result */
129
+ stackResearch: StackResearch;
130
+ /** The detected stack */
131
+ stack: DetectedStack;
132
+ }
@@ -8,6 +8,7 @@ import type { ScanResult, DetectedStack, DetectionResult } from '../scanner/type
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';
12
13
 
13
14
  /**
@@ -44,6 +45,32 @@ export interface McpRecommendations {
44
45
  recommended?: string[];
45
46
  }
46
47
 
48
+ /**
49
+ * Technology-specific testing and debugging tools
50
+ */
51
+ export interface TechnologyTools {
52
+ /** Testing commands specific to the detected technologies */
53
+ testing?: string[];
54
+ /** Debugging/inspection tools for the stack */
55
+ debugging?: string[];
56
+ /** Linting/validation tools beyond the standard ones */
57
+ validation?: string[];
58
+ }
59
+
60
+ /**
61
+ * Technology-specific best practices
62
+ */
63
+ export interface TechnologyPractices {
64
+ /** The primary project type (e.g., "MCP Server", "REST API", "React SPA") */
65
+ projectType?: string;
66
+ /** Practices specific to the detected technologies */
67
+ practices?: string[];
68
+ /** Anti-patterns to avoid for this stack */
69
+ antiPatterns?: string[];
70
+ /** Links to relevant documentation (optional) */
71
+ documentationHints?: string[];
72
+ }
73
+
47
74
  /**
48
75
  * AI analysis result - focused on actionable outputs
49
76
  */
@@ -58,6 +85,10 @@ export interface AIAnalysisResult {
58
85
  mcpServers?: McpRecommendations;
59
86
  /** Additional technologies that may have been missed */
60
87
  possibleMissedTechnologies?: string[];
88
+ /** Technology-specific tools for testing/debugging */
89
+ technologyTools?: TechnologyTools;
90
+ /** Technology-specific best practices based on detected stack */
91
+ technologyPractices?: TechnologyPractices;
61
92
  }
62
93
 
63
94
  /**
@@ -79,6 +110,10 @@ export interface EnhancerOptions {
79
110
  verbose?: boolean;
80
111
  /** Use agentic mode with tools for deeper codebase exploration */
81
112
  agentic?: boolean;
113
+ /** Tavily API key for web search (optional) */
114
+ tavilyApiKey?: string;
115
+ /** Context7 API key for documentation lookup (optional) */
116
+ context7ApiKey?: string;
82
117
  }
83
118
 
84
119
  /**
@@ -173,12 +208,16 @@ export class AIEnhancer {
173
208
  private model?: string;
174
209
  private verbose: boolean;
175
210
  private agentic: boolean;
211
+ private tavilyApiKey?: string;
212
+ private context7ApiKey?: string;
176
213
 
177
214
  constructor(options: EnhancerOptions = {}) {
178
215
  this.provider = options.provider || 'anthropic';
179
216
  this.model = options.model;
180
217
  this.verbose = options.verbose || false;
181
218
  this.agentic = options.agentic || false;
219
+ this.tavilyApiKey = options.tavilyApiKey;
220
+ this.context7ApiKey = options.context7ApiKey;
182
221
  }
183
222
 
184
223
  /**
@@ -285,12 +324,41 @@ export class AIEnhancer {
285
324
  }
286
325
 
287
326
  /**
288
- * Agentic enhancement mode - use tools to explore codebase
327
+ * Agentic enhancement mode - use multi-agent system to explore codebase
289
328
  */
290
329
  private async enhanceAgentic(
291
330
  model: ReturnType<typeof getModel>['model'],
292
331
  modelId: string,
293
332
  scanResult: ScanResult
333
+ ): Promise<AIAnalysisResult | null> {
334
+ // Use the multi-agent system for deeper analysis
335
+ const multiAgentResult = await runMultiAgentAnalysis(
336
+ model,
337
+ modelId,
338
+ scanResult,
339
+ {
340
+ tavilyApiKey: this.tavilyApiKey,
341
+ context7ApiKey: this.context7ApiKey,
342
+ verbose: this.verbose,
343
+ }
344
+ );
345
+
346
+ if (!multiAgentResult) {
347
+ // Fall back to simple agentic mode
348
+ return this.enhanceLegacyAgentic(model, modelId, scanResult);
349
+ }
350
+
351
+ // Convert MultiAgentAnalysis to AIAnalysisResult for backward compatibility
352
+ return convertMultiAgentToAIAnalysis(multiAgentResult);
353
+ }
354
+
355
+ /**
356
+ * Legacy agentic mode (fallback when multi-agent fails)
357
+ */
358
+ private async enhanceLegacyAgentic(
359
+ model: ReturnType<typeof getModel>['model'],
360
+ modelId: string,
361
+ scanResult: ScanResult
294
362
  ): Promise<AIAnalysisResult | null> {
295
363
  const tools = createExplorationTools(scanResult.projectRoot);
296
364
 
@@ -325,7 +393,6 @@ When done exploring, output your final analysis as valid JSON matching this stru
325
393
  }`;
326
394
 
327
395
  // Use agentic loop - AI will call tools until it has enough info
328
- // stopWhen: stepCountIs(10) allows up to 10 tool-calling steps
329
396
  const result = await generateText({
330
397
  model,
331
398
  system: SYSTEM_PROMPT_AGENTIC,
@@ -333,7 +400,6 @@ When done exploring, output your final analysis as valid JSON matching this stru
333
400
  tools,
334
401
  stopWhen: stepCountIs(10),
335
402
  maxOutputTokens: 4000,
336
- // Reasoning models don't support temperature
337
403
  ...(isReasoningModel(modelId) ? {} : { temperature: 0.3 }),
338
404
  });
339
405
 
@@ -346,7 +412,6 @@ When done exploring, output your final analysis as valid JSON matching this stru
346
412
  logger.info(`No direct text output, checking ${result.steps?.length || 0} steps...`);
347
413
  }
348
414
 
349
- // Look through steps for text content (from last to first)
350
415
  const steps = result.steps || [];
351
416
  for (let i = steps.length - 1; i >= 0; i--) {
352
417
  const step = steps[i];
@@ -368,6 +433,39 @@ When done exploring, output your final analysis as valid JSON matching this stru
368
433
  }
369
434
  }
370
435
 
436
+ /**
437
+ * Convert MultiAgentAnalysis to AIAnalysisResult for backward compatibility
438
+ */
439
+ function convertMultiAgentToAIAnalysis(multiAgent: MultiAgentAnalysis): AIAnalysisResult {
440
+ const { codebaseAnalysis, stackResearch, mcpServers } = multiAgent;
441
+
442
+ return {
443
+ projectContext: {
444
+ entryPoints: codebaseAnalysis.projectContext.entryPoints,
445
+ keyDirectories: codebaseAnalysis.projectContext.keyDirectories,
446
+ namingConventions: codebaseAnalysis.projectContext.namingConventions,
447
+ },
448
+ commands: codebaseAnalysis.commands,
449
+ implementationGuidelines: codebaseAnalysis.implementationGuidelines,
450
+ mcpServers: {
451
+ essential: mcpServers.essential,
452
+ recommended: mcpServers.recommended,
453
+ },
454
+ possibleMissedTechnologies: codebaseAnalysis.possibleMissedTechnologies,
455
+ technologyTools: {
456
+ testing: stackResearch.testingTools,
457
+ debugging: stackResearch.debuggingTools,
458
+ validation: [],
459
+ },
460
+ technologyPractices: {
461
+ projectType: codebaseAnalysis.projectContext.projectType,
462
+ practices: stackResearch.bestPractices,
463
+ antiPatterns: stackResearch.antiPatterns,
464
+ documentationHints: stackResearch.documentationHints,
465
+ },
466
+ };
467
+ }
468
+
371
469
  /**
372
470
  * Convenience function to enhance scan results with AI
373
471
  */
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,