@link-assistant/hive-mind 1.50.15 → 1.52.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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.52.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 5b24866: Add Claude Opus 4.7 model support with adaptive thinking, model-correct xhigh/max effort mapping, Opus 4.5/Mythos effort detection, and the --show-thinking-content option.
8
+
9
+ ## 1.51.0
10
+
11
+ ### Minor Changes
12
+
13
+ - fd3c76c: Add per-tool Telegram solve aliases: /claude, /codex, /opencode, and /agent.
14
+
3
15
  ## 1.50.15
4
16
 
5
17
  ### Patch Changes
package/README.hi.md CHANGED
@@ -444,6 +444,18 @@ Examples:
444
444
  /solve https://github.com/owner/repo/issues/123 --model sonnet
445
445
  /solve https://github.com/owner/repo/issues/123 --model opus --think max
446
446
 
447
+ Aliases:
448
+ /do और /continue /solve के बराबर हैं
449
+ /claude /solve --tool claude के बराबर है
450
+ /codex /solve --tool codex के बराबर है
451
+ /opencode /solve --tool opencode के बराबर है
452
+ /agent /solve --tool agent के बराबर है
453
+
454
+ Tool alias examples:
455
+ /codex https://github.com/owner/repo/issues/123 --model gpt-5.4
456
+ /opencode https://github.com/owner/repo/issues/123 --model grok-code-fast-1
457
+ /agent https://github.com/owner/repo/issues/123 --model nemotron-3-super-free
458
+
447
459
  Free Models (with --tool agent):
448
460
  /solve https://github.com/owner/repo/issues/123 --tool agent --model nemotron-3-super-free
449
461
  /solve https://github.com/owner/repo/issues/123 --tool agent --model opencode/nemotron-3-super-free
package/README.md CHANGED
@@ -453,6 +453,18 @@ Examples:
453
453
  /solve https://github.com/owner/repo/issues/123 --model sonnet
454
454
  /solve https://github.com/owner/repo/issues/123 --model opus --think max
455
455
 
456
+ Aliases:
457
+ /do and /continue are equivalent to /solve
458
+ /claude is equivalent to /solve --tool claude
459
+ /codex is equivalent to /solve --tool codex
460
+ /opencode is equivalent to /solve --tool opencode
461
+ /agent is equivalent to /solve --tool agent
462
+
463
+ Tool alias examples:
464
+ /codex https://github.com/owner/repo/issues/123 --model gpt-5.4
465
+ /opencode https://github.com/owner/repo/issues/123 --model grok-code-fast-1
466
+ /agent https://github.com/owner/repo/issues/123 --model nemotron-3-super-free
467
+
456
468
  Free Models (with --tool agent):
457
469
  /solve https://github.com/owner/repo/issues/123 --tool agent --model nemotron-3-super-free
458
470
  /solve https://github.com/owner/repo/issues/123 --tool agent --model opencode/nemotron-3-super-free
package/README.ru.md CHANGED
@@ -444,6 +444,18 @@ Examples:
444
444
  /solve https://github.com/owner/repo/issues/123 --model sonnet
445
445
  /solve https://github.com/owner/repo/issues/123 --model opus --think max
446
446
 
447
+ Aliases:
448
+ /do и /continue эквивалентны /solve
449
+ /claude эквивалентна /solve --tool claude
450
+ /codex эквивалентна /solve --tool codex
451
+ /opencode эквивалентна /solve --tool opencode
452
+ /agent эквивалентна /solve --tool agent
453
+
454
+ Tool alias examples:
455
+ /codex https://github.com/owner/repo/issues/123 --model gpt-5.4
456
+ /opencode https://github.com/owner/repo/issues/123 --model grok-code-fast-1
457
+ /agent https://github.com/owner/repo/issues/123 --model nemotron-3-super-free
458
+
447
459
  Free Models (with --tool agent):
448
460
  /solve https://github.com/owner/repo/issues/123 --tool agent --model nemotron-3-super-free
449
461
  /solve https://github.com/owner/repo/issues/123 --tool agent --model opencode/nemotron-3-super-free
package/README.zh.md CHANGED
@@ -444,6 +444,18 @@ Examples:
444
444
  /solve https://github.com/owner/repo/issues/123 --model sonnet
445
445
  /solve https://github.com/owner/repo/issues/123 --model opus --think max
446
446
 
447
+ Aliases:
448
+ /do 和 /continue 等同于 /solve
449
+ /claude 等同于 /solve --tool claude
450
+ /codex 等同于 /solve --tool codex
451
+ /opencode 等同于 /solve --tool opencode
452
+ /agent 等同于 /solve --tool agent
453
+
454
+ Tool alias examples:
455
+ /codex https://github.com/owner/repo/issues/123 --model gpt-5.4
456
+ /opencode https://github.com/owner/repo/issues/123 --model grok-code-fast-1
457
+ /agent https://github.com/owner/repo/issues/123 --model nemotron-3-super-free
458
+
447
459
  Free Models (with --tool agent):
448
460
  /solve https://github.com/owner/repo/issues/123 --tool agent --model nemotron-3-super-free
449
461
  /solve https://github.com/owner/repo/issues/123 --tool agent --model opencode/nemotron-3-super-free
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.50.15",
3
+ "version": "1.52.0",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -13,7 +13,7 @@
13
13
  "hive-telegram-bot": "./src/telegram-bot.mjs"
14
14
  },
15
15
  "scripts": {
16
- "test": "node tests/solve-queue.test.mjs && node tests/limits-display.test.mjs && node tests/test-usage-limit.mjs && node tests/test-issue-1616-pr-issue-link-preservation.mjs && node tests/test-telegram-message-filters.mjs && node tests/test-solve-queue-command.mjs && node tests/test-queue-display-1267.mjs && node tests/test-telegram-bot-launcher.mjs",
16
+ "test": "node tests/solve-queue.test.mjs && node tests/limits-display.test.mjs && node tests/test-usage-limit.mjs && node tests/test-issue-1616-pr-issue-link-preservation.mjs && node tests/test-telegram-message-filters.mjs && node tests/test-telegram-bot-command-aliases.mjs && node tests/test-solve-queue-command.mjs && node tests/test-queue-display-1267.mjs && node tests/test-telegram-bot-launcher.mjs",
17
17
  "test:queue": "node tests/solve-queue.test.mjs",
18
18
  "test:limits-display": "node tests/limits-display.test.mjs",
19
19
  "test:usage-limit": "node tests/test-usage-limit.mjs",
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mjs';
7
7
  import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.lib.mjs';
8
+ import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
8
9
 
9
10
  /**
10
11
  * Build the user prompt for Agent
@@ -58,15 +59,9 @@ export const buildUserPrompt = params => {
58
59
  promptLines.push('');
59
60
  }
60
61
 
61
- // Add thinking instruction based on --think level
62
- if (argv && argv.think) {
63
- const thinkMessages = {
64
- low: 'Think.',
65
- medium: 'Think hard.',
66
- high: 'Think harder.',
67
- max: 'Ultrathink.',
68
- };
69
- promptLines.push(thinkMessages[argv.think]);
62
+ const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'agent', argv });
63
+ if (thinkingPromptInstruction) {
64
+ promptLines.push(thinkingPromptInstruction);
70
65
  }
71
66
 
72
67
  // Final instruction
@@ -301,6 +301,7 @@ export const executeClaude = async params => {
301
301
  owner,
302
302
  repo,
303
303
  argv,
304
+ claudeVersion: getClaudeVersion(),
304
305
  });
305
306
  // Build the system prompt
306
307
  const systemPrompt = buildSystemPrompt({
@@ -784,7 +785,7 @@ export const executeClaudeCommand = async params => {
784
785
  }
785
786
  try {
786
787
  const { thinkingBudget: resolvedThinkingBudget, thinkLevel, isNewVersion, maxBudget } = await resolveThinkingSettings(argv, log);
787
- const claudeEnv = getClaudeEnv({ thinkingBudget: resolvedThinkingBudget, model: effectiveModel, thinkLevel, maxBudget, planModel: resolvedPlanModel, executionModel: resolvedExecutionModel });
788
+ const claudeEnv = getClaudeEnv({ thinkingBudget: resolvedThinkingBudget, model: effectiveModel, thinkLevel, maxBudget, planModel: resolvedPlanModel, executionModel: resolvedExecutionModel, showThinkingContent: argv.showThinkingContent });
788
789
  if (argv.verbose) claudeEnv.ANTHROPIC_LOG = 'debug';
789
790
  const modelMaxOutputTokens = getMaxOutputTokensForModel(effectiveModel);
790
791
  if (argv.verbose) {
@@ -792,6 +793,7 @@ export const executeClaudeCommand = async params => {
792
793
  if (resolvedPlanModel) await log(`📊 opusplan: plan=${resolvedPlanModel}, exec=${resolvedExecutionModel}`, { verbose: true });
793
794
  if (resolvedThinkingBudget !== undefined) await log(`📊 MAX_THINKING_TOKENS: ${resolvedThinkingBudget}`, { verbose: true });
794
795
  if (claudeEnv.CLAUDE_CODE_EFFORT_LEVEL) await log(`📊 CLAUDE_CODE_EFFORT_LEVEL: ${claudeEnv.CLAUDE_CODE_EFFORT_LEVEL}`, { verbose: true });
796
+ if (claudeEnv.CLAUDE_CODE_SHOW_THINKING) await log(`📊 CLAUDE_CODE_SHOW_THINKING: ${claudeEnv.CLAUDE_CODE_SHOW_THINKING}`, { verbose: true });
795
797
  if (!isNewVersion && thinkLevel) await log(`📊 Thinking level (via keywords): ${thinkLevel}`, { verbose: true });
796
798
  }
797
799
  const simpleEscapedSystem = systemPrompt.replace(/"/g, '\\"');
@@ -6,6 +6,7 @@
6
6
  import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mjs';
7
7
  import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.lib.mjs';
8
8
  import { primaryModelNames } from './models/index.mjs';
9
+ import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
9
10
 
10
11
  /**
11
12
  * Build the user prompt for Claude
@@ -13,7 +14,7 @@ import { primaryModelNames } from './models/index.mjs';
13
14
  * @returns {string} The formatted user prompt
14
15
  */
15
16
  export const buildUserPrompt = params => {
16
- const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, forkedRepo, feedbackLines, owner, repo, argv, contributingGuidelines } = params;
17
+ const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, forkedRepo, feedbackLines, owner, repo, argv, contributingGuidelines, claudeVersion } = params;
17
18
 
18
19
  const promptLines = [];
19
20
 
@@ -65,18 +66,9 @@ export const buildUserPrompt = params => {
65
66
  promptLines.push('');
66
67
  }
67
68
 
68
- // Note: --think keywords are deprecated for Claude Code >= 2.1.12
69
- // Thinking is now enabled by default with 31,999 token budget
70
- // Use --thinking-budget to control MAX_THINKING_TOKENS instead
71
- // Keeping keywords for backward compatibility with older Claude Code versions
72
- if (argv && argv.think) {
73
- const thinkMessages = {
74
- low: 'Think.',
75
- medium: 'Think hard.',
76
- high: 'Think harder.',
77
- max: 'Ultrathink.',
78
- };
79
- promptLines.push(thinkMessages[argv.think]);
69
+ const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'claude', argv, claudeVersion });
70
+ if (thinkingPromptInstruction) {
71
+ promptLines.push(thinkingPromptInstruction);
80
72
  }
81
73
 
82
74
  // Final instruction
@@ -9,6 +9,7 @@ const THINK_LEVEL_TO_CODEX_REASONING = {
9
9
  low: 'low',
10
10
  medium: 'medium',
11
11
  high: 'high',
12
+ xhigh: 'xhigh',
12
13
  max: 'xhigh',
13
14
  };
14
15
 
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mjs';
7
7
  import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.lib.mjs';
8
+ import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
8
9
 
9
10
  /**
10
11
  * Build the user prompt for Codex
@@ -58,15 +59,9 @@ export const buildUserPrompt = params => {
58
59
  promptLines.push('');
59
60
  }
60
61
 
61
- // Add thinking instruction based on --think level
62
- if (argv && argv.think) {
63
- const thinkMessages = {
64
- low: 'Think.',
65
- medium: 'Think hard.',
66
- high: 'Think harder.',
67
- max: 'Ultrathink.',
68
- };
69
- promptLines.push(thinkMessages[argv.think]);
62
+ const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'codex', argv });
63
+ if (thinkingPromptInstruction) {
64
+ promptLines.push(thinkingPromptInstruction);
70
65
  }
71
66
 
72
67
  // Final instruction
@@ -178,11 +178,82 @@ export const isOpus46OrLater = model => {
178
178
  if (!model) return false;
179
179
  const normalizedModel = model.toLowerCase();
180
180
  // Check for explicit opus-4-6 or later versions, or opusplan (Issue #1223)
181
- // Note: The 'opus' alias now maps to Opus 4.6 (Issue #1433), so we also check for the alias directly
181
+ // Note: The 'opus' alias now maps to Opus 4.7 (Issue #1620), so we also check for the alias directly
182
182
  // opusplan uses Opus for planning, so it should get Opus-level settings
183
183
  return normalizedModel === 'opus' || normalizedModel === 'opusplan' || normalizedModel.includes('opus-4-6') || normalizedModel.includes('opus-4-7') || normalizedModel.includes('opus-5');
184
184
  };
185
185
 
186
+ const isOpus47 = model => {
187
+ if (!model) return false;
188
+ const normalizedModel = model.toLowerCase();
189
+ // 'opus' alias now maps to Opus 4.7 (Issue #1620)
190
+ // opusplan uses Opus for planning, so it gets Opus-level settings
191
+ return normalizedModel === 'opus' || normalizedModel === 'opusplan' || normalizedModel.includes('opus-4-7');
192
+ };
193
+
194
+ /**
195
+ * Check if a model is Opus 4.7 or later (Issue #1620)
196
+ * These models use Opus 4.7+ adaptive thinking behavior.
197
+ * @param {string} model - The model name or ID
198
+ * @returns {boolean} True if the model is Opus 4.7 or later
199
+ */
200
+ export const isOpus47OrLater = model => {
201
+ if (!model) return false;
202
+ const normalizedModel = model.toLowerCase();
203
+ return isOpus47(model) || normalizedModel.includes('opus-5');
204
+ };
205
+
206
+ const isOpus45 = model => {
207
+ if (!model) return false;
208
+ const m = model.toLowerCase();
209
+ return m === 'opus-4-5' || m.includes('opus-4-5');
210
+ };
211
+
212
+ const isOpus46 = model => {
213
+ if (!model) return false;
214
+ const m = model.toLowerCase();
215
+ return m === 'opus-4-6' || m.includes('opus-4-6');
216
+ };
217
+
218
+ const isSonnet46OrLater = model => {
219
+ if (!model) return false;
220
+ const m = model.toLowerCase();
221
+ return m === 'sonnet' || m === 'sonnet-4-6' || m.includes('sonnet-4-6') || m.includes('sonnet-5');
222
+ };
223
+
224
+ const isMythosPreview = model => {
225
+ if (!model) return false;
226
+ return model.toLowerCase().includes('mythos');
227
+ };
228
+
229
+ /**
230
+ * Check if a model supports CLAUDE_CODE_EFFORT_LEVEL (Issue #1238, Issue #1620)
231
+ * Official effort support: Claude Mythos Preview, Opus 4.7, Opus 4.6, Sonnet 4.6, and Opus 4.5.
232
+ * Haiku 4.5 and older models use MAX_THINKING_TOKENS only.
233
+ * @param {string} model - The model name or ID
234
+ * @returns {boolean} True if the model supports effort levels
235
+ */
236
+ export const supportsEffortLevel = model => {
237
+ if (!model) return false;
238
+ return isMythosPreview(model) || isOpus47OrLater(model) || isOpus46(model) || isSonnet46OrLater(model) || isOpus45(model);
239
+ };
240
+
241
+ /**
242
+ * Check if a model supports the xhigh effort level.
243
+ * Official docs list xhigh only for Claude Opus 4.7.
244
+ * @param {string} model - The model name or ID
245
+ * @returns {boolean} True if the model supports xhigh effort
246
+ */
247
+ export const supportsXHighEffortLevel = model => isOpus47(model);
248
+
249
+ /**
250
+ * Check if a model supports the max effort level.
251
+ * Official docs list max for Claude Mythos Preview, Opus 4.7, Opus 4.6, and Sonnet 4.6.
252
+ * @param {string} model - The model name or ID
253
+ * @returns {boolean} True if the model supports max effort
254
+ */
255
+ export const supportsMaxEffortLevel = model => isMythosPreview(model) || isOpus47OrLater(model) || isOpus46(model) || isSonnet46OrLater(model);
256
+
186
257
  /**
187
258
  * Get the max output tokens for a specific model (Issue #1221)
188
259
  * @param {string} model - The model name or ID
@@ -218,6 +289,7 @@ export const getThinkingLevelToTokens = (maxBudget = DEFAULT_MAX_THINKING_BUDGET
218
289
  low: Math.floor(maxBudget / 4), // ~8000 for default 31999
219
290
  medium: Math.floor(maxBudget / 2), // ~16000 for default 31999
220
291
  high: Math.floor((maxBudget * 3) / 4), // ~24000 for default 31999
292
+ xhigh: maxBudget, // same as max when represented as MAX_THINKING_TOKENS
221
293
  max: maxBudget, // 31999 by default
222
294
  });
223
295
 
@@ -250,56 +322,73 @@ export const getTokensToThinkingLevel = (maxBudget = DEFAULT_MAX_THINKING_BUDGET
250
322
  export const tokensToThinkingLevel = getTokensToThinkingLevel(DEFAULT_MAX_THINKING_BUDGET);
251
323
 
252
324
  /**
253
- * Valid effort levels for Opus 4.6 (Issue #1238)
254
- * Opus 4.6 uses CLAUDE_CODE_EFFORT_LEVEL for thinking depth control
325
+ * Valid effort levels for Opus 4.6 and Sonnet 4.6 (Issue #1238, Issue #1620)
326
+ * These models use CLAUDE_CODE_EFFORT_LEVEL for thinking depth control
327
+ * @type {string[]}
328
+ */
329
+ export const OPUS_46_EFFORT_LEVELS = ['low', 'medium', 'high', 'max'];
330
+
331
+ /**
332
+ * Valid effort levels for Opus 4.7 (Issue #1620)
333
+ * Opus 4.7 supports the additional 'xhigh' level.
334
+ * See: https://platform.claude.com/docs/en/build-with-claude/effort
255
335
  * @type {string[]}
256
336
  */
257
- export const OPUS_46_EFFORT_LEVELS = ['low', 'medium', 'high'];
337
+ export const OPUS_47_EFFORT_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max'];
258
338
 
259
339
  /**
260
- * Convert thinking level to Opus 4.6 effort level (Issue #1238)
261
- * Opus 4.6 uses CLAUDE_CODE_EFFORT_LEVEL (low/medium/high) instead of MAX_THINKING_TOKENS
262
- * @param {string|undefined} thinkLevel - The thinking level (off/low/medium/high/max)
263
- * @returns {string|undefined} The effort level (low/medium/high) or undefined if thinking is off
340
+ * Convert thinking level to effort level (Issue #1238, Issue #1620)
341
+ * Models with max support keep max as max. Opus 4.7 keeps xhigh as xhigh.
342
+ * Models with effort but without max support use high for max/xhigh.
343
+ * @param {string|undefined} thinkLevel - The thinking level (off/low/medium/high/xhigh/max)
344
+ * @param {Object} [options] - Options
345
+ * @param {boolean} [options.isOpus47] - Backward-compatible shorthand for supportsXHigh
346
+ * @param {boolean} [options.supportsXHigh] - Whether the model supports xhigh effort
347
+ * @param {boolean} [options.supportsMax] - Whether the model supports max effort
348
+ * @returns {string|undefined} The effort level or undefined if thinking is off
264
349
  */
265
- export const thinkLevelToEffortLevel = thinkLevel => {
350
+ export const thinkLevelToEffortLevel = (thinkLevel, options = {}) => {
266
351
  if (!thinkLevel || thinkLevel === 'off') {
267
- // No effort level when thinking is disabled
268
352
  return undefined;
269
353
  }
270
354
 
271
- // Map hive-mind thinking levels to Opus 4.6 effort levels
272
- // Note: Opus 4.6 only supports low/medium/high, not 'max'
273
- // We map 'max' to 'high' as it's the highest available level
355
+ const supportsXHigh = options.supportsXHigh ?? options.isOpus47 ?? false;
356
+ const supportsMax = options.supportsMax ?? true;
357
+
274
358
  switch (thinkLevel) {
275
359
  case 'low':
276
360
  return 'low';
277
361
  case 'medium':
278
362
  return 'medium';
279
363
  case 'high':
280
- case 'max':
281
364
  return 'high';
365
+ case 'xhigh':
366
+ return supportsXHigh ? 'xhigh' : supportsMax ? 'max' : 'high';
367
+ case 'max':
368
+ return supportsMax ? 'max' : 'high';
282
369
  default:
283
370
  return undefined;
284
371
  }
285
372
  };
286
373
 
287
374
  /**
288
- * Convert thinking budget (tokens) to Opus 4.6 effort level (Issue #1238)
375
+ * Convert thinking budget (tokens) to effort level (Issue #1238, Issue #1620)
289
376
  * Uses token thresholds to determine the appropriate effort level
290
377
  * @param {number|undefined} thinkingBudget - The thinking budget in tokens
291
378
  * @param {number} maxBudget - Maximum thinking budget (default: 31999)
292
- * @returns {string|undefined} The effort level (low/medium/high) or undefined if thinking is off
379
+ * @param {Object} [options] - Options
380
+ * @param {boolean} [options.isOpus47] - Backward-compatible shorthand for supportsXHigh
381
+ * @param {boolean} [options.supportsXHigh] - Whether the model supports xhigh effort
382
+ * @param {boolean} [options.supportsMax] - Whether the model supports max effort
383
+ * @returns {string|undefined} The effort level or undefined if thinking is off
293
384
  */
294
- export const thinkingBudgetToEffortLevel = (thinkingBudget, maxBudget = DEFAULT_MAX_THINKING_BUDGET) => {
385
+ export const thinkingBudgetToEffortLevel = (thinkingBudget, maxBudget = DEFAULT_MAX_THINKING_BUDGET, options = {}) => {
295
386
  if (thinkingBudget === undefined || thinkingBudget === 0) {
296
- // No effort level when thinking is disabled
297
387
  return undefined;
298
388
  }
299
389
 
300
- // Convert tokens to thinking level, then to effort level
301
390
  const thinkLevel = getTokensToThinkingLevel(maxBudget)(thinkingBudget);
302
- return thinkLevelToEffortLevel(thinkLevel);
391
+ return thinkLevelToEffortLevel(thinkLevel, options);
303
392
  };
304
393
 
305
394
  // Check if a version supports thinking budget (>= minimum version)
@@ -339,27 +428,40 @@ export const getClaudeEnv = (options = {}) => {
339
428
  MCP_TOOL_TIMEOUT: String(claudeCode.mcpToolTimeout),
340
429
  };
341
430
 
342
- // Set MAX_THINKING_TOKENS to control Claude Code's extended thinking feature (Claude Code >= 2.1.12)
343
- // Default is 0 (thinking disabled) per Issue #1238. Set to 0 to disable thinking.
344
- // Users can explicitly enable thinking via --think or --thinking-budget options.
345
- env.MAX_THINKING_TOKENS = String(options.thinkingBudget ?? 0);
431
+ // Opus 4.7+ always uses adaptive thinking MAX_THINKING_TOKENS has no effect (Issue #1620)
432
+ // For Opus 4.6 and earlier, MAX_THINKING_TOKENS controls extended thinking (Claude Code >= 2.1.12)
433
+ // Default is 0 (thinking disabled) per Issue #1238.
434
+ const opus47 = options.model && isOpus47OrLater(options.model);
435
+ if (opus47) {
436
+ // Remove any inherited MAX_THINKING_TOKENS from process.env — Opus 4.7 ignores it
437
+ delete env.MAX_THINKING_TOKENS;
438
+ } else {
439
+ env.MAX_THINKING_TOKENS = String(options.thinkingBudget ?? 0);
440
+ }
346
441
 
347
- // For Opus 4.6+, also set CLAUDE_CODE_EFFORT_LEVEL to control thinking depth (Issue #1238)
348
- // Opus 4.6 uses effort level (low/medium/high) instead of MAX_THINKING_TOKENS for thinking depth.
349
- // MAX_THINKING_TOKENS is only used to disable thinking (when set to 0).
350
- if (options.model && isOpus46OrLater(options.model)) {
351
- // Convert thinkLevel or thinkingBudget to effort level
442
+ // Set CLAUDE_CODE_EFFORT_LEVEL for models that support it (Issue #1238, Issue #1620)
443
+ if (options.model && supportsEffortLevel(options.model)) {
444
+ const effortOptions = {
445
+ supportsXHigh: supportsXHighEffortLevel(options.model),
446
+ supportsMax: supportsMaxEffortLevel(options.model),
447
+ };
352
448
  let effortLevel;
353
449
  if (options.thinkLevel) {
354
- effortLevel = thinkLevelToEffortLevel(options.thinkLevel);
450
+ effortLevel = thinkLevelToEffortLevel(options.thinkLevel, effortOptions);
355
451
  } else if (options.thinkingBudget !== undefined && options.thinkingBudget > 0) {
356
- effortLevel = thinkingBudgetToEffortLevel(options.thinkingBudget, options.maxBudget);
452
+ effortLevel = thinkingBudgetToEffortLevel(options.thinkingBudget, options.maxBudget, effortOptions);
357
453
  }
358
454
 
359
455
  if (effortLevel) {
360
456
  env.CLAUDE_CODE_EFFORT_LEVEL = effortLevel;
361
457
  }
362
458
  }
459
+
460
+ // Opus 4.7 omits thinking content by default; opt in with --show-thinking-content (Issue #1620)
461
+ // Sets CLAUDE_CODE_SHOW_THINKING=1 which Claude Code uses to request display: "summarized"
462
+ if (options.showThinkingContent) {
463
+ env.CLAUDE_CODE_SHOW_THINKING = '1';
464
+ }
363
465
  // Set ANTHROPIC_DEFAULT_OPUS_MODEL when planModel is specified (Issue #1223)
364
466
  // This tells Claude Code which model to use during plan mode in opusplan
365
467
  if (options.planModel) {
@@ -23,23 +23,25 @@ import { log } from '../lib.mjs';
23
23
  // ─── MODEL DATA ──────────────────────────────────────────────────────────────
24
24
 
25
25
  // Claude models (Anthropic API)
26
- // Updated for Opus 4.5/4.6 and Sonnet 4.6 support (Issue #1221, Issue #1238, Issue #1329, Issue #1433)
26
+ // Updated for Opus 4.5/4.6/4.7 and Sonnet 4.6 support (Issue #1221, Issue #1238, Issue #1329, Issue #1433, Issue #1620)
27
27
  export const claudeModels = {
28
28
  sonnet: 'claude-sonnet-4-6', // Sonnet 4.6 (default, Issue #1329)
29
- opus: 'claude-opus-4-6', // Opus 4.6 (Issue #1433)
29
+ opus: 'claude-opus-4-7', // Opus 4.7 (Issue #1620)
30
30
  haiku: 'claude-haiku-4-5-20251001', // Haiku 4.5
31
31
  'haiku-3-5': 'claude-3-5-haiku-20241022', // Haiku 3.5
32
32
  'haiku-3': 'claude-3-haiku-20240307', // Haiku 3
33
33
  opusplan: 'opusplan', // Special mode: Opus for planning, Sonnet for execution (Issue #1223)
34
34
  // Shorter version aliases (Issue #1221, Issue #1329 - PR comment feedback)
35
35
  'sonnet-4-6': 'claude-sonnet-4-6', // Sonnet 4.6 short alias (Issue #1329)
36
- 'opus-4-6': 'claude-opus-4-6', // Opus 4.6 short alias
36
+ 'opus-4-7': 'claude-opus-4-7', // Opus 4.7 short alias (Issue #1620)
37
+ 'opus-4-6': 'claude-opus-4-6', // Opus 4.6 short alias (backward compatibility)
37
38
  'opus-4-5': 'claude-opus-4-5-20251101', // Opus 4.5 short alias
38
39
  'sonnet-4-5': 'claude-sonnet-4-5-20250929', // Sonnet 4.5 short alias (backward compatibility)
39
40
  'haiku-4-5': 'claude-haiku-4-5-20251001', // Haiku 4.5 short alias
40
- // Version aliases for backward compatibility (Issue #1221, Issue #1329)
41
+ // Version aliases for backward compatibility (Issue #1221, Issue #1329, Issue #1620)
42
+ 'claude-opus-4-7': 'claude-opus-4-7', // Opus 4.7 (Issue #1620)
41
43
  'claude-sonnet-4-6': 'claude-sonnet-4-6', // Sonnet 4.6 (Issue #1329)
42
- 'claude-opus-4-6': 'claude-opus-4-6', // Opus 4.6
44
+ 'claude-opus-4-6': 'claude-opus-4-6', // Opus 4.6 (backward compatibility)
43
45
  'claude-opus-4-5': 'claude-opus-4-5-20251101', // Opus 4.5
44
46
  'claude-sonnet-4-5': 'claude-sonnet-4-5-20250929', // Sonnet 4.5 (backward compatibility)
45
47
  'claude-haiku-4-5': 'claude-haiku-4-5-20251001', // Haiku 4.5
@@ -129,6 +131,7 @@ export const defaultModels = {
129
131
  // Models that support 1M token context window via [1m] suffix (Issue #1221, Issue #1238, Issue #1329)
130
132
  // See: https://code.claude.com/docs/en/model-config
131
133
  export const MODELS_SUPPORTING_1M_CONTEXT = [
134
+ 'claude-opus-4-7', // Opus 4.7 (Issue #1620)
132
135
  'claude-opus-4-6',
133
136
  'claude-opus-4-5-20251101',
134
137
  'claude-sonnet-4-6', // Sonnet 4.6 (Issue #1329)
@@ -136,7 +139,8 @@ export const MODELS_SUPPORTING_1M_CONTEXT = [
136
139
  'claude-sonnet-4-5',
137
140
  'sonnet', // Now maps to Sonnet 4.6 (Issue #1329)
138
141
  'sonnet-4-6', // Short alias (Issue #1329)
139
- 'opus',
142
+ 'opus', // Now maps to Opus 4.7 (Issue #1620)
143
+ 'opus-4-7', // Short alias (Issue #1620)
140
144
  'opus-4-6', // Short alias (Issue #1221 - PR comment feedback)
141
145
  'opus-4-5', // Short alias (Issue #1238)
142
146
  'sonnet-4-5', // Short alias (Issue #1221 - PR comment feedback)
@@ -165,6 +169,7 @@ export const freeToBaseModelMap = {
165
169
 
166
170
  export const CLAUDE_MODELS = {
167
171
  ...claudeModels,
172
+ 'claude-opus-4-7': 'claude-opus-4-7', // Opus 4.7 full ID (Issue #1620)
168
173
  'claude-sonnet-4-5-20250929': 'claude-sonnet-4-5-20250929',
169
174
  'claude-opus-4-5-20251101': 'claude-opus-4-5-20251101',
170
175
  'claude-haiku-4-5-20251001': 'claude-haiku-4-5-20251001',
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mjs';
7
7
  import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.lib.mjs';
8
+ import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
8
9
 
9
10
  /**
10
11
  * Build the user prompt for OpenCode
@@ -58,15 +59,9 @@ export const buildUserPrompt = params => {
58
59
  promptLines.push('');
59
60
  }
60
61
 
61
- // Add thinking instruction based on --think level
62
- if (argv && argv.think) {
63
- const thinkMessages = {
64
- low: 'Think.',
65
- medium: 'Think hard.',
66
- high: 'Think harder.',
67
- max: 'Ultrathink.',
68
- };
69
- promptLines.push(thinkMessages[argv.think]);
62
+ const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'opencode', argv });
63
+ if (thinkingPromptInstruction) {
64
+ promptLines.push(thinkingPromptInstruction);
70
65
  }
71
66
 
72
67
  // Final instruction
@@ -229,8 +229,8 @@ export const SOLVE_OPTION_DEFINITIONS = {
229
229
  },
230
230
  think: {
231
231
  type: 'string',
232
- description: 'Thinking level hint. For Claude, translated to --thinking-budget for Claude Code >= 2.1.12 (off=0, low=~8000, medium=~16000, high=~24000, max=31999). For Codex, mapped to reasoning effort (off=none, low=low, medium=medium, high=high, max=xhigh).',
233
- choices: ['off', 'low', 'medium', 'high', 'max'],
232
+ description: 'Thinking level hint. For Claude, translated to --thinking-budget for Claude Code >= 2.1.12 (off=0, low=~8000, medium=~16000, high=~24000, xhigh/max=31999) and to CLAUDE_CODE_EFFORT_LEVEL when supported. Opus 4.7 supports xhigh and max; Opus 4.6/Sonnet 4.6/Mythos support max; Opus 4.5 uses high for xhigh/max. For Codex, mapped to reasoning effort (off=none, low=low, medium=medium, high=high, xhigh/max=xhigh).',
233
+ choices: ['off', 'low', 'medium', 'high', 'xhigh', 'max'],
234
234
  default: undefined,
235
235
  },
236
236
  'thinking-budget': {
@@ -248,6 +248,11 @@ export const SOLVE_OPTION_DEFINITIONS = {
248
248
  description: 'Maximum thinking budget for calculating --think level mappings (default: 31999 for Claude Code). Values: off=0, low=max/4, medium=max/2, high=max*3/4, max=max.',
249
249
  default: 31999,
250
250
  },
251
+ 'show-thinking-content': {
252
+ type: 'boolean',
253
+ description: 'Show thinking content in Claude responses. Opus 4.7 omits thinking content by default; this option opts in to receive summarized thinking blocks. Disabled by default. Only affects --tool claude.',
254
+ default: false,
255
+ },
251
256
  'prompt-plan-sub-agent': {
252
257
  type: 'boolean',
253
258
  description: 'Encourage AI to use a planning sub-agent or planning workflow for initial planning. Supported for --tool claude and --tool codex.',
@@ -45,6 +45,7 @@ const { formatUsageMessage, formatCodexLimitsSection, getAllCachedLimits } = awa
45
45
  const { getVersionInfo, formatVersionMessage } = await import('./version-info.lib.mjs');
46
46
  const { escapeMarkdown, escapeMarkdownV2, cleanNonPrintableChars, makeSpecialCharsVisible } = await import('./telegram-markdown.lib.mjs');
47
47
  const { getSolveQueue, createQueueExecuteCallback } = await import('./telegram-solve-queue.lib.mjs');
48
+ const { applySolveToolAlias, getSolveCommandNameFromText, getSolveToolAliasFromText, parseCommandArgs, SOLVE_COMMAND_NAMES } = await import('./telegram-solve-command.lib.mjs');
48
49
  const { isChatStopped, getChatStopInfo, getStoppedChatRejectMessage, DEFAULT_STOP_REASON } = await import('./telegram-start-stop-command.lib.mjs');
49
50
  const { isOldMessage: _isOldMessage, isGroupChat: _isGroupChat, isChatAuthorized: _isChatAuthorized, isForwardedOrReply: _isForwardedOrReply, extractCommandFromText, extractGitHubUrl: _extractGitHubUrl } = await import('./telegram-message-filters.lib.mjs');
50
51
  const { launchBotWithRetry } = await import('./telegram-bot-launcher.lib.mjs');
@@ -447,49 +448,6 @@ function validateModelInArgs(args, tool = 'claude') {
447
448
  return null;
448
449
  }
449
450
 
450
- function parseCommandArgs(text) {
451
- // Use only first line and trim it
452
- const firstLine = text.split('\n')[0].trim();
453
- const argsText = firstLine.replace(/^\/\w+\s*/, '');
454
-
455
- if (!argsText.trim()) {
456
- return [];
457
- }
458
-
459
- // Replace em-dash (—) with double-dash (--) to fix Telegram auto-replacement
460
- const normalizedArgsText = argsText.replace(/—/g, '--');
461
-
462
- const args = [];
463
- let currentArg = '';
464
- let inQuotes = false;
465
- let quoteChar = null;
466
-
467
- for (let i = 0; i < normalizedArgsText.length; i++) {
468
- const char = normalizedArgsText[i];
469
-
470
- if ((char === '"' || char === "'") && !inQuotes) {
471
- inQuotes = true;
472
- quoteChar = char;
473
- } else if (char === quoteChar && inQuotes) {
474
- inQuotes = false;
475
- quoteChar = null;
476
- } else if (char === ' ' && !inQuotes) {
477
- if (currentArg) {
478
- args.push(currentArg);
479
- currentArg = '';
480
- }
481
- } else {
482
- currentArg += char;
483
- }
484
- }
485
-
486
- if (currentArg) {
487
- args.push(currentArg);
488
- }
489
-
490
- return args;
491
- }
492
-
493
451
  function mergeArgsWithOverrides(userArgs, overrides) {
494
452
  if (!overrides || overrides.length === 0) {
495
453
  return userArgs;
@@ -651,16 +609,17 @@ bot.command('help', async ctx => {
651
609
  message += '📝 *Available Commands:*\n\n';
652
610
 
653
611
  if (solveEnabled) {
654
- message += '*/solve* (aliases: */do*, */continue*) - Solve a GitHub issue\n';
612
+ message += '*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*) - Solve a GitHub issue\n';
655
613
  message += 'Usage: `/solve <github-url> [options]`\n';
656
614
  message += 'Example: `/solve https://github.com/owner/repo/issues/123 --model sonnet`\n';
615
+ message += 'Tool aliases imply `--tool <tool>`: `/codex <github-url>` equals `/solve <github-url> --tool codex`\n';
657
616
  message += 'Or reply to a message with a GitHub link: `/solve`\n';
658
617
  if (solveOverrides.length > 0) {
659
618
  message += `🔒 Locked options: \`${solveOverrides.join(' ')}\`\n`;
660
619
  }
661
620
  message += '\n';
662
621
  } else {
663
- message += '*/solve* (aliases: */do*, */continue*) - ❌ Disabled\n\n';
622
+ message += '*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*) - ❌ Disabled\n\n';
664
623
  }
665
624
 
666
625
  if (hiveEnabled) {
@@ -688,11 +647,11 @@ bot.command('help', async ctx => {
688
647
  message += '🔔 *Session Notifications:* The bot monitors sessions and notifies when they complete.\n';
689
648
  if (ISOLATION_BACKEND) message += `🔒 *Isolation Mode:* \`${ISOLATION_BACKEND}\` (experimental)\n`;
690
649
  message += '\n';
691
- message += '⚠️ *Note:* /solve, /do, /continue, /hive, /solve\\_queue, /limits, /version, /accept\\_invites, /merge, /stop and /start commands only work in group chats.\n\n';
650
+ message += '⚠️ *Note:* /solve, /do, /continue, /claude, /codex, /opencode, /agent, /hive, /solve\\_queue, /limits, /version, /accept\\_invites, /merge, /stop and /start commands only work in group chats.\n\n';
692
651
  message += '🔧 *Common Options:*\n';
693
652
  message += `• \`--model <model>\` or \`-m\` - ${buildModelOptionDescription()}\n`;
694
653
  message += '• `--base-branch <branch>` or `-b` - Target branch for PR (default: repo default branch)\n';
695
- message += '• `--think <level>` - Thinking level (off/low/medium/high/max) | `--thinking-budget <num>` - Token budget (0-63999)\n';
654
+ message += '• `--think <level>` - Thinking level (off/low/medium/high/xhigh/max) | `--thinking-budget <num>` - Token budget (0-63999)\n';
696
655
  message += '• `--verbose` or `-v` - Verbose output | `--attach-logs` - Attach logs to PR\n';
697
656
  message += '\n💡 *Tip:* Many more options available. See full documentation for complete list.\n';
698
657
 
@@ -795,12 +754,14 @@ const { handleSolveQueueCommand } = registerSolveQueueCommand(bot, { ...sharedCo
795
754
 
796
755
  // Named handler for /solve command - extracted for reuse by text-based fallback (issue #1207)
797
756
  async function handleSolveCommand(ctx) {
798
- VERBOSE && console.log('[VERBOSE] /solve command received');
757
+ const solveCommandName = getSolveCommandNameFromText(ctx.message?.text) || 'solve';
758
+ const solveCommandDisplay = `/${solveCommandName}`;
759
+ VERBOSE && console.log(`[VERBOSE] ${solveCommandDisplay} command received`);
799
760
 
800
761
  // Add breadcrumb for error tracking
801
762
  await addBreadcrumb({
802
763
  category: 'telegram.command',
803
- message: '/solve command received',
764
+ message: `${solveCommandDisplay} command received`,
804
765
  level: 'info',
805
766
  data: {
806
767
  chatId: ctx.chat?.id,
@@ -812,16 +773,16 @@ async function handleSolveCommand(ctx) {
812
773
 
813
774
  if (!solveEnabled) {
814
775
  if (VERBOSE) {
815
- console.log('[VERBOSE] /solve ignored: command disabled');
776
+ console.log(`[VERBOSE] ${solveCommandDisplay} ignored: command disabled`);
816
777
  }
817
- await ctx.reply('❌ The /solve command is disabled on this bot instance.');
778
+ await ctx.reply('❌ The solve command is disabled on this bot instance.');
818
779
  return;
819
780
  }
820
781
 
821
782
  // Ignore messages sent before bot started
822
783
  if (isOldMessage(ctx)) {
823
784
  if (VERBOSE) {
824
- console.log('[VERBOSE] /solve ignored: old message');
785
+ console.log(`[VERBOSE] ${solveCommandDisplay} ignored: old message`);
825
786
  }
826
787
  return;
827
788
  }
@@ -834,22 +795,22 @@ async function handleSolveCommand(ctx) {
834
795
 
835
796
  if (isForwarded || isOldApiForwarded) {
836
797
  if (VERBOSE) {
837
- console.log('[VERBOSE] /solve ignored: forwarded message');
798
+ console.log(`[VERBOSE] ${solveCommandDisplay} ignored: forwarded message`);
838
799
  }
839
800
  return;
840
801
  }
841
802
 
842
803
  if (!_isGroupChat(ctx)) {
843
804
  if (VERBOSE) {
844
- console.log('[VERBOSE] /solve ignored: not a group chat');
805
+ console.log(`[VERBOSE] ${solveCommandDisplay} ignored: not a group chat`);
845
806
  }
846
- await ctx.reply('❌ The /solve command only works in group chats. Please add this bot to a group and make it an admin.', { reply_to_message_id: ctx.message.message_id });
807
+ await ctx.reply(`❌ The ${solveCommandDisplay} command only works in group chats. Please add this bot to a group and make it an admin.`, { reply_to_message_id: ctx.message.message_id });
847
808
  return;
848
809
  }
849
810
 
850
811
  if (!isTopicAuthorized(ctx)) {
851
812
  if (VERBOSE) {
852
- console.log('[VERBOSE] /solve ignored: not authorized');
813
+ console.log(`[VERBOSE] ${solveCommandDisplay} ignored: not authorized`);
853
814
  }
854
815
  await ctx.reply(buildAuthErrorMessage(ctx), { reply_to_message_id: ctx.message.message_id });
855
816
  return;
@@ -863,8 +824,9 @@ async function handleSolveCommand(ctx) {
863
824
  return;
864
825
  }
865
826
 
866
- VERBOSE && console.log('[VERBOSE] /solve passed all checks, executing...');
827
+ VERBOSE && console.log(`[VERBOSE] ${solveCommandDisplay} passed all checks, executing...`);
867
828
 
829
+ const solveToolAlias = getSolveToolAliasFromText(ctx.message.text);
868
830
  let userArgs = parseCommandArgs(ctx.message.text);
869
831
 
870
832
  // Check if this is a reply to a message and user didn't provide URL as first argument
@@ -911,6 +873,8 @@ async function handleSolveCommand(ctx) {
911
873
  }
912
874
  }
913
875
 
876
+ userArgs = applySolveToolAlias(userArgs, solveToolAlias);
877
+
914
878
  const validation = validateGitHubUrl(userArgs);
915
879
  if (!validation.valid) {
916
880
  let errorMsg = `❌ ${validation.error}`;
@@ -1040,7 +1004,10 @@ async function handleSolveCommand(ctx) {
1040
1004
  }
1041
1005
  }
1042
1006
 
1043
- bot.command([/^solve$/i, /^do$/i, /^continue$/i], handleSolveCommand); // /do and /continue are aliases (issue #525)
1007
+ bot.command(
1008
+ SOLVE_COMMAND_NAMES.map(command => new RegExp(`^${command}$`, 'i')),
1009
+ handleSolveCommand
1010
+ );
1044
1011
 
1045
1012
  // Named handler for /hive command - extracted for reuse by text-based fallback (issue #1207)
1046
1013
  async function handleHiveCommand(ctx) {
@@ -1267,8 +1234,8 @@ bot.on('message', async (ctx, next) => {
1267
1234
  }
1268
1235
 
1269
1236
  // Check if this is a command we handle
1270
- // /do and /continue are aliases for /solve (issue #525)
1271
- const handlers = { solve: handleSolveCommand, do: handleSolveCommand, continue: handleSolveCommand, hive: handleHiveCommand, solve_queue: handleSolveQueueCommand, solvequeue: handleSolveQueueCommand };
1237
+ const solveHandlers = Object.fromEntries(SOLVE_COMMAND_NAMES.map(command => [command, handleSolveCommand]));
1238
+ const handlers = { ...solveHandlers, hive: handleHiveCommand, solve_queue: handleSolveQueueCommand, solvequeue: handleSolveQueueCommand };
1272
1239
 
1273
1240
  const handler = handlers[extracted.command];
1274
1241
  if (!handler) return next();
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Shared parsing helpers for Telegram solve commands.
3
+ *
4
+ * Keeps /solve aliases and argument normalization testable without loading the
5
+ * full Telegram bot entry point.
6
+ *
7
+ * @see https://github.com/link-assistant/hive-mind/issues/525
8
+ * @see https://github.com/link-assistant/hive-mind/issues/1618
9
+ */
10
+
11
+ export const TOOL_SOLVE_COMMAND_ALIASES = Object.freeze({
12
+ claude: 'claude',
13
+ codex: 'codex',
14
+ opencode: 'opencode',
15
+ agent: 'agent',
16
+ });
17
+
18
+ export const SOLVE_COMMAND_NAMES = Object.freeze(['solve', 'do', 'continue', ...Object.keys(TOOL_SOLVE_COMMAND_ALIASES)]);
19
+
20
+ export function parseCommandArgs(text) {
21
+ const firstLine = text.split('\n')[0].trim();
22
+ const argsText = firstLine.replace(/^\/\w+(?:@\S+)?\s*/, '');
23
+
24
+ if (!argsText.trim()) {
25
+ return [];
26
+ }
27
+
28
+ // Replace em-dash with double-dash to fix Telegram auto-replacement.
29
+ const normalizedArgsText = argsText.replace(/—/g, '--');
30
+
31
+ const args = [];
32
+ let currentArg = '';
33
+ let inQuotes = false;
34
+ let quoteChar = null;
35
+
36
+ for (let i = 0; i < normalizedArgsText.length; i++) {
37
+ const char = normalizedArgsText[i];
38
+
39
+ if ((char === '"' || char === "'") && !inQuotes) {
40
+ inQuotes = true;
41
+ quoteChar = char;
42
+ } else if (char === quoteChar && inQuotes) {
43
+ inQuotes = false;
44
+ quoteChar = null;
45
+ } else if (char === ' ' && !inQuotes) {
46
+ if (currentArg) {
47
+ args.push(currentArg);
48
+ currentArg = '';
49
+ }
50
+ } else {
51
+ currentArg += char;
52
+ }
53
+ }
54
+
55
+ if (currentArg) {
56
+ args.push(currentArg);
57
+ }
58
+
59
+ return args;
60
+ }
61
+
62
+ export function getSolveCommandNameFromText(text) {
63
+ if (!text || typeof text !== 'string') {
64
+ return null;
65
+ }
66
+
67
+ const firstLine = text.split('\n')[0].trim();
68
+ const match = firstLine.match(/^\/(\w+)(?:@\S+)?(?:\s|$)/);
69
+ return match ? match[1].toLowerCase() : null;
70
+ }
71
+
72
+ export function getSolveToolAliasFromText(text) {
73
+ const command = getSolveCommandNameFromText(text);
74
+ return command ? TOOL_SOLVE_COMMAND_ALIASES[command] || null : null;
75
+ }
76
+
77
+ export function applySolveToolAlias(args, toolAlias) {
78
+ if (!toolAlias || args.length === 0) {
79
+ return args;
80
+ }
81
+
82
+ const filteredArgs = [];
83
+
84
+ for (let i = 0; i < args.length; i++) {
85
+ const arg = args[i];
86
+
87
+ if (arg === '--tool') {
88
+ if (i + 1 < args.length && !args[i + 1].startsWith('--')) {
89
+ i++;
90
+ }
91
+ continue;
92
+ }
93
+
94
+ if (arg.startsWith('--tool=')) {
95
+ continue;
96
+ }
97
+
98
+ filteredArgs.push(arg);
99
+ }
100
+
101
+ return [...filteredArgs, '--tool', toolAlias];
102
+ }
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { supportsEffortLevel, supportsThinkingBudget } from './config.lib.mjs';
4
+ import { defaultModels, mapModelForTool } from './models/index.mjs';
5
+
6
+ export const THINK_PROMPT_MESSAGES = Object.freeze({
7
+ low: 'Think.',
8
+ medium: 'Think hard.',
9
+ high: 'Think harder.',
10
+ xhigh: 'Ultrathink.',
11
+ max: 'Ultrathink.',
12
+ });
13
+
14
+ export const isClaudeLikeModel = model => {
15
+ if (!model) return false;
16
+ const normalized = String(model).toLowerCase();
17
+ return normalized === 'opusplan' || normalized.includes('claude') || normalized.startsWith('anthropic/');
18
+ };
19
+
20
+ export const resolvePromptModelForTool = (tool = 'claude', model) => {
21
+ const selectedModel = model || defaultModels[tool];
22
+ return selectedModel ? mapModelForTool(tool, selectedModel) : null;
23
+ };
24
+
25
+ export const toolSupportsStructuredThinkingBudget = ({ tool = 'claude', claudeVersion, thinkingBudgetClaudeMinimumVersion } = {}) => {
26
+ if (tool === 'claude') {
27
+ return supportsThinkingBudget(claudeVersion || '0.0.0', thinkingBudgetClaudeMinimumVersion || '2.1.12');
28
+ }
29
+
30
+ // Codex maps --think/--thinking-budget to model_reasoning_effort instead of prompt text.
31
+ return tool === 'codex';
32
+ };
33
+
34
+ export const shouldAddThinkingPromptInstruction = ({ tool = 'claude', argv, claudeVersion } = {}) => {
35
+ const thinkLevel = argv?.think;
36
+ if (!thinkLevel || !THINK_PROMPT_MESSAGES[thinkLevel]) {
37
+ return false;
38
+ }
39
+
40
+ const resolvedModel = resolvePromptModelForTool(tool, argv?.model);
41
+ if (!isClaudeLikeModel(resolvedModel)) {
42
+ return false;
43
+ }
44
+
45
+ if (supportsEffortLevel(resolvedModel)) {
46
+ return false;
47
+ }
48
+
49
+ return !toolSupportsStructuredThinkingBudget({
50
+ tool,
51
+ claudeVersion,
52
+ thinkingBudgetClaudeMinimumVersion: argv?.thinkingBudgetClaudeMinimumVersion,
53
+ });
54
+ };
55
+
56
+ export const getThinkingPromptInstruction = options => {
57
+ if (!shouldAddThinkingPromptInstruction(options)) {
58
+ return undefined;
59
+ }
60
+ return THINK_PROMPT_MESSAGES[options?.argv?.think];
61
+ };