wiggum-cli 0.2.2 → 0.2.5

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.
@@ -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
- import { getModel, type AIProvider, hasApiKey, getApiKeyEnvVar } from './providers.js';
9
- import { SYSTEM_PROMPT, createAnalysisPrompt } from './prompts.js';
8
+ import { getModel, type AIProvider, hasApiKey, getApiKeyEnvVar, isReasoningModel } from './providers.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, modelId, scanResult);
203
+ } else {
204
+ // Simple mode: just analyze detected stack
205
+ analysis = await this.enhanceSimple(model, modelId, scanResult);
206
+ }
221
207
 
222
208
  if (!analysis) {
223
209
  return {
@@ -250,6 +236,84 @@ 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
+ modelId: string,
246
+ scanResult: ScanResult
247
+ ): Promise<AIAnalysisResult | null> {
248
+ const prompt = createAnalysisPrompt(scanResult);
249
+
250
+ const { text } = await generateText({
251
+ model,
252
+ system: SYSTEM_PROMPT,
253
+ prompt,
254
+ maxOutputTokens: 2000,
255
+ // Reasoning models don't support temperature
256
+ ...(isReasoningModel(modelId) ? {} : { temperature: 0.3 }),
257
+ });
258
+
259
+ return parseAIResponse(text);
260
+ }
261
+
262
+ /**
263
+ * Agentic enhancement mode - use tools to explore codebase
264
+ */
265
+ private async enhanceAgentic(
266
+ model: ReturnType<typeof getModel>['model'],
267
+ modelId: string,
268
+ scanResult: ScanResult
269
+ ): Promise<AIAnalysisResult | null> {
270
+ const tools = createExplorationTools(scanResult.projectRoot);
271
+
272
+ const prompt = `Analyze this codebase and produce configuration for AI-assisted development.
273
+
274
+ Project: ${scanResult.projectRoot}
275
+
276
+ Start by exploring the codebase structure, then produce your analysis.
277
+ When done exploring, output your final analysis as valid JSON matching this structure:
278
+
279
+ {
280
+ "projectContext": {
281
+ "entryPoints": ["src/index.ts"],
282
+ "keyDirectories": {"src/routes": "API routes"},
283
+ "namingConventions": "camelCase files, PascalCase components"
284
+ },
285
+ "commands": {
286
+ "test": "npm test",
287
+ "lint": "npm run lint",
288
+ "build": "npm run build",
289
+ "dev": "npm run dev"
290
+ },
291
+ "implementationGuidelines": [
292
+ "Run npm test after changes",
293
+ "Use Zod for validation"
294
+ ],
295
+ "mcpServers": {
296
+ "essential": ["filesystem", "git"],
297
+ "recommended": ["docker"]
298
+ },
299
+ "possibleMissedTechnologies": ["Redis"]
300
+ }`;
301
+
302
+ // Use agentic loop - AI will call tools until it has enough info
303
+ // stopWhen: stepCountIs(10) allows up to 10 tool-calling steps
304
+ const { text } = await generateText({
305
+ model,
306
+ system: SYSTEM_PROMPT_AGENTIC,
307
+ prompt,
308
+ tools,
309
+ stopWhen: stepCountIs(10),
310
+ maxOutputTokens: 4000,
311
+ // Reasoning models don't support temperature
312
+ ...(isReasoningModel(modelId) ? {} : { temperature: 0.3 }),
313
+ });
314
+
315
+ return parseAIResponse(text);
316
+ }
253
317
  }
254
318
 
255
319
  /**
@@ -269,85 +333,73 @@ export async function enhanceWithAI(
269
333
  export function formatAIAnalysis(analysis: AIAnalysisResult): string {
270
334
  const lines: string[] = [];
271
335
 
272
- lines.push('=== AI Analysis ===');
273
- lines.push('');
336
+ // Project context
337
+ if (analysis.projectContext) {
338
+ const ctx = analysis.projectContext;
274
339
 
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}`);
340
+ if (ctx.entryPoints && ctx.entryPoints.length > 0) {
341
+ lines.push('Entry Points:');
342
+ for (const entry of ctx.entryPoints) {
343
+ lines.push(` ${entry}`);
344
+ }
345
+ lines.push('');
284
346
  }
285
- lines.push('');
286
- }
287
347
 
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}`);
348
+ if (ctx.keyDirectories && Object.keys(ctx.keyDirectories).length > 0) {
349
+ lines.push('Key Directories:');
350
+ for (const [dir, purpose] of Object.entries(ctx.keyDirectories)) {
351
+ lines.push(` ${dir} ${purpose}`);
352
+ }
353
+ lines.push('');
294
354
  }
295
- lines.push('');
296
- }
297
355
 
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}`);
356
+ if (ctx.namingConventions) {
357
+ lines.push(`Naming: ${ctx.namingConventions}`);
358
+ lines.push('');
304
359
  }
305
- lines.push('');
306
360
  }
307
361
 
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}`);
362
+ // Detected commands
363
+ if (analysis.commands) {
364
+ const cmds = analysis.commands;
365
+ const cmdList = Object.entries(cmds).filter(([_, v]) => v);
366
+
367
+ if (cmdList.length > 0) {
368
+ lines.push('Commands:');
369
+ for (const [name, cmd] of cmdList) {
370
+ lines.push(` ${name}: ${cmd}`);
371
+ }
372
+ lines.push('');
314
373
  }
315
- lines.push('');
316
374
  }
317
375
 
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}`);
376
+ // Implementation guidelines (the most important part)
377
+ if (analysis.implementationGuidelines && analysis.implementationGuidelines.length > 0) {
378
+ lines.push('Implementation Guidelines:');
379
+ for (const guideline of analysis.implementationGuidelines) {
380
+ lines.push(` ${guideline}`);
323
381
  }
324
382
  lines.push('');
325
383
  }
326
384
 
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('');
385
+ // MCP servers
386
+ if (analysis.mcpServers) {
387
+ if (analysis.mcpServers.essential && analysis.mcpServers.essential.length > 0) {
388
+ lines.push(`MCP (essential): ${analysis.mcpServers.essential.join(', ')}`);
338
389
  }
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
- }
390
+ if (analysis.mcpServers.recommended && analysis.mcpServers.recommended.length > 0) {
391
+ lines.push(`MCP (optional): ${analysis.mcpServers.recommended.join(', ')}`);
392
+ }
393
+ if (analysis.mcpServers.essential?.length || analysis.mcpServers.recommended?.length) {
348
394
  lines.push('');
349
395
  }
350
396
  }
351
397
 
398
+ // Possibly missed technologies (brief)
399
+ if (analysis.possibleMissedTechnologies && analysis.possibleMissedTechnologies.length > 0) {
400
+ lines.push(`May also use: ${analysis.possibleMissedTechnologies.join(', ')}`);
401
+ lines.push('');
402
+ }
403
+
352
404
  return lines.join('\n');
353
405
  }
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,
package/src/ai/prompts.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import type { ScanResult, DetectedStack } from '../scanner/types.js';
7
+ import { RIPGREP_SKILL } from './tools.js';
7
8
 
8
9
  /**
9
10
  * Format the detected stack for inclusion in prompts
@@ -87,20 +88,52 @@ export function formatStackForPrompt(stack: DetectedStack): string {
87
88
  }
88
89
 
89
90
  /**
90
- * System prompt for codebase analysis
91
+ * System prompt for codebase analysis (agentic mode)
91
92
  */
92
- export const SYSTEM_PROMPT = `You are an expert software architect analyzing a codebase to provide insights for AI-assisted development.
93
+ export const SYSTEM_PROMPT_AGENTIC = `You are an expert codebase analyst with tools to explore the project.
93
94
 
94
- Your role is to:
95
- 1. Analyze the detected tech stack and provide deeper insights
96
- 2. Identify architectural patterns and coding conventions
97
- 3. Suggest improvements to the detection results
98
- 4. Recommend MCP servers that would benefit this project
99
- 5. Generate custom prompt suggestions for AI coding assistants
95
+ Your goal is to thoroughly understand the codebase structure and produce actionable configuration for AI-assisted development.
100
96
 
101
- Be concise and actionable. Focus on practical insights that help developers work more effectively with AI tools.
97
+ ## Exploration Strategy
98
+ 1. First, list the root directory to understand project structure
99
+ 2. Read package.json to understand scripts and dependencies
100
+ 3. Search for key patterns: entry points, routes, components, tests
101
+ 4. Identify naming conventions by examining existing files
102
+ 5. Look for existing documentation (.md files, README)
102
103
 
103
- Respond in valid JSON format only.`;
104
+ ## Tools Available
105
+ You have these tools to explore the codebase:
106
+ - searchCode: Search using ripgrep patterns
107
+ - readFile: Read file contents
108
+ - listDirectory: List directory structure
109
+ - getPackageInfo: Get package.json info
110
+
111
+ ${RIPGREP_SKILL}
112
+
113
+ ## Output Requirements
114
+ After exploration, output valid JSON with:
115
+ - projectContext: entry points, key directories, naming conventions
116
+ - commands: test, lint, build, dev commands from package.json
117
+ - implementationGuidelines: short actionable rules (5-10 words each, max 7)
118
+ - mcpServers: essential and recommended servers
119
+ - possibleMissedTechnologies: technologies that might be in use
120
+
121
+ Be concise. Focus on WHAT TO DO, not what exists.`;
122
+
123
+ /**
124
+ * System prompt for codebase analysis (simple mode - no tools)
125
+ */
126
+ export const SYSTEM_PROMPT = `You are analyzing a codebase to help configure AI-assisted development tools.
127
+
128
+ Your goal is to produce SHORT, ACTIONABLE output that helps AI coding assistants work effectively on this codebase.
129
+
130
+ Rules:
131
+ - Output valid JSON only
132
+ - Be extremely concise (5-10 words per item max)
133
+ - Focus on WHAT TO DO, not what exists
134
+ - Include specific file paths and commands
135
+ - Max 5-7 items per array
136
+ - No explanations, just actionable rules`;
104
137
 
105
138
  /**
106
139
  * Create the codebase analysis prompt
@@ -108,49 +141,48 @@ Respond in valid JSON format only.`;
108
141
  export function createAnalysisPrompt(scanResult: ScanResult): string {
109
142
  const stackInfo = formatStackForPrompt(scanResult.stack);
110
143
 
111
- return `Analyze this codebase and provide enhanced insights.
144
+ return `Analyze this codebase for AI-assisted development configuration.
112
145
 
113
- Project Root: ${scanResult.projectRoot}
146
+ Project: ${scanResult.projectRoot}
114
147
 
115
148
  Detected Stack:
116
149
  ${stackInfo || 'No technologies detected'}
117
150
 
118
- Based on this stack, provide analysis in the following JSON format:
151
+ Respond with this JSON structure (keep values SHORT - 5-10 words max per item):
119
152
  {
120
- "frameworkInsights": {
121
- "variant": "more specific variant if detectable (e.g., 'app-router', 'pages-router', 'spa', 'ssr')",
122
- "confidence": "high/medium/low",
123
- "notes": "any additional observations about framework usage"
153
+ "projectContext": {
154
+ "entryPoints": ["src/index.ts", "src/server.ts"],
155
+ "keyDirectories": {
156
+ "src/routes": "API route handlers",
157
+ "src/models": "Database models"
158
+ },
159
+ "namingConventions": "camelCase files, PascalCase components"
124
160
  },
125
- "architecturalPatterns": [
126
- {
127
- "pattern": "pattern name",
128
- "confidence": "high/medium/low",
129
- "evidence": "why you think this pattern is used"
130
- }
131
- ],
132
- "codingConventions": [
133
- {
134
- "convention": "convention name",
135
- "suggestion": "how to follow this convention"
136
- }
137
- ],
138
- "recommendedMcpServers": [
139
- {
140
- "name": "server name",
141
- "reason": "why this would be useful"
142
- }
143
- ],
144
- "customPromptSuggestions": [
145
- "Specific prompt suggestions tailored to this codebase"
161
+ "commands": {
162
+ "test": "npm test",
163
+ "lint": "npm run lint",
164
+ "typecheck": "npm run typecheck",
165
+ "build": "npm run build",
166
+ "dev": "npm run dev"
167
+ },
168
+ "implementationGuidelines": [
169
+ "Run npm test after every change",
170
+ "Use Zod for request validation",
171
+ "Place routes in src/routes/<resource>.ts",
172
+ "Follow error pattern in src/utils/errors.ts"
146
173
  ],
147
- "additionalDetections": {
148
- "possibleMissed": ["technologies that might be in use but weren't detected"],
149
- "refinements": ["suggestions to improve existing detections"]
150
- }
174
+ "mcpServers": {
175
+ "essential": ["filesystem", "git"],
176
+ "recommended": ["docker", "postgres"]
177
+ },
178
+ "possibleMissedTechnologies": ["Redis", "WebSockets"]
151
179
  }
152
180
 
153
- Only include sections where you have meaningful insights. Keep responses focused and actionable.`;
181
+ Important:
182
+ - implementationGuidelines should be short rules (not analysis prompts)
183
+ - Include actual file paths from this project
184
+ - Infer commands from package.json patterns
185
+ - Max 5-7 items per array`;
154
186
  }
155
187
 
156
188
  /**
@@ -166,3 +166,21 @@ export function getAvailableProvider(): AIProvider | null {
166
166
  export function getApiKeyEnvVar(provider: AIProvider): string {
167
167
  return API_KEY_ENV_VARS[provider];
168
168
  }
169
+
170
+ /**
171
+ * Models that don't support temperature parameter (reasoning models)
172
+ */
173
+ const REASONING_MODELS = [
174
+ // OpenAI reasoning models
175
+ 'o1', 'o1-mini', 'o1-preview',
176
+ 'o3', 'o3-mini',
177
+ 'gpt-5', 'gpt-5.1', 'gpt-5-mini',
178
+ 'gpt-5.1-codex', 'gpt-5.1-codex-max',
179
+ ];
180
+
181
+ /**
182
+ * Check if a model is a reasoning model that doesn't support temperature
183
+ */
184
+ export function isReasoningModel(modelId: string): boolean {
185
+ return REASONING_MODELS.some(m => modelId.startsWith(m));
186
+ }