@link-assistant/hive-mind 1.50.14 → 1.51.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 +12 -0
- package/README.hi.md +12 -0
- package/README.md +12 -0
- package/README.ru.md +12 -0
- package/README.zh.md +12 -0
- package/package.json +2 -2
- package/src/solve.validation.lib.mjs +3 -1
- package/src/telegram-bot.mjs +26 -59
- package/src/telegram-solve-command.lib.mjs +102 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.51.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- fd3c76c: Add per-tool Telegram solve aliases: /claude, /codex, /opencode, and /agent.
|
|
8
|
+
|
|
9
|
+
## 1.50.15
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 7cecf09: Fix auto-resume reset time parsing when usage-limit output includes a month/day prefix such as `Apr 17, 4:00 AM`.
|
|
14
|
+
|
|
3
15
|
## 1.50.14
|
|
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.
|
|
3
|
+
"version": "1.51.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",
|
|
@@ -371,10 +371,12 @@ export const parseUrlComponents = issueUrl => {
|
|
|
371
371
|
export const parseResetTime = timeStr => {
|
|
372
372
|
// Normalize and parse time formats like:
|
|
373
373
|
// "5:30am", "11:45pm", "12:16 PM", "07:05 Am", "5am", "5 AM"
|
|
374
|
+
// Also accepts date+time forms like "Apr 17, 4:00 AM" and ignores the date portion.
|
|
374
375
|
const normalized = (timeStr || '').toString().trim();
|
|
376
|
+
const timePortion = normalized.replace(/^(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:t(?:ember)?)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\s+\d{1,2},\s+/i, '');
|
|
375
377
|
|
|
376
378
|
// Accept both HH:MM am/pm and HH am/pm
|
|
377
|
-
let match =
|
|
379
|
+
let match = timePortion.match(/^(\d{1,2})(?::(\d{2}))?\s*([ap]m)$/i);
|
|
378
380
|
if (!match) {
|
|
379
381
|
throw new Error(`Invalid time format: ${timeStr}`);
|
|
380
382
|
}
|
package/src/telegram-bot.mjs
CHANGED
|
@@ -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,7 +647,7 @@ 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';
|
|
@@ -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
|
-
|
|
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:
|
|
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(
|
|
776
|
+
console.log(`[VERBOSE] ${solveCommandDisplay} ignored: command disabled`);
|
|
816
777
|
}
|
|
817
|
-
await ctx.reply('❌ The
|
|
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(
|
|
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(
|
|
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(
|
|
805
|
+
console.log(`[VERBOSE] ${solveCommandDisplay} ignored: not a group chat`);
|
|
845
806
|
}
|
|
846
|
-
await ctx.reply(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
1271
|
-
const handlers = {
|
|
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
|
+
}
|