wiggum-cli 0.2.1 → 0.2.3

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.
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Simpson Color Palette
3
+ * Custom ANSI color utilities for the CLI
4
+ */
5
+ /**
6
+ * Simpson color palette hex values
7
+ */
8
+ export declare const SIMPSON_COLORS: {
9
+ readonly blue: "#2f64d6";
10
+ readonly yellow: "#f8db27";
11
+ readonly brown: "#9c5b01";
12
+ readonly white: "#ffffff";
13
+ readonly pink: "#ff81c1";
14
+ };
15
+ /**
16
+ * Simpson color functions for terminal output
17
+ */
18
+ export declare const simpson: {
19
+ yellow: (text: string) => string;
20
+ blue: (text: string) => string;
21
+ brown: (text: string) => string;
22
+ white: (text: string) => string;
23
+ pink: (text: string) => string;
24
+ };
25
+ /**
26
+ * Box drawing characters
27
+ */
28
+ export declare const box: {
29
+ topLeft: string;
30
+ topRight: string;
31
+ bottomLeft: string;
32
+ bottomRight: string;
33
+ horizontal: string;
34
+ vertical: string;
35
+ };
36
+ /**
37
+ * Draw a horizontal line
38
+ */
39
+ export declare function drawLine(width?: number): string;
40
+ /**
41
+ * Draw a box around text
42
+ */
43
+ export declare function drawBox(text: string, padding?: number): string;
44
+ /**
45
+ * Section header with horizontal lines
46
+ */
47
+ export declare function sectionHeader(title: string): string;
48
+ //# sourceMappingURL=colors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.d.ts","sourceRoot":"","sources":["../../src/utils/colors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;CAMjB,CAAC;AA8BX;;GAEG;AACH,eAAO,MAAM,OAAO;mBACH,MAAM,KAAG,MAAM;iBACjB,MAAM,KAAG,MAAM;kBACd,MAAM,KAAG,MAAM;kBACf,MAAM,KAAG,MAAM;iBAChB,MAAM,KAAG,MAAM;CAC7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,GAAG;;;;;;;CAOf,CAAC;AAEF;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,GAAE,MAAW,GAAG,MAAM,CAEnD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,MAAU,GAAG,MAAM,CASjE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGnD"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Simpson Color Palette
3
+ * Custom ANSI color utilities for the CLI
4
+ */
5
+ /**
6
+ * Simpson color palette hex values
7
+ */
8
+ export const SIMPSON_COLORS = {
9
+ blue: '#2f64d6',
10
+ yellow: '#f8db27',
11
+ brown: '#9c5b01',
12
+ white: '#ffffff',
13
+ pink: '#ff81c1',
14
+ };
15
+ /**
16
+ * Convert hex to RGB
17
+ */
18
+ function hexToRgb(hex) {
19
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
20
+ if (!result) {
21
+ return { r: 255, g: 255, b: 255 };
22
+ }
23
+ return {
24
+ r: parseInt(result[1], 16),
25
+ g: parseInt(result[2], 16),
26
+ b: parseInt(result[3], 16),
27
+ };
28
+ }
29
+ /**
30
+ * Create ANSI escape code for foreground color from hex
31
+ */
32
+ function fgHex(hex) {
33
+ const { r, g, b } = hexToRgb(hex);
34
+ return `\x1b[38;2;${r};${g};${b}m`;
35
+ }
36
+ /**
37
+ * Reset ANSI escape code
38
+ */
39
+ const reset = '\x1b[0m';
40
+ /**
41
+ * Simpson color functions for terminal output
42
+ */
43
+ export const simpson = {
44
+ yellow: (text) => `${fgHex(SIMPSON_COLORS.yellow)}${text}${reset}`,
45
+ blue: (text) => `${fgHex(SIMPSON_COLORS.blue)}${text}${reset}`,
46
+ brown: (text) => `${fgHex(SIMPSON_COLORS.brown)}${text}${reset}`,
47
+ white: (text) => `${fgHex(SIMPSON_COLORS.white)}${text}${reset}`,
48
+ pink: (text) => `${fgHex(SIMPSON_COLORS.pink)}${text}${reset}`,
49
+ };
50
+ /**
51
+ * Box drawing characters
52
+ */
53
+ export const box = {
54
+ topLeft: '┌',
55
+ topRight: '┐',
56
+ bottomLeft: '└',
57
+ bottomRight: '┘',
58
+ horizontal: '─',
59
+ vertical: '│',
60
+ };
61
+ /**
62
+ * Draw a horizontal line
63
+ */
64
+ export function drawLine(width = 50) {
65
+ return simpson.brown(box.horizontal.repeat(width));
66
+ }
67
+ /**
68
+ * Draw a box around text
69
+ */
70
+ export function drawBox(text, padding = 1) {
71
+ const paddedText = ' '.repeat(padding) + text + ' '.repeat(padding);
72
+ const width = paddedText.length;
73
+ const top = simpson.brown(box.topLeft + box.horizontal.repeat(width) + box.topRight);
74
+ const middle = simpson.brown(box.vertical) + paddedText + simpson.brown(box.vertical);
75
+ const bottom = simpson.brown(box.bottomLeft + box.horizontal.repeat(width) + box.bottomRight);
76
+ return `${top}\n${middle}\n${bottom}`;
77
+ }
78
+ /**
79
+ * Section header with horizontal lines
80
+ */
81
+ export function sectionHeader(title) {
82
+ const line = drawLine(50);
83
+ return `\n${line}\n${simpson.yellow(title)}\n${line}`;
84
+ }
85
+ //# sourceMappingURL=colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.js","sourceRoot":"","sources":["../../src/utils/colors.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,9 @@
1
1
  /**
2
- * Display the RALPH ASCII header in Simpson yellow
2
+ * Display the RALPH ASCII header with welcome box
3
3
  */
4
4
  export declare function displayHeader(): void;
5
+ /**
6
+ * Display a minimal header (for subcommands)
7
+ */
8
+ export declare function displayMinimalHeader(): void;
5
9
  //# sourceMappingURL=header.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../src/utils/header.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CASpC"}
1
+ {"version":3,"file":"header.d.ts","sourceRoot":"","sources":["../../src/utils/header.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,aAAa,IAAI,IAAI,CAgBpC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAI3C"}
@@ -1,15 +1,30 @@
1
1
  import cfonts from 'cfonts';
2
+ import { simpson, drawBox, SIMPSON_COLORS } from './colors.js';
2
3
  /**
3
- * Display the RALPH ASCII header in Simpson yellow
4
+ * Display the RALPH ASCII header with welcome box
4
5
  */
5
6
  export function displayHeader() {
6
- cfonts.say('RALPH', {
7
+ // Welcome box like Claude Code
8
+ const welcomeText = simpson.pink('*') + ' Welcome to ' + simpson.yellow('Ralph') + '🍩';
9
+ console.log('');
10
+ console.log(drawBox(welcomeText, 2));
11
+ console.log('');
12
+ // ASCII art logo in Simpson yellow
13
+ cfonts.say('WIGGUM CLI', {
7
14
  font: 'block',
8
- colors: ['#FED90F'],
15
+ colors: [SIMPSON_COLORS.yellow],
9
16
  letterSpacing: 1,
10
17
  lineHeight: 1,
11
- space: true,
18
+ space: false,
12
19
  maxLength: 0,
13
20
  });
14
21
  }
22
+ /**
23
+ * Display a minimal header (for subcommands)
24
+ */
25
+ export function displayMinimalHeader() {
26
+ console.log('');
27
+ console.log(simpson.yellow('Ralph') + simpson.brown(' │ ') + 'AI-powered loop development');
28
+ console.log('');
29
+ }
15
30
  //# sourceMappingURL=header.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"header.js","sourceRoot":"","sources":["../../src/utils/header.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE;QAClB,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,CAAC,SAAS,CAAC;QACnB,aAAa,EAAE,CAAC;QAChB,UAAU,EAAE,CAAC;QACb,KAAK,EAAE,IAAI;QACX,SAAS,EAAE,CAAC;KACb,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"header.js","sourceRoot":"","sources":["../../src/utils/header.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/D;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,+BAA+B;IAC/B,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IACxF,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAEhB,mCAAmC;IACnC,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE;QACvB,IAAI,EAAE,OAAO;QACb,MAAM,EAAE,CAAC,cAAc,CAAC,MAAM,CAAC;QAC/B,aAAa,EAAE,CAAC;QAChB,UAAU,EAAE,CAAC;QACb,KAAK,EAAE,KAAK;QACZ,SAAS,EAAE,CAAC;KACb,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,6BAA6B,CAAC,CAAC;IAC5F,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AAClB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wiggum-cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "AI-powered feature development loop CLI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -32,7 +32,8 @@
32
32
  "ai": "^6.0.41",
33
33
  "cfonts": "^3.2.0",
34
34
  "commander": "^12.1.0",
35
- "picocolors": "^1.0.0"
35
+ "picocolors": "^1.0.0",
36
+ "zod": "^4.3.5"
36
37
  },
37
38
  "devDependencies": {
38
39
  "@types/node": "^20.10.0",
@@ -3,64 +3,61 @@
3
3
  * Uses AI to analyze the codebase for deeper insights
4
4
  */
5
5
 
6
- import { generateText } from 'ai';
6
+ import { generateText, stepCountIs } from 'ai';
7
7
  import type { ScanResult, DetectedStack, DetectionResult } from '../scanner/types.js';
8
8
  import { getModel, type AIProvider, hasApiKey, getApiKeyEnvVar } from './providers.js';
9
- import { SYSTEM_PROMPT, createAnalysisPrompt } from './prompts.js';
9
+ import { SYSTEM_PROMPT, SYSTEM_PROMPT_AGENTIC, createAnalysisPrompt } from './prompts.js';
10
+ import { createExplorationTools } from './tools.js';
10
11
  import { logger } from '../utils/logger.js';
11
12
 
12
13
  /**
13
- * Framework insights from AI analysis
14
+ * Project context from AI analysis - key structure information
14
15
  */
15
- export interface FrameworkInsights {
16
- variant?: string;
17
- confidence: 'high' | 'medium' | 'low';
18
- notes?: string;
16
+ export interface ProjectContext {
17
+ /** Key entry point files */
18
+ entryPoints?: string[];
19
+ /** Important directories and their purposes */
20
+ keyDirectories?: Record<string, string>;
21
+ /** Naming conventions used in the project */
22
+ namingConventions?: string;
19
23
  }
20
24
 
21
25
  /**
22
- * Architectural pattern detected by AI
26
+ * Detected commands from package.json scripts or common patterns
23
27
  */
24
- export interface ArchitecturalPattern {
25
- pattern: string;
26
- confidence: 'high' | 'medium' | 'low';
27
- evidence: string;
28
+ export interface DetectedCommands {
29
+ test?: string;
30
+ lint?: string;
31
+ typecheck?: string;
32
+ build?: string;
33
+ dev?: string;
34
+ format?: string;
28
35
  }
29
36
 
30
37
  /**
31
- * Coding convention detected by AI
38
+ * MCP server recommendations (categorized)
32
39
  */
33
- export interface CodingConvention {
34
- convention: string;
35
- suggestion: string;
40
+ export interface McpRecommendations {
41
+ /** Essential MCP servers for this stack */
42
+ essential?: string[];
43
+ /** Recommended but optional MCP servers */
44
+ recommended?: string[];
36
45
  }
37
46
 
38
47
  /**
39
- * MCP server recommendation
40
- */
41
- export interface McpRecommendation {
42
- name: string;
43
- reason: string;
44
- }
45
-
46
- /**
47
- * Additional detection suggestions
48
- */
49
- export interface AdditionalDetections {
50
- possibleMissed?: string[];
51
- refinements?: string[];
52
- }
53
-
54
- /**
55
- * AI analysis result
48
+ * AI analysis result - focused on actionable outputs
56
49
  */
57
50
  export interface AIAnalysisResult {
58
- frameworkInsights?: FrameworkInsights;
59
- architecturalPatterns?: ArchitecturalPattern[];
60
- codingConventions?: CodingConvention[];
61
- recommendedMcpServers?: McpRecommendation[];
62
- customPromptSuggestions?: string[];
63
- additionalDetections?: AdditionalDetections;
51
+ /** Project structure and context */
52
+ projectContext?: ProjectContext;
53
+ /** Detected commands from package.json */
54
+ commands?: DetectedCommands;
55
+ /** Short, actionable implementation guidelines */
56
+ implementationGuidelines?: string[];
57
+ /** MCP server recommendations */
58
+ mcpServers?: McpRecommendations;
59
+ /** Additional technologies that may have been missed */
60
+ possibleMissedTechnologies?: string[];
64
61
  }
65
62
 
66
63
  /**
@@ -80,6 +77,8 @@ export interface EnhancerOptions {
80
77
  provider?: AIProvider;
81
78
  model?: string;
82
79
  verbose?: boolean;
80
+ /** Use agentic mode with tools for deeper codebase exploration */
81
+ agentic?: boolean;
83
82
  }
84
83
 
85
84
  /**
@@ -119,37 +118,24 @@ function applyEnhancements(
119
118
  ): DetectedStack {
120
119
  const enhanced = { ...stack };
121
120
 
122
- // Enhance framework detection with AI insights
123
- if (analysis.frameworkInsights && enhanced.framework) {
124
- // If AI detected a more specific variant with high confidence
125
- if (
126
- analysis.frameworkInsights.variant &&
127
- analysis.frameworkInsights.confidence === 'high'
128
- ) {
129
- enhanced.framework = {
130
- ...enhanced.framework,
131
- variant: analysis.frameworkInsights.variant,
132
- evidence: [
133
- ...enhanced.framework.evidence,
134
- `AI: ${analysis.frameworkInsights.notes || 'variant detected'}`,
121
+ // Enhance MCP recommendations from AI analysis
122
+ if (analysis.mcpServers) {
123
+ const aiRecommended = [
124
+ ...(analysis.mcpServers.essential || []),
125
+ ...(analysis.mcpServers.recommended || []),
126
+ ];
127
+
128
+ if (aiRecommended.length > 0) {
129
+ enhanced.mcp = {
130
+ ...enhanced.mcp,
131
+ recommended: [
132
+ ...(enhanced.mcp?.recommended || []),
133
+ ...aiRecommended.filter(r => !enhanced.mcp?.recommended?.includes(r)),
135
134
  ],
136
135
  };
137
136
  }
138
137
  }
139
138
 
140
- // Enhance MCP recommendations
141
- if (analysis.recommendedMcpServers && analysis.recommendedMcpServers.length > 0) {
142
- const aiRecommended = analysis.recommendedMcpServers.map(r => r.name);
143
-
144
- enhanced.mcp = {
145
- ...enhanced.mcp,
146
- recommended: [
147
- ...(enhanced.mcp?.recommended || []),
148
- ...aiRecommended.filter(r => !enhanced.mcp?.recommended?.includes(r)),
149
- ],
150
- };
151
- }
152
-
153
139
  return enhanced;
154
140
  }
155
141
 
@@ -161,11 +147,13 @@ export class AIEnhancer {
161
147
  private provider: AIProvider;
162
148
  private model?: string;
163
149
  private verbose: boolean;
150
+ private agentic: boolean;
164
151
 
165
152
  constructor(options: EnhancerOptions = {}) {
166
153
  this.provider = options.provider || 'anthropic';
167
154
  this.model = options.model;
168
155
  this.verbose = options.verbose || false;
156
+ this.agentic = options.agentic || false;
169
157
  }
170
158
 
171
159
  /**
@@ -202,22 +190,20 @@ export class AIEnhancer {
202
190
 
203
191
  if (this.verbose) {
204
192
  logger.info(`Using AI provider: ${provider} (${modelId})`);
193
+ if (this.agentic) {
194
+ logger.info('Agentic mode enabled - AI will explore the codebase with tools');
195
+ }
205
196
  }
206
197
 
207
- // Create the analysis prompt
208
- const prompt = createAnalysisPrompt(scanResult);
209
-
210
- // Call the AI model
211
- const { text } = await generateText({
212
- model,
213
- system: SYSTEM_PROMPT,
214
- prompt,
215
- maxOutputTokens: 2000,
216
- temperature: 0.3, // Lower temperature for more consistent output
217
- });
198
+ let analysis: AIAnalysisResult | null;
218
199
 
219
- // Parse the response
220
- const analysis = parseAIResponse(text);
200
+ if (this.agentic) {
201
+ // Agentic mode: use tools to explore codebase
202
+ analysis = await this.enhanceAgentic(model, scanResult);
203
+ } else {
204
+ // Simple mode: just analyze detected stack
205
+ analysis = await this.enhanceSimple(model, scanResult);
206
+ }
221
207
 
222
208
  if (!analysis) {
223
209
  return {
@@ -250,6 +236,80 @@ export class AIEnhancer {
250
236
  };
251
237
  }
252
238
  }
239
+
240
+ /**
241
+ * Simple enhancement mode - analyze detected stack without tools
242
+ */
243
+ private async enhanceSimple(
244
+ model: ReturnType<typeof getModel>['model'],
245
+ scanResult: ScanResult
246
+ ): Promise<AIAnalysisResult | null> {
247
+ const prompt = createAnalysisPrompt(scanResult);
248
+
249
+ const { text } = await generateText({
250
+ model,
251
+ system: SYSTEM_PROMPT,
252
+ prompt,
253
+ maxOutputTokens: 2000,
254
+ temperature: 0.3,
255
+ });
256
+
257
+ return parseAIResponse(text);
258
+ }
259
+
260
+ /**
261
+ * Agentic enhancement mode - use tools to explore codebase
262
+ */
263
+ private async enhanceAgentic(
264
+ model: ReturnType<typeof getModel>['model'],
265
+ scanResult: ScanResult
266
+ ): Promise<AIAnalysisResult | null> {
267
+ const tools = createExplorationTools(scanResult.projectRoot);
268
+
269
+ const prompt = `Analyze this codebase and produce configuration for AI-assisted development.
270
+
271
+ Project: ${scanResult.projectRoot}
272
+
273
+ Start by exploring the codebase structure, then produce your analysis.
274
+ When done exploring, output your final analysis as valid JSON matching this structure:
275
+
276
+ {
277
+ "projectContext": {
278
+ "entryPoints": ["src/index.ts"],
279
+ "keyDirectories": {"src/routes": "API routes"},
280
+ "namingConventions": "camelCase files, PascalCase components"
281
+ },
282
+ "commands": {
283
+ "test": "npm test",
284
+ "lint": "npm run lint",
285
+ "build": "npm run build",
286
+ "dev": "npm run dev"
287
+ },
288
+ "implementationGuidelines": [
289
+ "Run npm test after changes",
290
+ "Use Zod for validation"
291
+ ],
292
+ "mcpServers": {
293
+ "essential": ["filesystem", "git"],
294
+ "recommended": ["docker"]
295
+ },
296
+ "possibleMissedTechnologies": ["Redis"]
297
+ }`;
298
+
299
+ // Use agentic loop - AI will call tools until it has enough info
300
+ // stopWhen: stepCountIs(10) allows up to 10 tool-calling steps
301
+ const { text } = await generateText({
302
+ model,
303
+ system: SYSTEM_PROMPT_AGENTIC,
304
+ prompt,
305
+ tools,
306
+ stopWhen: stepCountIs(10),
307
+ maxOutputTokens: 4000,
308
+ temperature: 0.3,
309
+ });
310
+
311
+ return parseAIResponse(text);
312
+ }
253
313
  }
254
314
 
255
315
  /**
@@ -269,85 +329,73 @@ export async function enhanceWithAI(
269
329
  export function formatAIAnalysis(analysis: AIAnalysisResult): string {
270
330
  const lines: string[] = [];
271
331
 
272
- lines.push('=== AI Analysis ===');
273
- lines.push('');
332
+ // Project context
333
+ if (analysis.projectContext) {
334
+ const ctx = analysis.projectContext;
274
335
 
275
- // Framework insights
276
- if (analysis.frameworkInsights) {
277
- lines.push('Framework Insights:');
278
- if (analysis.frameworkInsights.variant) {
279
- lines.push(` Variant: ${analysis.frameworkInsights.variant}`);
280
- }
281
- lines.push(` Confidence: ${analysis.frameworkInsights.confidence}`);
282
- if (analysis.frameworkInsights.notes) {
283
- lines.push(` Notes: ${analysis.frameworkInsights.notes}`);
336
+ if (ctx.entryPoints && ctx.entryPoints.length > 0) {
337
+ lines.push('Entry Points:');
338
+ for (const entry of ctx.entryPoints) {
339
+ lines.push(` ${entry}`);
340
+ }
341
+ lines.push('');
284
342
  }
285
- lines.push('');
286
- }
287
343
 
288
- // Architectural patterns
289
- if (analysis.architecturalPatterns && analysis.architecturalPatterns.length > 0) {
290
- lines.push('Architectural Patterns:');
291
- for (const pattern of analysis.architecturalPatterns) {
292
- lines.push(` - ${pattern.pattern} [${pattern.confidence}]`);
293
- lines.push(` Evidence: ${pattern.evidence}`);
344
+ if (ctx.keyDirectories && Object.keys(ctx.keyDirectories).length > 0) {
345
+ lines.push('Key Directories:');
346
+ for (const [dir, purpose] of Object.entries(ctx.keyDirectories)) {
347
+ lines.push(` ${dir} ${purpose}`);
348
+ }
349
+ lines.push('');
294
350
  }
295
- lines.push('');
296
- }
297
351
 
298
- // Coding conventions
299
- if (analysis.codingConventions && analysis.codingConventions.length > 0) {
300
- lines.push('Coding Conventions:');
301
- for (const convention of analysis.codingConventions) {
302
- lines.push(` - ${convention.convention}`);
303
- lines.push(` Suggestion: ${convention.suggestion}`);
352
+ if (ctx.namingConventions) {
353
+ lines.push(`Naming: ${ctx.namingConventions}`);
354
+ lines.push('');
304
355
  }
305
- lines.push('');
306
356
  }
307
357
 
308
- // MCP recommendations
309
- if (analysis.recommendedMcpServers && analysis.recommendedMcpServers.length > 0) {
310
- lines.push('Recommended MCP Servers:');
311
- for (const server of analysis.recommendedMcpServers) {
312
- lines.push(` - ${server.name}`);
313
- lines.push(` Reason: ${server.reason}`);
358
+ // Detected commands
359
+ if (analysis.commands) {
360
+ const cmds = analysis.commands;
361
+ const cmdList = Object.entries(cmds).filter(([_, v]) => v);
362
+
363
+ if (cmdList.length > 0) {
364
+ lines.push('Commands:');
365
+ for (const [name, cmd] of cmdList) {
366
+ lines.push(` ${name}: ${cmd}`);
367
+ }
368
+ lines.push('');
314
369
  }
315
- lines.push('');
316
370
  }
317
371
 
318
- // Custom prompt suggestions
319
- if (analysis.customPromptSuggestions && analysis.customPromptSuggestions.length > 0) {
320
- lines.push('Custom Prompt Suggestions:');
321
- for (const suggestion of analysis.customPromptSuggestions) {
322
- lines.push(` - ${suggestion}`);
372
+ // Implementation guidelines (the most important part)
373
+ if (analysis.implementationGuidelines && analysis.implementationGuidelines.length > 0) {
374
+ lines.push('Implementation Guidelines:');
375
+ for (const guideline of analysis.implementationGuidelines) {
376
+ lines.push(` ${guideline}`);
323
377
  }
324
378
  lines.push('');
325
379
  }
326
380
 
327
- // Additional detections
328
- if (analysis.additionalDetections) {
329
- if (
330
- analysis.additionalDetections.possibleMissed &&
331
- analysis.additionalDetections.possibleMissed.length > 0
332
- ) {
333
- lines.push('Possibly Missed Technologies:');
334
- for (const tech of analysis.additionalDetections.possibleMissed) {
335
- lines.push(` - ${tech}`);
336
- }
337
- lines.push('');
381
+ // MCP servers
382
+ if (analysis.mcpServers) {
383
+ if (analysis.mcpServers.essential && analysis.mcpServers.essential.length > 0) {
384
+ lines.push(`MCP (essential): ${analysis.mcpServers.essential.join(', ')}`);
338
385
  }
339
-
340
- if (
341
- analysis.additionalDetections.refinements &&
342
- analysis.additionalDetections.refinements.length > 0
343
- ) {
344
- lines.push('Detection Refinements:');
345
- for (const refinement of analysis.additionalDetections.refinements) {
346
- lines.push(` - ${refinement}`);
347
- }
386
+ if (analysis.mcpServers.recommended && analysis.mcpServers.recommended.length > 0) {
387
+ lines.push(`MCP (optional): ${analysis.mcpServers.recommended.join(', ')}`);
388
+ }
389
+ if (analysis.mcpServers.essential?.length || analysis.mcpServers.recommended?.length) {
348
390
  lines.push('');
349
391
  }
350
392
  }
351
393
 
394
+ // Possibly missed technologies (brief)
395
+ if (analysis.possibleMissedTechnologies && analysis.possibleMissedTechnologies.length > 0) {
396
+ lines.push(`May also use: ${analysis.possibleMissedTechnologies.join(', ')}`);
397
+ lines.push('');
398
+ }
399
+
352
400
  return lines.join('\n');
353
401
  }
package/src/ai/index.ts CHANGED
@@ -17,18 +17,23 @@ export {
17
17
  export {
18
18
  formatStackForPrompt,
19
19
  SYSTEM_PROMPT,
20
+ SYSTEM_PROMPT_AGENTIC,
20
21
  createAnalysisPrompt,
21
22
  createValidationPrompt,
22
23
  createRecommendationsPrompt,
23
24
  } from './prompts.js';
24
25
 
26
+ // Tools for agentic exploration
27
+ export {
28
+ createExplorationTools,
29
+ RIPGREP_SKILL,
30
+ } from './tools.js';
31
+
25
32
  // AI enhancer
26
33
  export {
27
- type FrameworkInsights,
28
- type ArchitecturalPattern,
29
- type CodingConvention,
30
- type McpRecommendation,
31
- type AdditionalDetections,
34
+ type ProjectContext,
35
+ type DetectedCommands,
36
+ type McpRecommendations,
32
37
  type AIAnalysisResult,
33
38
  type EnhancedScanResult,
34
39
  type EnhancerOptions,