wiggum-cli 0.2.5 → 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.
- 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 +150 -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 +187 -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 +274 -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 +105 -9
- 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/package.json +3 -3
- package/src/ai/agents/codebase-analyst.ts +172 -0
- package/src/ai/agents/index.ts +147 -0
- package/src/ai/agents/orchestrator.ts +222 -0
- package/src/ai/agents/stack-researcher.ts +298 -0
- package/src/ai/agents/types.ts +132 -0
- package/src/ai/enhancer.ts +159 -9
- 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
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codebase Analyst Agent
|
|
3
|
+
* Explores the codebase to understand its structure and patterns
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { generateText, stepCountIs, type LanguageModel } from 'ai';
|
|
7
|
+
import type { CodebaseAnalysis, CodebaseAnalystInput } from './types.js';
|
|
8
|
+
import { createExplorationTools } from '../tools.js';
|
|
9
|
+
import { isReasoningModel } from '../providers.js';
|
|
10
|
+
import { logger } from '../../utils/logger.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* System prompt for the Codebase Analyst agent
|
|
14
|
+
*/
|
|
15
|
+
const CODEBASE_ANALYST_SYSTEM_PROMPT = `You are a Codebase Analyst agent. Your job is to thoroughly explore a codebase and produce a structured analysis.
|
|
16
|
+
|
|
17
|
+
## Your Mission
|
|
18
|
+
Explore the codebase to understand:
|
|
19
|
+
1. Project structure and entry points
|
|
20
|
+
2. Key directories and their purposes
|
|
21
|
+
3. Naming conventions
|
|
22
|
+
4. Available commands (from package.json)
|
|
23
|
+
5. The primary project type
|
|
24
|
+
|
|
25
|
+
## Exploration Strategy
|
|
26
|
+
1. First, list the root directory to understand project structure
|
|
27
|
+
2. Read package.json to understand scripts and dependencies
|
|
28
|
+
3. Search for key patterns: entry points, routes, components
|
|
29
|
+
4. Identify the PROJECT TYPE:
|
|
30
|
+
- MCP Server: Has @modelcontextprotocol dependencies
|
|
31
|
+
- REST API: Express/Fastify/Hono with route handlers
|
|
32
|
+
- React SPA: React with components, no server-side rendering
|
|
33
|
+
- Next.js App: Next.js with app or pages directory
|
|
34
|
+
- CLI Tool: Has bin entry in package.json
|
|
35
|
+
- Library: Published package without app entry
|
|
36
|
+
|
|
37
|
+
## Tools Available
|
|
38
|
+
- searchCode: Search using ripgrep patterns
|
|
39
|
+
- readFile: Read file contents
|
|
40
|
+
- listDirectory: List directory structure
|
|
41
|
+
- getPackageInfo: Get package.json info
|
|
42
|
+
|
|
43
|
+
## Output Format
|
|
44
|
+
After exploration, output ONLY valid JSON with this exact structure:
|
|
45
|
+
{
|
|
46
|
+
"projectContext": {
|
|
47
|
+
"entryPoints": ["src/index.ts"],
|
|
48
|
+
"keyDirectories": {"src/routes": "API routes"},
|
|
49
|
+
"namingConventions": "camelCase files, PascalCase components",
|
|
50
|
+
"projectType": "MCP Server"
|
|
51
|
+
},
|
|
52
|
+
"commands": {
|
|
53
|
+
"test": "npm test",
|
|
54
|
+
"lint": "npm run lint",
|
|
55
|
+
"build": "npm run build",
|
|
56
|
+
"dev": "npm run dev"
|
|
57
|
+
},
|
|
58
|
+
"implementationGuidelines": [
|
|
59
|
+
"Run npm test after changes",
|
|
60
|
+
"Use Zod for validation"
|
|
61
|
+
],
|
|
62
|
+
"possibleMissedTechnologies": ["Redis"]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Keep each guideline to 5-10 words max. Max 7 guidelines.`;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Run the Codebase Analyst agent
|
|
69
|
+
*/
|
|
70
|
+
export async function runCodebaseAnalyst(
|
|
71
|
+
model: LanguageModel,
|
|
72
|
+
modelId: string,
|
|
73
|
+
input: CodebaseAnalystInput,
|
|
74
|
+
verbose: boolean = false
|
|
75
|
+
): Promise<CodebaseAnalysis | null> {
|
|
76
|
+
const tools = createExplorationTools(input.projectRoot);
|
|
77
|
+
|
|
78
|
+
const prompt = `Analyze this codebase and produce a structured analysis.
|
|
79
|
+
|
|
80
|
+
Project: ${input.projectRoot}
|
|
81
|
+
|
|
82
|
+
Start by exploring the directory structure and package.json, then produce your analysis as JSON.`;
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const result = await generateText({
|
|
86
|
+
model,
|
|
87
|
+
system: CODEBASE_ANALYST_SYSTEM_PROMPT,
|
|
88
|
+
prompt,
|
|
89
|
+
tools,
|
|
90
|
+
stopWhen: stepCountIs(12),
|
|
91
|
+
maxOutputTokens: 3000,
|
|
92
|
+
...(isReasoningModel(modelId) ? {} : { temperature: 0.3 }),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Extract JSON from response
|
|
96
|
+
const analysis = parseCodebaseAnalysis(result.text, result.steps, verbose);
|
|
97
|
+
return analysis;
|
|
98
|
+
} catch (error) {
|
|
99
|
+
if (verbose) {
|
|
100
|
+
logger.error(`Codebase Analyst error: ${error instanceof Error ? error.message : String(error)}`);
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Parse the codebase analysis from agent response
|
|
108
|
+
*/
|
|
109
|
+
function parseCodebaseAnalysis(
|
|
110
|
+
text: string,
|
|
111
|
+
steps: Array<{ text?: string }> | undefined,
|
|
112
|
+
verbose: boolean
|
|
113
|
+
): CodebaseAnalysis | null {
|
|
114
|
+
// Try to get text from the result or steps
|
|
115
|
+
let textToParse = text;
|
|
116
|
+
|
|
117
|
+
if (!textToParse || textToParse.trim() === '') {
|
|
118
|
+
// Look through steps for text content
|
|
119
|
+
const stepsList = steps || [];
|
|
120
|
+
for (let i = stepsList.length - 1; i >= 0; i--) {
|
|
121
|
+
const step = stepsList[i];
|
|
122
|
+
if (step.text && step.text.trim() !== '') {
|
|
123
|
+
textToParse = step.text;
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!textToParse || textToParse.trim() === '') {
|
|
130
|
+
if (verbose) {
|
|
131
|
+
logger.warn('Codebase Analyst: No text output found');
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
// Remove markdown code blocks if present
|
|
138
|
+
let jsonText = textToParse;
|
|
139
|
+
const jsonMatch = textToParse.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
140
|
+
if (jsonMatch) {
|
|
141
|
+
jsonText = jsonMatch[1];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Find JSON object
|
|
145
|
+
const objectMatch = jsonText.match(/\{[\s\S]*\}/);
|
|
146
|
+
if (objectMatch) {
|
|
147
|
+
jsonText = objectMatch[0];
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const parsed = JSON.parse(jsonText) as CodebaseAnalysis;
|
|
151
|
+
|
|
152
|
+
// Validate required fields
|
|
153
|
+
if (!parsed.projectContext || !parsed.commands) {
|
|
154
|
+
if (verbose) {
|
|
155
|
+
logger.warn('Codebase Analyst: Missing required fields in response');
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Ensure projectType is set
|
|
161
|
+
if (!parsed.projectContext.projectType) {
|
|
162
|
+
parsed.projectContext.projectType = 'Unknown';
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return parsed;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if (verbose) {
|
|
168
|
+
logger.warn(`Codebase Analyst: Failed to parse JSON - ${error instanceof Error ? error.message : String(error)}`);
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agents Index
|
|
3
|
+
* Exports all agent types and functions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Types
|
|
7
|
+
export type {
|
|
8
|
+
CodebaseAnalysis,
|
|
9
|
+
StackResearch,
|
|
10
|
+
McpRecommendations,
|
|
11
|
+
MultiAgentAnalysis,
|
|
12
|
+
AgentCapabilities,
|
|
13
|
+
AgentOptions,
|
|
14
|
+
CodebaseAnalystInput,
|
|
15
|
+
StackResearcherInput,
|
|
16
|
+
OrchestratorInput,
|
|
17
|
+
} from './types.js';
|
|
18
|
+
|
|
19
|
+
// Agents
|
|
20
|
+
export { runCodebaseAnalyst } from './codebase-analyst.js';
|
|
21
|
+
export { runStackResearcher } from './stack-researcher.js';
|
|
22
|
+
export { runOrchestrator, mergeAgentResults } from './orchestrator.js';
|
|
23
|
+
|
|
24
|
+
// Re-export for convenience
|
|
25
|
+
import type { LanguageModel } from 'ai';
|
|
26
|
+
import type { ScanResult, DetectedStack } from '../../scanner/types.js';
|
|
27
|
+
import type {
|
|
28
|
+
MultiAgentAnalysis,
|
|
29
|
+
AgentCapabilities,
|
|
30
|
+
AgentOptions,
|
|
31
|
+
} from './types.js';
|
|
32
|
+
import { runCodebaseAnalyst } from './codebase-analyst.js';
|
|
33
|
+
import { runStackResearcher } from './stack-researcher.js';
|
|
34
|
+
import { runOrchestrator, mergeAgentResults } from './orchestrator.js';
|
|
35
|
+
import { logger } from '../../utils/logger.js';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Run the full multi-agent analysis pipeline
|
|
39
|
+
*/
|
|
40
|
+
export async function runMultiAgentAnalysis(
|
|
41
|
+
model: LanguageModel,
|
|
42
|
+
modelId: string,
|
|
43
|
+
scanResult: ScanResult,
|
|
44
|
+
options: AgentOptions = {}
|
|
45
|
+
): Promise<MultiAgentAnalysis | null> {
|
|
46
|
+
const { tavilyApiKey, context7ApiKey, verbose = false } = options;
|
|
47
|
+
|
|
48
|
+
// Determine capabilities
|
|
49
|
+
const capabilities: AgentCapabilities = {
|
|
50
|
+
hasTavily: !!tavilyApiKey,
|
|
51
|
+
hasContext7: !!context7ApiKey,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
if (verbose) {
|
|
55
|
+
logger.info('Starting multi-agent analysis...');
|
|
56
|
+
logger.info(`Capabilities: Tavily=${capabilities.hasTavily}, Context7=${capabilities.hasContext7}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Run Codebase Analyst
|
|
60
|
+
if (verbose) {
|
|
61
|
+
logger.info('Running Codebase Analyst...');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const codebaseAnalysis = await runCodebaseAnalyst(
|
|
65
|
+
model,
|
|
66
|
+
modelId,
|
|
67
|
+
{
|
|
68
|
+
scanResult,
|
|
69
|
+
projectRoot: scanResult.projectRoot,
|
|
70
|
+
},
|
|
71
|
+
verbose
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
if (!codebaseAnalysis) {
|
|
75
|
+
if (verbose) {
|
|
76
|
+
logger.warn('Codebase Analyst failed, using defaults');
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Run Stack Researcher
|
|
82
|
+
if (verbose) {
|
|
83
|
+
logger.info('Running Stack Researcher...');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const stackResearch = await runStackResearcher(
|
|
87
|
+
model,
|
|
88
|
+
modelId,
|
|
89
|
+
{
|
|
90
|
+
stack: scanResult.stack,
|
|
91
|
+
projectType: codebaseAnalysis.projectContext.projectType,
|
|
92
|
+
capabilities,
|
|
93
|
+
},
|
|
94
|
+
{ tavilyApiKey, context7ApiKey },
|
|
95
|
+
verbose
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (!stackResearch) {
|
|
99
|
+
if (verbose) {
|
|
100
|
+
logger.warn('Stack Researcher failed, using defaults');
|
|
101
|
+
}
|
|
102
|
+
// Continue with defaults - stack research is optional
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Run Orchestrator to merge results
|
|
106
|
+
if (verbose) {
|
|
107
|
+
logger.info('Running Orchestrator...');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const mcpServers = await runOrchestrator(
|
|
111
|
+
model,
|
|
112
|
+
modelId,
|
|
113
|
+
{
|
|
114
|
+
codebaseAnalysis,
|
|
115
|
+
stackResearch: stackResearch || getDefaultStackResearch(),
|
|
116
|
+
stack: scanResult.stack,
|
|
117
|
+
},
|
|
118
|
+
verbose
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// Merge all results
|
|
122
|
+
const finalResult = mergeAgentResults(
|
|
123
|
+
codebaseAnalysis,
|
|
124
|
+
stackResearch || getDefaultStackResearch(),
|
|
125
|
+
mcpServers
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
if (verbose) {
|
|
129
|
+
logger.info('Multi-agent analysis complete');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return finalResult;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get default stack research when agent fails
|
|
137
|
+
*/
|
|
138
|
+
function getDefaultStackResearch() {
|
|
139
|
+
return {
|
|
140
|
+
bestPractices: ['Follow project conventions'],
|
|
141
|
+
antiPatterns: ['Avoid skipping tests'],
|
|
142
|
+
testingTools: ['npm test'],
|
|
143
|
+
debuggingTools: ['console.log'],
|
|
144
|
+
documentationHints: ['Check official docs'],
|
|
145
|
+
researchMode: 'knowledge-only' as const,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator Agent
|
|
3
|
+
* Coordinates the multi-agent analysis and merges results
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { generateText, type LanguageModel } from 'ai';
|
|
7
|
+
import type {
|
|
8
|
+
CodebaseAnalysis,
|
|
9
|
+
StackResearch,
|
|
10
|
+
MultiAgentAnalysis,
|
|
11
|
+
McpRecommendations,
|
|
12
|
+
OrchestratorInput,
|
|
13
|
+
} from './types.js';
|
|
14
|
+
import type { DetectedStack } from '../../scanner/types.js';
|
|
15
|
+
import { isReasoningModel } from '../providers.js';
|
|
16
|
+
import { logger } from '../../utils/logger.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* System prompt for the Orchestrator
|
|
20
|
+
*/
|
|
21
|
+
const ORCHESTRATOR_SYSTEM_PROMPT = `You are an Orchestrator agent that merges analysis results into final recommendations.
|
|
22
|
+
|
|
23
|
+
Your job is to:
|
|
24
|
+
1. Review the codebase analysis and stack research
|
|
25
|
+
2. Recommend appropriate MCP servers for the stack
|
|
26
|
+
3. Produce a final merged recommendation
|
|
27
|
+
|
|
28
|
+
## MCP Server Selection Guidelines
|
|
29
|
+
Based on the detected stack, recommend these MCP servers:
|
|
30
|
+
|
|
31
|
+
**Always Essential:**
|
|
32
|
+
- filesystem: For file operations (all projects)
|
|
33
|
+
- git: For version control (all projects)
|
|
34
|
+
|
|
35
|
+
**By Project Type:**
|
|
36
|
+
- MCP Server: memory (for context)
|
|
37
|
+
- REST API: fetch (external APIs), postgres/sqlite (databases)
|
|
38
|
+
- React/Next.js: fetch (data fetching), memory (state persistence)
|
|
39
|
+
- CLI Tool: filesystem (primary), memory (caching)
|
|
40
|
+
- Library: git (versioning)
|
|
41
|
+
|
|
42
|
+
**By Technology:**
|
|
43
|
+
- Docker in use → docker MCP server
|
|
44
|
+
- PostgreSQL → postgres MCP server
|
|
45
|
+
- SQLite → sqlite MCP server
|
|
46
|
+
- AWS services → aws-kb-retrieval MCP server
|
|
47
|
+
- GitHub workflows → github MCP server
|
|
48
|
+
|
|
49
|
+
## Output Format
|
|
50
|
+
Output ONLY valid JSON:
|
|
51
|
+
{
|
|
52
|
+
"mcpServers": {
|
|
53
|
+
"essential": ["filesystem", "git"],
|
|
54
|
+
"recommended": ["docker", "postgres"]
|
|
55
|
+
}
|
|
56
|
+
}`;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Run the Orchestrator to merge results and recommend MCP servers
|
|
60
|
+
*/
|
|
61
|
+
export async function runOrchestrator(
|
|
62
|
+
model: LanguageModel,
|
|
63
|
+
modelId: string,
|
|
64
|
+
input: OrchestratorInput,
|
|
65
|
+
verbose: boolean = false
|
|
66
|
+
): Promise<McpRecommendations> {
|
|
67
|
+
const prompt = createOrchestratorPrompt(input);
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const result = await generateText({
|
|
71
|
+
model,
|
|
72
|
+
system: ORCHESTRATOR_SYSTEM_PROMPT,
|
|
73
|
+
prompt,
|
|
74
|
+
maxOutputTokens: 1000,
|
|
75
|
+
...(isReasoningModel(modelId) ? {} : { temperature: 0.2 }),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const mcpServers = parseMcpRecommendations(result.text, input.stack, verbose);
|
|
79
|
+
return mcpServers;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (verbose) {
|
|
82
|
+
logger.error(`Orchestrator error: ${error instanceof Error ? error.message : String(error)}`);
|
|
83
|
+
}
|
|
84
|
+
// Return default recommendations based on project type
|
|
85
|
+
return getDefaultMcpRecommendations(input.codebaseAnalysis.projectContext.projectType, input.stack);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Create the orchestrator prompt
|
|
91
|
+
*/
|
|
92
|
+
function createOrchestratorPrompt(input: OrchestratorInput): string {
|
|
93
|
+
const { codebaseAnalysis, stackResearch, stack } = input;
|
|
94
|
+
|
|
95
|
+
// Summarize the stack
|
|
96
|
+
const stackSummary: string[] = [];
|
|
97
|
+
if (stack.framework) stackSummary.push(`Framework: ${stack.framework.name}`);
|
|
98
|
+
if (stack.database) stackSummary.push(`Database: ${stack.database.name}`);
|
|
99
|
+
if (stack.orm) stackSummary.push(`ORM: ${stack.orm.name}`);
|
|
100
|
+
if (stack.deployment?.length) stackSummary.push(`Deployment: ${stack.deployment.map(d => d.name).join(', ')}`);
|
|
101
|
+
if (stack.mcp?.isProject) stackSummary.push('This is an MCP Server project');
|
|
102
|
+
|
|
103
|
+
return `Analyze these results and recommend MCP servers:
|
|
104
|
+
|
|
105
|
+
## Project Type
|
|
106
|
+
${codebaseAnalysis.projectContext.projectType}
|
|
107
|
+
|
|
108
|
+
## Detected Stack
|
|
109
|
+
${stackSummary.join('\n') || 'Unknown'}
|
|
110
|
+
|
|
111
|
+
## Testing Tools Identified
|
|
112
|
+
${stackResearch.testingTools.join(', ') || 'None identified'}
|
|
113
|
+
|
|
114
|
+
## Debugging Tools
|
|
115
|
+
${stackResearch.debuggingTools.join(', ') || 'None identified'}
|
|
116
|
+
|
|
117
|
+
Based on this analysis, recommend essential and optional MCP servers.
|
|
118
|
+
Output as JSON with "mcpServers" containing "essential" and "recommended" arrays.`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Parse MCP recommendations from orchestrator response
|
|
123
|
+
*/
|
|
124
|
+
function parseMcpRecommendations(
|
|
125
|
+
text: string,
|
|
126
|
+
stack: DetectedStack,
|
|
127
|
+
verbose: boolean
|
|
128
|
+
): McpRecommendations {
|
|
129
|
+
if (!text || text.trim() === '') {
|
|
130
|
+
return getDefaultMcpRecommendations('Unknown', stack);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
// Remove markdown code blocks
|
|
135
|
+
let jsonText = text;
|
|
136
|
+
const jsonMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
137
|
+
if (jsonMatch) {
|
|
138
|
+
jsonText = jsonMatch[1];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Find JSON object
|
|
142
|
+
const objectMatch = jsonText.match(/\{[\s\S]*\}/);
|
|
143
|
+
if (objectMatch) {
|
|
144
|
+
jsonText = objectMatch[0];
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const parsed = JSON.parse(jsonText) as { mcpServers?: McpRecommendations };
|
|
148
|
+
|
|
149
|
+
if (parsed.mcpServers) {
|
|
150
|
+
return {
|
|
151
|
+
essential: parsed.mcpServers.essential || ['filesystem', 'git'],
|
|
152
|
+
recommended: parsed.mcpServers.recommended || [],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return getDefaultMcpRecommendations('Unknown', stack);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
if (verbose) {
|
|
159
|
+
logger.warn(`Orchestrator: Failed to parse JSON - ${error instanceof Error ? error.message : String(error)}`);
|
|
160
|
+
}
|
|
161
|
+
return getDefaultMcpRecommendations('Unknown', stack);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get default MCP recommendations based on project type and stack
|
|
167
|
+
*/
|
|
168
|
+
function getDefaultMcpRecommendations(projectType: string, stack: DetectedStack): McpRecommendations {
|
|
169
|
+
const essential: string[] = ['filesystem', 'git'];
|
|
170
|
+
const recommended: string[] = [];
|
|
171
|
+
|
|
172
|
+
// Add based on project type
|
|
173
|
+
const lowerType = projectType.toLowerCase();
|
|
174
|
+
|
|
175
|
+
if (lowerType.includes('mcp')) {
|
|
176
|
+
recommended.push('memory');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (lowerType.includes('api') || lowerType.includes('server')) {
|
|
180
|
+
recommended.push('fetch');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Add based on detected stack
|
|
184
|
+
if (stack.database) {
|
|
185
|
+
const dbName = stack.database.name.toLowerCase();
|
|
186
|
+
if (dbName.includes('postgres')) {
|
|
187
|
+
recommended.push('postgres');
|
|
188
|
+
} else if (dbName.includes('sqlite')) {
|
|
189
|
+
recommended.push('sqlite');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (stack.deployment?.some(d => d.name.toLowerCase().includes('docker'))) {
|
|
194
|
+
recommended.push('docker');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Add from existing MCP recommendations in stack
|
|
198
|
+
if (stack.mcp?.recommended) {
|
|
199
|
+
for (const rec of stack.mcp.recommended) {
|
|
200
|
+
if (!essential.includes(rec) && !recommended.includes(rec)) {
|
|
201
|
+
recommended.push(rec);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return { essential, recommended };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Merge all agent results into a final MultiAgentAnalysis
|
|
211
|
+
*/
|
|
212
|
+
export function mergeAgentResults(
|
|
213
|
+
codebaseAnalysis: CodebaseAnalysis,
|
|
214
|
+
stackResearch: StackResearch,
|
|
215
|
+
mcpServers: McpRecommendations
|
|
216
|
+
): MultiAgentAnalysis {
|
|
217
|
+
return {
|
|
218
|
+
codebaseAnalysis,
|
|
219
|
+
stackResearch,
|
|
220
|
+
mcpServers,
|
|
221
|
+
};
|
|
222
|
+
}
|