winter-super-cli 2026.5.27 → 2026.5.28

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "winter-super-cli",
3
- "version": "2026.5.27",
3
+ "version": "2026.5.28",
4
4
  "description": "❄️ AI-Powered Development CLI with Interactive REPL",
5
5
  "type": "module",
6
6
  "main": "bin/winter.js",
@@ -0,0 +1,185 @@
1
+ /**
2
+ * ❄️ MODEL CAPABILITIES ❄️
3
+ * Detect AI model capability tier from model name.
4
+ * Small models need aggressive prompting to compete with large ones.
5
+ */
6
+
7
+ export const MODEL_TIERS = {
8
+ TINY: 'tiny', // <3B params — barely functional for code
9
+ SMALL: 'small', // 3B-15B params — basic code ability
10
+ MEDIUM: 'medium', // 15B-40B params — decent code ability
11
+ LARGE: 'large', // 40B-120B params — strong code ability, could be flagship
12
+ FLAGSHIP: 'flagship', // 120B+ or proprietary frontier models
13
+ };
14
+
15
+ /**
16
+ * Ordered tiers from weakest to strongest (for comparison).
17
+ */
18
+ const TIER_ORDER = [MODEL_TIERS.TINY, MODEL_TIERS.SMALL, MODEL_TIERS.MEDIUM, MODEL_TIERS.LARGE, MODEL_TIERS.FLAGSHIP];
19
+
20
+ /**
21
+ * Classify a model name into a capability tier.
22
+ * @param {string} modelName - e.g. "llama3", "gpt-4", "qwen2.5:7b"
23
+ * @param {string} [provider] - e.g. "ollama", "openai" (optional, helps disambiguate)
24
+ * @returns {string} One of MODEL_TIERS
25
+ */
26
+ export function classifyModelTier(modelName, provider = '') {
27
+ const name = (modelName || '').toLowerCase().trim();
28
+ const prov = (provider || '').toLowerCase().trim();
29
+
30
+ // ===== FLAGSHIP (frontier models) =====
31
+ const flagshipPatterns = [
32
+ /claude-3-5-sonnet/i, /claude-opus/i, /claude-4/i, /claude-sonnet-4/i,
33
+ /gpt-4o/i, /gpt-4-turbo/i, /o1/i, /o3/i,
34
+ /gemini-2\.5-pro/i, /gemini-2\.0-ultra/i,
35
+ /deepseek-v3/i, /deepseek-r1/i,
36
+ /llama-4/i, /llama-3-70b/i, /llama3-70b/i, /llama3\.1-70b/i, /llama3\.2-90b/i, /llama3\.3/i,
37
+ /qwen2\.5-?72b/i, /qwen2\.5-?70b/i, /qwen-?2\.5-?72b/i,
38
+ /mistral-large/i, /mixtral-8x22b/i,
39
+ /command-r-plus/i, /command-a/i,
40
+ /yi-?34b/i,
41
+ /dbrx-instruct/i,
42
+ ];
43
+
44
+ // If using a cloud provider like OpenAI/Anthropic/Groq, their default models are typically large+
45
+ if (prov === 'openai' || prov === 'anthropic' || prov === 'claude') {
46
+ if (name.includes('gpt-3.5') || name.includes('gpt-3')) return MODEL_TIERS.MEDIUM;
47
+ if (name.includes('claude-3-haiku') || name.includes('claude-3-5-haiku')) return MODEL_TIERS.MEDIUM;
48
+ return MODEL_TIERS.LARGE; // Default for OpenAI/Anthropic is >= gpt-4 level
49
+ }
50
+
51
+ if (prov === 'groq') {
52
+ // Groq runs open models, most are large but some are not
53
+ if (/llama.*8b|llama3.*8b|llama3\.2.*3b/i.test(name)) return MODEL_TIERS.SMALL;
54
+ if (/gemma2.*9b/i.test(name)) return MODEL_TIERS.SMALL;
55
+ if (/mixtral-8x7|llama.*70b|llama3.*70b|llama3\.1.*70b|qwen/i.test(name)) return MODEL_TIERS.LARGE;
56
+ return MODEL_TIERS.MEDIUM; // Default for Groq
57
+ }
58
+
59
+ // Check patterns for any provider
60
+ for (const pattern of flagshipPatterns) {
61
+ if (pattern.test(name)) return MODEL_TIERS.FLAGSHIP;
62
+ }
63
+
64
+ // ===== LARGE MODELS =====
65
+ const largePatterns = [
66
+ /claude-sonnet/i, /claude-3/i, /claude-2/i,
67
+ /gpt-4/i, /gpt-4-32k/i,
68
+ /llama-3\.1-?70b/i, /llama-3\.2-?70b/i, /llama3-?70b/i,
69
+ /llama-2-?70b/i,
70
+ /qwen-?2\.5-?32b/i, /qwen-?2-?72b/i,
71
+ /codellama-?70b/i,
72
+ /mixtral/i,
73
+ /deepseek-?v2/i,
74
+ /gemini-1\.5-pro/i, /gemini-2\.0-flash/i,
75
+ /command-r/i,
76
+ /yi-?34b/i,
77
+ /mistral-medium/i,
78
+ ];
79
+
80
+ for (const pattern of largePatterns) {
81
+ if (pattern.test(name)) return MODEL_TIERS.LARGE;
82
+ }
83
+
84
+ // ===== MEDIUM MODELS =====
85
+ const mediumPatterns = [
86
+ /qwen-?2\.5-?14b/i, /qwen-?2\.5-?7b/i, /qwen-?2/i,
87
+ /llama-3-?8b/i, /llama-3\.1-?8b/i, /llama-3\.2-?11b/i,
88
+ /llama-2-?13b/i, /llama-2-?7b/i,
89
+ /deepseek-coder-?6\.7b/i, /deepseek-coder-?33b/i,
90
+ /codellama-?34b/i, /codellama-?13b/i, /codellama-?7b/i,
91
+ /mistral/i, /mistral-7b/i,
92
+ /gemma-2-?9b/i, /gemma-?7b/i,
93
+ /phi-3/i, /phi-3-medium/i,
94
+ /nemotron/i,
95
+ /solar/i,
96
+ /dbrx/i,
97
+ /starcoder2/i,
98
+ /deepseek-llm/i,
99
+ /yi-?6b/i, /yi-?9b/i,
100
+ ];
101
+
102
+ for (const pattern of mediumPatterns) {
103
+ if (pattern.test(name)) return MODEL_TIERS.MEDIUM;
104
+ }
105
+
106
+ // ===== SMALL MODELS =====
107
+ const smallPatterns = [
108
+ /llama-3\.2-?3b/i, /llama-3\.2-?1b/i, /tinyllama/i,
109
+ /qwen-?2\.5-?3b/i, /qwen-?2\.5-?1\.5b/i, /qwen-?2\.5-?0\.5b/i,
110
+ /phi-?3-?mini/i, /phi-?2/i, /phi-?1/i,
111
+ /gemma-?2-?2b/i,
112
+ /stablelm/i,
113
+ /orca/i,
114
+ /falcon/i,
115
+ /red-pajama/i,
116
+ /pythia/i,
117
+ /opt/i,
118
+ /bloom/i,
119
+ /mpnet/i,
120
+ ];
121
+
122
+ for (const pattern of smallPatterns) {
123
+ if (pattern.test(name)) return MODEL_TIERS.SMALL;
124
+ }
125
+
126
+ if (/tiny/i.test(name) || /mini/i.test(name) || /small/i.test(name) || /nano/i.test(name)) {
127
+ return MODEL_TIERS.TINY;
128
+ }
129
+
130
+ // Fallback: if Ollama, likely small
131
+ if (prov === 'ollama' || prov === 'local') return MODEL_TIERS.SMALL;
132
+
133
+ // Default: assume medium
134
+ return MODEL_TIERS.MEDIUM;
135
+ }
136
+
137
+ /**
138
+ * Check if a model tier is considered "small" (needs aggressive prompting).
139
+ */
140
+ export function isSmallModel(tier) {
141
+ return tier === MODEL_TIERS.TINY || tier === MODEL_TIERS.SMALL;
142
+ }
143
+
144
+ /**
145
+ * Get the index of a tier in the order array (0=weakest).
146
+ * @private
147
+ */
148
+ function tierIndex(tier) {
149
+ const idx = TIER_ORDER.indexOf(tier);
150
+ return idx >= 0 ? idx : 2; // Default to medium index
151
+ }
152
+
153
+ /**
154
+ * Compare two tiers. Returns negative if a < b, positive if a > b, 0 if equal.
155
+ * @private
156
+ */
157
+ function compareTiers(a, b) {
158
+ return tierIndex(a) - tierIndex(b);
159
+ }
160
+
161
+ /**
162
+ * Get recommended reasoning level bump for small models.
163
+ * Small models need more aggressive reasoning prompting to compensate.
164
+ */
165
+ export function getReasoningBump(tier) {
166
+ switch (tier) {
167
+ case MODEL_TIERS.TINY: return 2; // bump 2 levels
168
+ case MODEL_TIERS.SMALL: return 1; // bump 1 level
169
+ default: return 0;
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Build a short string describing model capability for system prompt injection.
175
+ */
176
+ export function getModelCapabilityLabel(tier) {
177
+ switch (tier) {
178
+ case MODEL_TIERS.TINY: return 'tiny local model — needs maximum guidance';
179
+ case MODEL_TIERS.SMALL: return 'small local model — needs extra structure';
180
+ case MODEL_TIERS.MEDIUM: return 'medium-capability model';
181
+ case MODEL_TIERS.LARGE: return 'high-capability model';
182
+ case MODEL_TIERS.FLAGSHIP: return 'frontier model — full capability expected';
183
+ default: return '';
184
+ }
185
+ }
@@ -1,9 +1,13 @@
1
1
  /**
2
2
  * Dynamic System Prompt Builder
3
3
  * Builds context-aware system prompts based on task, role, and session state.
4
+ * Small models get aggressive structural guidance to compensate for limited capability.
4
5
  */
5
6
 
7
+ import { isSmallModel, getModelCapabilityLabel } from '../model-capabilities.js';
8
+
6
9
  const BASE_PRINCIPLES = [
10
+ 'Execute, don\'t describe - Do the work, don\'t write plans about doing the work',
7
11
  'Think Before Coding - State assumptions, ask when unclear',
8
12
  'Simplicity First - Minimum code that solves the problem',
9
13
  'Surgical Changes - Touch only what you must',
@@ -36,15 +40,36 @@ function buildEnvironmentSummary() {
36
40
  ].join('\n');
37
41
  }
38
42
 
39
- export function buildSystemPrompt({
43
+ /**
44
+ * Build a "boosted" system prompt for small/tiny models.
45
+ * Small models need: more explicit structure, strict formats, explicit step-by-step forcing.
46
+ */
47
+ function buildSmallModelSystemPrompt({
40
48
  role = 'coding',
41
49
  context,
42
50
  tools = [],
43
51
  session,
44
52
  environment,
53
+ design,
54
+ resourceContext,
55
+ modelTier,
45
56
  } = {}) {
46
57
  const parts = [
47
- `You are Winter, an expert AI coding assistant.`,
58
+ `You are Winter, an expert AI coding assistant. You are running on a ${getModelCapabilityLabel(modelTier)}.`,
59
+ '',
60
+ '## CRITICAL: YOU MUST THINK STEP BY STEP',
61
+ '',
62
+ 'Because you are a smaller model, you MUST use structured thinking to produce quality results.',
63
+ 'Before any response, use <thinking> tags to reason through the problem.',
64
+ '',
65
+ 'Your thinking must cover:',
66
+ '1. What does the user want? (restate briefly)',
67
+ '2. What files/tools do I need to use?',
68
+ '3. What is the best approach?',
69
+ '4. What could go wrong? Edge cases?',
70
+ '5. Is my solution complete and correct?',
71
+ '',
72
+ 'After thinking, THEN act. Never skip the thinking step.',
48
73
  '',
49
74
  '## Core Principles',
50
75
  ...BASE_PRINCIPLES.map((p, i) => `${i + 1}. ${p}`),
@@ -70,21 +95,79 @@ export function buildSystemPrompt({
70
95
  parts.push('');
71
96
  }
72
97
 
98
+ if (design) {
99
+ parts.push('## Design Guidelines');
100
+ if (design.brand) {
101
+ parts.push(`Brand: ${design.brand}`);
102
+ parts.push('');
103
+ const lines = design.content.split('\n').filter(Boolean);
104
+ const preview = lines.slice(0, 40).join('\n');
105
+ parts.push(preview);
106
+ if (lines.length > 40) parts.push('... (design file truncated)');
107
+ } else if (design.type === 'design_hint') {
108
+ parts.push('Design-related task detected. Consider applying one of the available design systems.');
109
+ parts.push(`Available: ${design.brands.join(', ')}`);
110
+ }
111
+ parts.push('');
112
+ }
113
+
114
+ if (resourceContext) {
115
+ parts.push(resourceContext);
116
+ }
117
+
73
118
  parts.push(
74
- '## Guidelines',
75
- '- After using tools, always provide a direct final answer to the user.',
76
- '- Answer normal questions directly without unnecessary legal or policy disclaimers.',
77
- '- If a request is illegal, unsafe, or harmful, refuse briefly and offer a safe alternative.',
78
- '- Be proactive: anticipate what the user needs next.',
119
+ '## Execution Rules (STRICT)',
120
+ '- EXECUTE FIRST. Read files, then edit. Do NOT describe what you will do — just do it.',
121
+ '- Keep explanations under 2 sentences. Say what you changed, not what you could do.',
122
+ '- After using tools, give only a one-line summary of what was done.',
123
+ '- Answer questions directly no disclaimers or warnings.',
124
+ '- If a request is unsafe, refuse briefly and stop.',
125
+ '',
126
+ '## Thinking Format (MANDATORY)',
127
+ '<thinking>',
128
+ 'Step-by-step reasoning here...',
129
+ '</thinking>',
130
+ '[Your action/answer here]',
79
131
  );
80
132
 
81
133
  return parts.join('\n');
82
134
  }
135
+ export function buildSystemPrompt({
136
+ role = 'coding',
137
+ context,
138
+ tools = [],
139
+ session,
140
+ environment,
141
+ design,
142
+ resourceContext,
143
+ modelTier,
144
+ } = {}) {
145
+ // ALL models get the deep-thinking system prompt for maximum code quality
146
+ return buildSmallModelSystemPrompt({
147
+ role,
148
+ context,
149
+ tools,
150
+ session,
151
+ environment,
152
+ design,
153
+ resourceContext,
154
+ modelTier,
155
+ });
156
+ }
83
157
 
84
158
  export function buildFastSystemPrompt({
85
159
  role = 'coding',
86
160
  tools = [],
161
+ modelTier,
87
162
  } = {}) {
163
+ if (modelTier && isSmallModel(modelTier)) {
164
+ return [
165
+ 'Winter (fast mode - small model). Be concise. Use tools when needed.',
166
+ tools.length > 0 ? `Tools: ${tools.join(', ')}` : '',
167
+ 'THINK inside <thinking> before acting. Keep responses to 1 sentence.',
168
+ ].filter(Boolean).join('\n');
169
+ }
170
+
88
171
  return [
89
172
  'You are Winter (fast mode). Be concise. Use tools when needed.',
90
173
  tools.length > 0 ? `Tools: ${tools.join(', ')}` : '',
@@ -92,7 +175,7 @@ export function buildFastSystemPrompt({
92
175
  ].filter(Boolean).join('\n');
93
176
  }
94
177
 
95
- export function buildAgentSystemPrompt(role, { tools = [] } = {}) {
178
+ export function buildAgentSystemPrompt(role, { tools = [], modelTier } = {}) {
96
179
  const roleConfigs = {
97
180
  plan: 'You analyze codebases and plan multi-step implementations. Output clear steps.',
98
181
  review: 'You review code for bugs, style issues, and improvements. Be critical but constructive.',
@@ -102,10 +185,15 @@ export function buildAgentSystemPrompt(role, { tools = [] } = {}) {
102
185
  };
103
186
 
104
187
  const base = roleConfigs[role] || roleConfigs.coding;
188
+ const smallNote = modelTier && isSmallModel(modelTier)
189
+ ? '\n\nYou are running on a small model. Use <thinking> tags and reason step by step before each action.'
190
+ : '';
191
+
105
192
  return [
106
193
  `You are Winter (${role} agent).`,
107
194
  base,
108
195
  tools.length > 0 ? `\nTools: ${tools.join(', ')}` : '',
196
+ smallNote,
109
197
  '\nCRITICAL: Output only the requested format. No extra commentary.',
110
198
  ].filter(Boolean).join('\n');
111
199
  }
@@ -24,6 +24,8 @@ export const TASK_CATEGORIES = {
24
24
  TEST: 'test',
25
25
  CONFIG: 'config',
26
26
  INSTALL: 'install',
27
+ DESIGN: 'design',
28
+ UI: 'ui',
27
29
  };
28
30
 
29
31
  const TYPE_KEYWORDS = {
@@ -39,6 +41,8 @@ const TYPE_KEYWORDS = {
39
41
  test: ['test', 'unit test', 'integration test', 'assert', 'spec'],
40
42
  config: ['config', 'setup', 'install', 'configure', 'initialize'],
41
43
  install: ['install', 'npm install', 'pip install', 'gem install', 'cargo install', 'brew install'],
44
+ design: ['design', 'ui', 'brand', 'style guide', 'make it look', 'pixel perfect', 'layout', 'color scheme', 'palette', 'typography', 'theme'],
45
+ ui: ['ui', 'interface', 'component', 'button', 'card', 'modal', 'form', 'navbar', 'sidebar', 'header', 'footer', 'dashboard', 'landing page'],
42
46
  };
43
47
 
44
48
  const COMPLEXITY_SIGNALS = {
@@ -108,7 +112,7 @@ export function classifyTask(userInput) {
108
112
  wordCount,
109
113
  estimatedTokens,
110
114
  requiresTools: bestCategory !== TASK_CATEGORIES.EXPLAIN,
111
- requiresContext: ['edit', 'refactor', 'debug', 'review', 'test'].includes(bestCategory),
115
+ requiresContext: ['edit', 'refactor', 'debug', 'review', 'test', 'design', 'ui'].includes(bestCategory),
112
116
  };
113
117
  }
114
118
 
@@ -8,6 +8,9 @@ import { selectExecutionProfile } from '../context/router.js';
8
8
  import { buildSystemPrompt, buildFastSystemPrompt, buildAgentSystemPrompt } from './prompts/system-prompt.js';
9
9
  import { classifyTask } from './prompts/task-classifier.js';
10
10
  import SuccessCriteria from './prompts/success-criteria.js';
11
+ import { ReasoningConfig, REASONING_LEVELS, complexityToReasoningLevel } from './reasoning.js';
12
+ import { buildResourceContext, getRelevantDesignGuide } from '../context/resource-loader.js';
13
+ import { classifyModelTier } from './model-capabilities.js';
11
14
 
12
15
  function isAuthError(error) {
13
16
  const msg = String(error?.message || error || '');
@@ -23,6 +26,10 @@ export class AIProviderManager {
23
26
  this.tools = [];
24
27
  this.initialized = false;
25
28
  this.authToken = null;
29
+ this._cachedResourceContext = '';
30
+ this._cachedDesignGuide = null;
31
+ this._fallbackWarned = false;
32
+ this._modelTier = null;
26
33
  }
27
34
 
28
35
  async init() {
@@ -96,6 +103,13 @@ export class AIProviderManager {
96
103
  if (available) this.activeProvider = available;
97
104
  }
98
105
 
106
+ // Auto-detect model capability tier
107
+ const providerConfig = this.providers[this.activeProvider] || {};
108
+ this._modelTier = classifyModelTier(providerConfig.model, this.activeProvider);
109
+
110
+ // Eager-load local resources (design systems, agent instructions) for contextual injection
111
+ this._loadResourceContext(); // fire-and-forget
112
+
99
113
  this.initialized = true;
100
114
  }
101
115
 
@@ -229,17 +243,27 @@ export class AIProviderManager {
229
243
  const routedProvider = this.providers[executionProfile.provider] || this.providers[this.activeProvider];
230
244
  const defaultProvider = this.providers[this.activeProvider];
231
245
 
246
+ const routingModel = options.model || executionProfile.model;
247
+ const routingReasoning = options.reasoning || executionProfile.reasoningParam;
248
+
232
249
  try {
233
250
  return await withRetry(() => this.sendRequestToProvider(routedProvider, messages, {
234
251
  ...options,
235
- model: options.model || executionProfile.model,
252
+ model: routingModel,
253
+ reasoning: routingReasoning,
254
+ reasoningLevel: options.reasoningLevel || executionProfile.reasoningLevel,
236
255
  }), { maxAttempts: 3, baseDelayMs: 150 });
237
256
  } catch (error) {
238
257
  if (isAuthError(error) && routedProvider !== defaultProvider && defaultProvider) {
239
- console.warn(`[winter] ${executionProfile.provider} provider auth error, falling back to ${this.activeProvider}`);
258
+ if (!this._fallbackWarned) {
259
+ console.warn(`[winter] ${executionProfile.provider} auth error, falling back to ${this.activeProvider}`);
260
+ this._fallbackWarned = true;
261
+ }
240
262
  return await withRetry(() => this.sendRequestToProvider(defaultProvider, messages, {
241
263
  ...options,
242
264
  model: options.model || defaultProvider.model,
265
+ reasoning: routingReasoning,
266
+ reasoningLevel: options.reasoningLevel || executionProfile.reasoningLevel,
243
267
  }), { maxAttempts: 1, baseDelayMs: 0 });
244
268
  }
245
269
  throw error;
@@ -257,17 +281,27 @@ export class AIProviderManager {
257
281
  const routedProvider = this.providers[executionProfile.provider] || this.providers[this.activeProvider];
258
282
  const defaultProvider = this.providers[this.activeProvider];
259
283
 
284
+ const routingModel = options.model || executionProfile.model;
285
+ const routingReasoning = options.reasoning || executionProfile.reasoningParam;
286
+
260
287
  try {
261
288
  yield* this.streamRequestToProvider(routedProvider, messages, {
262
289
  ...options,
263
- model: options.model || executionProfile.model,
290
+ model: routingModel,
291
+ reasoning: routingReasoning,
292
+ reasoningLevel: options.reasoningLevel || executionProfile.reasoningLevel,
264
293
  });
265
294
  } catch (error) {
266
295
  if (isAuthError(error) && routedProvider !== defaultProvider && defaultProvider) {
267
- console.warn(`[winter] ${executionProfile.provider} provider auth error, falling back to ${this.activeProvider}`);
296
+ if (!this._fallbackWarned) {
297
+ console.warn(`[winter] ${executionProfile.provider} auth error, falling back to ${this.activeProvider}`);
298
+ this._fallbackWarned = true;
299
+ }
268
300
  yield* this.streamRequestToProvider(defaultProvider, messages, {
269
301
  ...options,
270
302
  model: options.model || defaultProvider.model,
303
+ reasoning: routingReasoning,
304
+ reasoningLevel: options.reasoningLevel || executionProfile.reasoningLevel,
271
305
  });
272
306
  } else {
273
307
  throw error;
@@ -285,6 +319,17 @@ export class AIProviderManager {
285
319
  messages,
286
320
  };
287
321
 
322
+ // Apply reasoning configuration
323
+ const reasoningParam = options.reasoning || this._getReasoningParam(options, provider);
324
+ if (reasoningParam) {
325
+ if (reasoningParam.reasoning_effort) {
326
+ body.reasoning_effort = reasoningParam.reasoning_effort;
327
+ }
328
+ if (reasoningParam.thinking) {
329
+ body.thinking = reasoningParam.thinking;
330
+ }
331
+ }
332
+
288
333
  if (this.tools.length > 0 && options.enableTools) {
289
334
  body.tools = this.tools;
290
335
  }
@@ -329,6 +374,17 @@ export class AIProviderManager {
329
374
  body.stream_options = { include_usage: true };
330
375
  }
331
376
 
377
+ // Apply reasoning configuration
378
+ const reasoningParam = options.reasoning || this._getReasoningParam(options, provider);
379
+ if (reasoningParam) {
380
+ if (reasoningParam.reasoning_effort) {
381
+ body.reasoning_effort = reasoningParam.reasoning_effort;
382
+ }
383
+ if (reasoningParam.thinking) {
384
+ body.thinking = reasoningParam.thinking;
385
+ }
386
+ }
387
+
332
388
  if (this.tools.length > 0 && options.enableTools) {
333
389
  body.tools = this.tools;
334
390
  }
@@ -519,6 +575,23 @@ export class AIProviderManager {
519
575
  return { error: 'Tool execution handled by REPL' };
520
576
  }
521
577
 
578
+ _getReasoningParam(options, provider) {
579
+ // 1. Explicit reasoning param passed through options
580
+ if (options.reasoning) return options.reasoning;
581
+
582
+ // 2. Reasoning level specified -> build from level
583
+ if (options.reasoningLevel) {
584
+ const config = new ReasoningConfig({
585
+ level: options.reasoningLevel,
586
+ provider: provider?.name || this.activeProvider,
587
+ });
588
+ return config.getApiReasoningParam();
589
+ }
590
+
591
+ // 3. No reasoning config at all
592
+ return null;
593
+ }
594
+
522
595
  getSystemPrompt(options = {}) {
523
596
  const taskInfo = options.task ? classifyTask(options.task) : null;
524
597
  const tools = this.tools ? Object.keys(this.tools) : [];
@@ -527,24 +600,85 @@ export class AIProviderManager {
527
600
  plans: options.plans || [],
528
601
  };
529
602
 
603
+ // Inject reasoning instructions if applicable
604
+ let reasoningPrompt = '';
605
+ if (options.reasoningLevel || options.reasoningPrompt) {
606
+ reasoningPrompt = options.reasoningPrompt || new ReasoningConfig({
607
+ level: options.reasoningLevel || REASONING_LEVELS.MEDIUM,
608
+ provider: this.activeProvider,
609
+ modelTier: this._modelTier,
610
+ }).getPromptInstructions();
611
+ } else if (taskInfo) {
612
+ // Auto-inject based on task complexity for providers without API reasoning
613
+ const level = complexityToReasoningLevel(taskInfo.type);
614
+ const config = new ReasoningConfig({
615
+ level,
616
+ provider: this.activeProvider,
617
+ modelTier: this._modelTier,
618
+ });
619
+ if (config.needsPromptInjection && level !== REASONING_LEVELS.NONE) {
620
+ reasoningPrompt = config.getPromptInstructions();
621
+ }
622
+ }
623
+
530
624
  if (options.role === 'agent') {
531
- return buildAgentSystemPrompt(options.agentRole || 'coding', { tools });
625
+ return buildAgentSystemPrompt(options.agentRole || 'coding', { tools, modelTier: this._modelTier }) + reasoningPrompt;
532
626
  }
533
627
 
534
628
  if (options.fast) {
535
- return buildFastSystemPrompt({ role: 'coding', tools });
629
+ return buildFastSystemPrompt({ role: 'coding', tools, modelTier: this._modelTier });
536
630
  }
537
631
 
538
632
  const successPrompt = options.task
539
633
  ? '\n\n' + SuccessCriteria.fromRequest(options.task).buildPrompt()
540
634
  : '';
541
635
 
636
+ // Use cached resource context (eager-loaded in init())
637
+ const resourceContext = this._cachedResourceContext || '';
638
+
639
+ // Auto-detect relevant design guide for UI/design tasks
640
+ let designGuide = null;
641
+ if (taskInfo && (taskInfo.category === 'design' || taskInfo.category === 'ui')) {
642
+ this._designGuidePromise = this._designGuidePromise || this._loadDesignGuide(options.task);
643
+ }
644
+ const design = this._cachedDesignGuide || null;
645
+
542
646
  return buildSystemPrompt({
543
647
  role: taskInfo?.category || 'coding',
544
648
  context: taskInfo,
545
649
  tools,
546
650
  session: sessionInfo,
547
- }) + successPrompt;
651
+ design,
652
+ resourceContext,
653
+ modelTier: this._modelTier,
654
+ }) + reasoningPrompt + successPrompt;
655
+ }
656
+
657
+ /**
658
+ * Load resource context (cached for session lifetime).
659
+ */
660
+ async _loadResourceContext() {
661
+ try {
662
+ this._cachedResourceContext = await buildResourceContext();
663
+ } catch (e) {
664
+ this._cachedResourceContext = '';
665
+ }
666
+ return this._cachedResourceContext;
667
+ }
668
+
669
+ /**
670
+ * Load relevant design guide for a task.
671
+ */
672
+ async _loadDesignGuide(task) {
673
+ try {
674
+ const guide = await getRelevantDesignGuide(task);
675
+ if (guide) {
676
+ this._cachedDesignGuide = guide;
677
+ }
678
+ } catch (e) {
679
+ // Silently fail - design context is optional
680
+ }
681
+ return this._cachedDesignGuide;
548
682
  }
549
683
 
550
684
  classifyTask(userInput) {
@@ -0,0 +1,266 @@
1
+ /**
2
+ * Reasoning Configuration Manager
3
+ *
4
+ * Controls reasoning effort / extended thinking per provider and task complexity.
5
+ *
6
+ * Supported APIs:
7
+ * - OpenAI: reasoning_effort ("low" | "medium" | "high") — o1, o3 models
8
+ * - Anthropic: thinking ({ type: "enabled", budget_tokens: number }) — Claude 3.7+ Sonnet
9
+ * - DeepSeek: built-in CoT reasoning (no explicit param needed)
10
+ * - Others: falls back to prompt-level reasoning instructions
11
+ */
12
+
13
+ import { isSmallModel } from './model-capabilities.js';
14
+
15
+ export const REASONING_LEVELS = {
16
+ NONE: 'none',
17
+ LOW: 'low',
18
+ MEDIUM: 'medium',
19
+ HIGH: 'high',
20
+ MAX: 'max',
21
+ };
22
+
23
+ const REASONING_EFFORT_MAP = {
24
+ [REASONING_LEVELS.NONE]: null,
25
+ [REASONING_LEVELS.LOW]: 'low',
26
+ [REASONING_LEVELS.MEDIUM]: 'medium',
27
+ [REASONING_LEVELS.HIGH]: 'high',
28
+ [REASONING_LEVELS.MAX]: 'high',
29
+ };
30
+
31
+ const EXTENDED_THINKING_BUDGET_MAP = {
32
+ [REASONING_LEVELS.NONE]: null,
33
+ [REASONING_LEVELS.LOW]: 1024,
34
+ [REASONING_LEVELS.MEDIUM]: 4096,
35
+ [REASONING_LEVELS.HIGH]: 8192,
36
+ [REASONING_LEVELS.MAX]: 16384,
37
+ };
38
+
39
+ /**
40
+ * Standard reasoning prompt templates for API-level reasoning models.
41
+ */
42
+ /**
43
+ * Unified deep reasoning prompts — ALL models use these aggressive templates.
44
+ * Every model, regardless of size, must think step by step with explicit structure.
45
+ * The structured <thinking> format forces deep reasoning, catches edge cases,
46
+ * and produces significantly higher quality code.
47
+ */
48
+ const REASONING_PROMPT_TEMPLATES = {
49
+ [REASONING_LEVELS.NONE]: '',
50
+ [REASONING_LEVELS.LOW]:
51
+ 'Think step by step before responding. Use <thinking> tags for your reasoning, then provide your answer.',
52
+ [REASONING_LEVELS.MEDIUM]:
53
+ 'CRITICAL: You MUST think step by step inside <thinking> tags before every response.\n' +
54
+ '\n' +
55
+ '<thinking>\n' +
56
+ '1. What is the user asking for? (restate briefly)\n' +
57
+ '2. What do I know / what files do I need?\n' +
58
+ '3. What is the correct approach?\n' +
59
+ '4. What could go wrong? (edge cases, errors)\n' +
60
+ '5. How do I verify my solution?\n' +
61
+ '</thinking>\n' +
62
+ 'Then provide your answer clearly and directly.',
63
+ [REASONING_LEVELS.HIGH]:
64
+ 'CRITICAL DEEP REASONING REQUIRED. Use this EXACT structured thinking process:\n' +
65
+ '\n' +
66
+ '<thinking>\n' +
67
+ '## STEP 1: UNDERSTAND\n' +
68
+ '- Restate the problem in your own words\n' +
69
+ '- Identify all key requirements (explicit + implicit)\n' +
70
+ '\n' +
71
+ '## STEP 2: ANALYZE\n' +
72
+ '- What information is provided? What is missing?\n' +
73
+ '- Consider multiple approaches\n' +
74
+ '- List potential edge cases and pitfalls\n' +
75
+ '\n' +
76
+ '## STEP 3: PLAN\n' +
77
+ '- Outline your solution step by step\n' +
78
+ '- For code: plan the exact files and changes needed\n' +
79
+ '- Verify each step makes sense\n' +
80
+ '\n' +
81
+ '## STEP 4: VERIFY\n' +
82
+ '- Check your solution against all requirements\n' +
83
+ '- Look for mistakes, regressions, or missing pieces\n' +
84
+ '- How will you confirm it works?\n' +
85
+ '</thinking>\n' +
86
+ 'After thinking, provide your final answer. The thinking is internal — be concise in your response.',
87
+ [REASONING_LEVELS.MAX]:
88
+ '## MANDATORY DEEP REASONING\n' +
89
+ 'You MUST do extremely thorough reasoning before every response. Do not skip any step.\n' +
90
+ '\n' +
91
+ 'Follow this EXACT thinking structure — fill out every section:\n' +
92
+ '\n' +
93
+ '<thinking>\n' +
94
+ '## PROBLEM RESTATEMENT\n' +
95
+ 'State what the user needs in one sentence.\n' +
96
+ '\n' +
97
+ '## REQUIREMENTS ANALYSIS\n' +
98
+ '- Explicit requirements:\n' +
99
+ '- Implicit requirements:\n' +
100
+ '- Constraints / boundaries:\n' +
101
+ '\n' +
102
+ '## CONTEXT & CODEBASE ANALYSIS\n' +
103
+ '- What files are relevant?\n' +
104
+ '- What existing patterns should I follow?\n' +
105
+ '- What assumptions am I making?\n' +
106
+ '\n' +
107
+ '## APPROACH COMPARISON\n' +
108
+ '- Option 1: [describe]\n' +
109
+ ' Pros: ... Cons: ...\n' +
110
+ '- Option 2: [describe]\n' +
111
+ ' Pros: ... Cons: ...\n' +
112
+ '- Best choice: [pick and explain why]\n' +
113
+ '\n' +
114
+ '## IMPLEMENTATION PLAN\n' +
115
+ 'Step-by-step what needs to happen:\n' +
116
+ '1. ...\n' +
117
+ '2. ...\n' +
118
+ '3. ...\n' +
119
+ '(For code: include exact files to read, edit, or create)\n' +
120
+ '\n' +
121
+ '## EDGE CASES & RISKS\n' +
122
+ '- What could go wrong?\n' +
123
+ '- How will I handle errors?\n' +
124
+ '- What about performance / security?\n' +
125
+ '\n' +
126
+ '## VERIFICATION STRATEGY\n' +
127
+ '- How will I confirm this works?\n' +
128
+ '- What tests or checks should be run?\n' +
129
+ '- What could break with these changes?\n' +
130
+ '</thinking>\n' +
131
+ '\n' +
132
+ 'After closing </thinking>, provide your final implementation.\n' +
133
+ 'Keep the reasoning internal — only show the user your result and a brief summary.',
134
+ };
135
+
136
+ /**
137
+ * Maps task complexity to recommended reasoning level.
138
+ */
139
+ export function complexityToReasoningLevel(taskType) {
140
+ switch (taskType) {
141
+ case 'quick': return REASONING_LEVELS.NONE;
142
+ case 'simple': return REASONING_LEVELS.LOW;
143
+ case 'moderate': return REASONING_LEVELS.MEDIUM;
144
+ case 'complex': return REASONING_LEVELS.HIGH;
145
+ case 'deep': return REASONING_LEVELS.MAX;
146
+ default: return REASONING_LEVELS.MEDIUM;
147
+ }
148
+ }
149
+
150
+ export class ReasoningConfig {
151
+ /**
152
+ * @param {object} options
153
+ * @param {string} options.level - One of REASONING_LEVELS
154
+ * @param {string} options.provider - Provider name (for API-specific config)
155
+ * @param {string} [options.modelTier] - Model capability tier from model-capabilities.js
156
+ * @param {object} [options.modelInfo] - Model metadata
157
+ * @param {object} [options.taskInfo] - Task classification result from task-classifier
158
+ */
159
+ constructor(options = {}) {
160
+ this.level = options.level || REASONING_LEVELS.MEDIUM;
161
+ this.provider = options.provider || '';
162
+ this.modelTier = options.modelTier || null;
163
+ this.modelInfo = options.modelInfo || {};
164
+ this.taskInfo = options.taskInfo || null;
165
+ }
166
+
167
+ /**
168
+ * Whether reasoning is enabled at all.
169
+ */
170
+ get enabled() {
171
+ return this.level !== REASONING_LEVELS.NONE && this.level !== null;
172
+ }
173
+
174
+ /**
175
+ * Whether this level should inject reasoning instructions into the system prompt.
176
+ */
177
+ get needsPromptInjection() {
178
+ return this.providerSupportsApiReasoning === false || !this.provider;
179
+ }
180
+
181
+ /**
182
+ * Check if provider has native API-level reasoning support.
183
+ */
184
+ get providerSupportsApiReasoning() {
185
+ const p = (this.provider || '').toLowerCase();
186
+ if (p === 'openai') return true;
187
+ if (p === 'anthropic' || p === 'claude') return true;
188
+ return false;
189
+ }
190
+
191
+ /**
192
+ * Whether this is a small model that needs aggressive prompting.
193
+ */
194
+ get isSmall() {
195
+ return this.modelTier ? isSmallModel(this.modelTier) : false;
196
+ }
197
+
198
+ /**
199
+ * Get the API-level reasoning parameter for the request body.
200
+ * Returns null if provider doesn't support API reasoning or level is NONE.
201
+ */
202
+ getApiReasoningParam() {
203
+ if (!this.enabled) return null;
204
+
205
+ const p = (this.provider || '').toLowerCase();
206
+
207
+ if (p === 'openai') {
208
+ const effort = REASONING_EFFORT_MAP[this.level];
209
+ if (!effort) return null;
210
+ return { reasoning_effort: effort };
211
+ }
212
+
213
+ if (p === 'anthropic' || p === 'claude') {
214
+ const budget = EXTENDED_THINKING_BUDGET_MAP[this.level];
215
+ if (!budget) return null;
216
+ return {
217
+ thinking: {
218
+ type: 'enabled',
219
+ budget_tokens: budget,
220
+ },
221
+ };
222
+ }
223
+
224
+ return null;
225
+ }
226
+
227
+ /**
228
+ * Get reasoning prompt instructions to inject into the system prompt.
229
+ * ALL models use the unified deep-reasoning templates.
230
+ * @param {string} [modelTier] - Unused, kept for backward compatibility.
231
+ * @returns {string}
232
+ */
233
+ getPromptInstructions(modelTier) {
234
+ if (!this.enabled || this.providerSupportsApiReasoning) return '';
235
+ return REASONING_PROMPT_TEMPLATES[this.level] || '';
236
+ }
237
+
238
+ /**
239
+ * Build a reasoning config for a given provider and task info.
240
+ */
241
+ static fromTask(taskInfo, provider, options = {}) {
242
+ const level = options.reasoningLevel
243
+ || (taskInfo ? complexityToReasoningLevel(taskInfo.type) : REASONING_LEVELS.MEDIUM);
244
+
245
+ return new ReasoningConfig({
246
+ level,
247
+ provider: provider || '',
248
+ taskInfo: taskInfo || null,
249
+ ...options,
250
+ });
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Default reasoning configuration for the system.
256
+ */
257
+ export const DEFAULT_REASONING_CONFIG = {
258
+ defaultLevel: REASONING_LEVELS.MEDIUM,
259
+ maxBudgetTokens: 16384,
260
+ // Per-provider overrides
261
+ providers: {
262
+ openai: { supports: true, paramType: 'reasoning_effort' },
263
+ anthropic: { supports: true, paramType: 'thinking' },
264
+ claude: { supports: true, paramType: 'thinking' },
265
+ },
266
+ };
@@ -1,5 +1,6 @@
1
1
  import path from 'path';
2
2
  import { colors } from './snowflake-logo.js';
3
+ import { SLASH_COMMANDS } from './slash-commands.js';
3
4
 
4
5
  /**
5
6
  * Handle slash commands in the Winter REPL.
package/src/cli/repl.js CHANGED
@@ -241,6 +241,7 @@ export class WinterREPL {
241
241
  // Show banner only if not already shown
242
242
  if (!process.env.WINTER_BANNER_SHOWN) {
243
243
  console.log(welcomeBanner(this.version, info));
244
+ this.showCommandMenu();
244
245
  process.env.WINTER_BANNER_SHOWN = '1';
245
246
  } else {
246
247
  this.showStatus();
@@ -315,6 +316,7 @@ export class WinterREPL {
315
316
  console.log(`${colors.dim}Project: ${this.projectPath}${colors.reset}`);
316
317
  console.log(`${colors.dim}Provider: ${this.ai.getActiveProvider()}${colors.reset}`);
317
318
  console.log(`${colors.dim}Session: ${this.session.getSessionId().substring(0, 8)}${colors.reset}`);
319
+ console.log(`${colors.dim}Type ${colors.cyan}/help${colors.dim} for commands or ${colors.cyan}/${colors.dim} for menu${colors.reset}`);
318
320
  console.log('');
319
321
  }
320
322
 
@@ -1042,7 +1044,7 @@ ${colors.reset}
1042
1044
  const currentToolSignature = this.buildToolCallSignature(toolCalls);
1043
1045
  if (currentToolSignature && currentToolSignature === lastToolSignature) {
1044
1046
  console.log(`
1045
- ${colors.yellow}ℹ AI đang lặp lại cùng một chuỗi tool call. Dừng vòng lặp để tránh spam.${colors.reset}`);
1047
+ ${colors.yellow}ℹ AI tool loop detected. Breaking out.${colors.reset}`);
1046
1048
  reachedToolLimit = false;
1047
1049
  break;
1048
1050
  }
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Resource Loader - Auto-discovers and indexes local resources
3
+ * (design systems, agent instructions, skills) for contextual injection.
4
+ */
5
+
6
+ import { promises as fs } from 'fs';
7
+ import path from 'path';
8
+ import { homedir } from 'os';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
+ const PROJECT_ROOT = path.resolve(__dirname, '..', '..');
13
+ const LOCAL_ROOT = path.join(PROJECT_ROOT, 'resources', 'local');
14
+
15
+ // ── Design Systems ──────────────────────────────────────────────────────────
16
+
17
+ const DESIGN_MD_DIR = path.join(LOCAL_ROOT, 'awesome-design-md', 'design-md');
18
+
19
+ /**
20
+ * Discover all available design system brands.
21
+ */
22
+ export async function discoverDesignBrands() {
23
+ try {
24
+ const entries = await fs.readdir(DESIGN_MD_DIR, { withFileTypes: true });
25
+ return entries
26
+ .filter(e => e.isDirectory())
27
+ .map(e => e.name)
28
+ .sort();
29
+ } catch {
30
+ return [];
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Load a DESIGN.md file for a specific brand.
36
+ */
37
+ export async function loadDesignMd(brand) {
38
+ try {
39
+ const dir = path.join(DESIGN_MD_DIR, brand);
40
+ const files = ['DESIGN.md', 'README.md'];
41
+ for (const file of files) {
42
+ const filePath = path.join(dir, file);
43
+ await fs.access(filePath);
44
+ return { brand, file, content: await fs.readFile(filePath, 'utf8') };
45
+ }
46
+ } catch {}
47
+ return null;
48
+ }
49
+
50
+ /**
51
+ * Search design systems by keyword (brand name or description).
52
+ */
53
+ export async function searchDesignSystems(query) {
54
+ const brands = await discoverDesignBrands();
55
+ const q = query.toLowerCase();
56
+ const matched = brands.filter(b => b.includes(q));
57
+ return matched.slice(0, 10);
58
+ }
59
+
60
+ // ── Resource Manifest ──────────────────────────────────────────────────────
61
+
62
+ const MANIFEST_PATH = path.join(LOCAL_ROOT, 'manifest.json');
63
+
64
+ export async function loadResourceManifest() {
65
+ try {
66
+ const raw = await fs.readFile(MANIFEST_PATH, 'utf8');
67
+ return JSON.parse(raw);
68
+ } catch {
69
+ return { localResources: [] };
70
+ }
71
+ }
72
+
73
+ // ── Context Builder ─────────────────────────────────────────────────────────
74
+
75
+ /**
76
+ * Build a concise context summary of all available local resources.
77
+ * Used for automatic injection into system prompts.
78
+ */
79
+ export async function buildResourceContext() {
80
+ const manifest = await loadResourceManifest();
81
+ const resources = manifest.localResources || [];
82
+ const designBrands = await discoverDesignBrands();
83
+
84
+ const parts = [];
85
+
86
+ if (resources.length > 0) {
87
+ parts.push('## Local Resources');
88
+ resources.forEach(r => {
89
+ parts.push(` - ${r.name}: ${r.fileCount} files, ${(r.size / 1024).toFixed(0)}KB`);
90
+ });
91
+ parts.push('');
92
+ }
93
+
94
+ if (designBrands.length > 0) {
95
+ const brandsStr = designBrands.slice(0, 40).join(', ');
96
+ const leftover = designBrands.length - 40;
97
+ parts.push(`## Design Systems (${designBrands.length} available)`);
98
+ parts.push(` ${brandsStr}${leftover > 0 ? `, +${leftover} more` : ''}`);
99
+ parts.push('');
100
+ }
101
+
102
+ return parts.join('\n');
103
+ }
104
+
105
+ /**
106
+ * Get a relevant DESIGN.md content based on the task description.
107
+ * Uses keyword matching between the task text and design system brand names.
108
+ */
109
+ export async function getRelevantDesignGuide(taskText) {
110
+ if (!taskText) return null;
111
+
112
+ const brands = await discoverDesignBrands();
113
+ const text = taskText.toLowerCase();
114
+
115
+ // Match by brand name in task text
116
+ for (const brand of brands) {
117
+ if (text.includes(brand)) {
118
+ const design = await loadDesignMd(brand);
119
+ if (design) return design;
120
+ }
121
+ }
122
+
123
+ // Match by context clues (e.g., "design", "ui", "looks like", "brand")
124
+ const designHint = /\b(design|ui|looks? like|brand guide|style guide|make it look)\b/i.test(text);
125
+ if (designHint && brands.length > 0) {
126
+ // Return the first few brands as options
127
+ return {
128
+ brand: null,
129
+ type: 'design_hint',
130
+ brands: brands.slice(0, 5),
131
+ note: 'Design-related task detected. Available design systems listed above.',
132
+ };
133
+ }
134
+
135
+ return null;
136
+ }
@@ -11,26 +11,83 @@ function flattenMessageText(messages) {
11
11
  : String(messages || '').toLowerCase();
12
12
  }
13
13
 
14
- export function selectExecutionProfile({ messages = [], activeProvider = null, providers = {}, options = {} } = {}) {
15
- const text = flattenMessageText(messages);
16
- const providerNames = Object.keys(providers).filter(name => providers[name]?.ready || providers[name]?.model);
17
- const hasProvider = name => providerNames.includes(name);
14
+ import { ReasoningConfig, REASONING_LEVELS } from '../ai/reasoning.js';
15
+ import { classifyModelTier, isSmallModel, getReasoningBump, MODEL_TIERS } from '../ai/model-capabilities.js';
18
16
 
19
- const explicitProvider = options.provider && hasProvider(options.provider) ? options.provider : null;
20
- let provider = explicitProvider || (activeProvider && hasProvider(activeProvider) ? activeProvider : providerNames[0] || null);
21
-
22
- if (explicitProvider) {
23
- provider = explicitProvider;
24
- } else if (/\b(review|refactor|debug|fix|bug|error|stack trace|test|tool|patch|code)\b/.test(text) && hasProvider('claude')) {
25
- provider = 'claude';
26
- } else if (/\b(summary|summarize|commit message|changelog|docs|explain|rewrite)\b/.test(text) && hasProvider('openai')) {
27
- provider = 'openai';
28
- } else if (/\b(local|offline|privacy|private|on-device)\b/.test(text) && hasProvider('ollama')) {
29
- provider = 'ollama';
30
- } else if (/\b(quick|brief|short|fast)\b/.test(text) && hasProvider('groq')) {
31
- provider = 'groq';
32
- }
17
+ /**
18
+ * Bump reasoning level by N steps.
19
+ */
20
+ function bumpReasoningLevel(level, steps) {
21
+ const order = [REASONING_LEVELS.NONE, REASONING_LEVELS.LOW, REASONING_LEVELS.MEDIUM, REASONING_LEVELS.HIGH, REASONING_LEVELS.MAX];
22
+ const idx = order.indexOf(level);
23
+ if (idx === -1) return level;
24
+ const newIdx = Math.min(idx + steps, order.length - 1);
25
+ return order[newIdx];
26
+ } export function selectExecutionProfile({ messages = [], activeProvider = null, providers = {}, options = {} } = {}) {
27
+ const text = flattenMessageText(messages);
28
+ const providerNames = Object.keys(providers).filter(name => providers[name]?.ready || providers[name]?.model);
29
+ const hasProvider = name => providerNames.includes(name);
30
+
31
+ const explicitProvider = options.provider && hasProvider(options.provider) ? options.provider : null;
32
+ let provider = explicitProvider || (activeProvider && hasProvider(activeProvider) ? activeProvider : providerNames[0] || null);
33
+
34
+ if (explicitProvider) {
35
+ provider = explicitProvider;
36
+ } else if (/\b(review|refactor|debug|fix|bug|error|stack trace|test|tool|patch|code)\b/.test(text) && hasProvider('claude')) {
37
+ provider = 'claude';
38
+ } else if (/\b(summary|summarize|commit message|changelog|docs|explain|rewrite)\b/.test(text) && hasProvider('openai')) {
39
+ provider = 'openai';
40
+ } else if (/\b(local|offline|privacy|private|on-device)\b/.test(text) && hasProvider('ollama')) {
41
+ provider = 'ollama';
42
+ } else if (/\b(quick|brief|short|fast)\b/.test(text) && hasProvider('groq')) {
43
+ provider = 'groq';
44
+ }
45
+
46
+ const providerConfig = providers[provider] || providers[activeProvider] || {};
47
+ const model = options.model || providerConfig.model || providers[activeProvider]?.model || null;
48
+
49
+ // Detect model capability tier
50
+ const modelTier = classifyModelTier(model, provider);
51
+ const isSmall = isSmallModel(modelTier);
52
+ const reasoningBump = getReasoningBump(modelTier);
53
+
54
+ // Determine reasoning level based on task complexity signals
55
+ // Default: HIGH for coding — all models must think deeply
56
+ let reasoningLevel = options.reasoningLevel || REASONING_LEVELS.HIGH;
57
+ if (!options.reasoningLevel) {
58
+ const hasDeepSignals = /\b(refactor|architecture|redesign|migrate|complex|full stack|e2e|end to end|security|optimize|performance|implement|build|create)\b/.test(text);
59
+ const hasComplexSignals = /\b(debug|fix|test|multiple|integrate|design|plan|review|analyze)\b/.test(text);
60
+
61
+ if (hasDeepSignals && text.length > 30) {
62
+ reasoningLevel = REASONING_LEVELS.MAX;
63
+ } else if (hasComplexSignals && text.length > 20) {
64
+ reasoningLevel = REASONING_LEVELS.MAX;
65
+ } else if (text.split(/\s+/).length > 10) {
66
+ reasoningLevel = REASONING_LEVELS.HIGH;
67
+ } else if (text.split(/\s+/).length < 3) {
68
+ reasoningLevel = REASONING_LEVELS.MEDIUM;
69
+ } else {
70
+ reasoningLevel = REASONING_LEVELS.HIGH;
71
+ }
72
+
73
+ // If small model, bump reasoning level even more to compensate
74
+ if (isSmall && reasoningBump > 0) {
75
+ reasoningLevel = bumpReasoningLevel(reasoningLevel, reasoningBump);
76
+ }
77
+ }
33
78
 
34
- const model = options.model || providers[provider]?.model || providers[activeProvider]?.model || null;
35
- return { provider, model };
79
+ const reasoning = new ReasoningConfig({
80
+ level: reasoningLevel,
81
+ provider: provider || activeProvider,
82
+ modelTier,
83
+ });
84
+
85
+ return {
86
+ provider,
87
+ model,
88
+ modelTier,
89
+ reasoningLevel,
90
+ reasoningParam: reasoning.getApiReasoningParam(),
91
+ reasoningPrompt: reasoning.getPromptInstructions(),
92
+ };
36
93
  }