@link-assistant/hive-mind 1.63.0 → 1.64.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 +6 -0
- package/README.hi.md +4 -3
- package/README.md +5 -3
- package/README.ru.md +4 -3
- package/README.zh.md +4 -3
- package/package.json +2 -2
- package/src/agent-commander.lib.mjs +361 -0
- package/src/claude.prompts.lib.mjs +2 -2
- package/src/codex.prompts.lib.mjs +2 -2
- package/src/hive.config.lib.mjs +1 -1
- package/src/models/index.mjs +46 -30
- package/src/qwen.prompts.lib.mjs +2 -2
- package/src/solve.config.lib.mjs +11 -5
- package/src/solve.mjs +35 -103
- package/src/solve.restart-shared.lib.mjs +28 -1
- package/src/solve.validation.lib.mjs +12 -1
- package/src/task.mjs +1 -1
- package/src/telegram-solve-command.lib.mjs +1 -1
- package/src/telegram-solve-queue.helpers.lib.mjs +8 -8
- package/src/telegram-solve-queue.lib.mjs +14 -14
- package/src/tool-connection-validation.lib.mjs +3 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.64.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2ffb808: Add experimental `--use-agent-commander` option to delegate supported tool execution to the agent-commander library, including Claude, Codex, OpenCode, Agent, Qwen, and Gemini.
|
|
8
|
+
|
|
3
9
|
## 1.63.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
package/README.hi.md
CHANGED
|
@@ -343,7 +343,7 @@ solve <issue-url> [options]
|
|
|
343
343
|
|
|
344
344
|
| विकल्प | संक्षिप्त | विवरण | डिफ़ॉल्ट |
|
|
345
345
|
| ------------------------ | --------- | ----------------------------------------------------------- | -------- |
|
|
346
|
-
| `--tool` | | AI टूल (claude, opencode, codex, agent,
|
|
346
|
+
| `--tool` | | AI टूल (claude, opencode, codex, agent, qwen, gemini) | claude |
|
|
347
347
|
| `--verbose` | `-v` | विस्तृत लॉगिंग सक्षम करें | false |
|
|
348
348
|
| `--attach-logs` | | PR में लॉग संलग्न करें (⚠️ संवेदनशील डेटा उजागर हो सकता है) | false |
|
|
349
349
|
| `--auto-init-repository` | | खाली रेपो स्वतः-आरंभ करें (README.md बनाता है) | false |
|
|
@@ -370,7 +370,7 @@ hive <github-url> [options]
|
|
|
370
370
|
|
|
371
371
|
| विकल्प | संक्षिप्त | विवरण | डिफ़ॉल्ट |
|
|
372
372
|
| ------------------------ | --------- | ----------------------------------------------------------- | -------- |
|
|
373
|
-
| `--tool` | | AI टूल (claude, opencode, codex, agent,
|
|
373
|
+
| `--tool` | | AI टूल (claude, opencode, codex, agent, qwen, gemini) | claude |
|
|
374
374
|
| `--concurrency` | `-c` | समानांतर वर्कर की संख्या | 2 |
|
|
375
375
|
| `--skip-issues-with-prs` | `-s` | मौजूदा PR वाले इश्यू छोड़ें | false |
|
|
376
376
|
| `--verbose` | `-v` | विस्तृत लॉगिंग सक्षम करें | false |
|
|
@@ -462,8 +462,8 @@ Aliases:
|
|
|
462
462
|
/codex /solve --tool codex के बराबर है
|
|
463
463
|
/opencode /solve --tool opencode के बराबर है
|
|
464
464
|
/agent /solve --tool agent के बराबर है
|
|
465
|
-
/gemini /solve --tool gemini के बराबर है
|
|
466
465
|
/qwen /solve --tool qwen के बराबर है
|
|
466
|
+
/gemini /solve --tool gemini के बराबर है
|
|
467
467
|
|
|
468
468
|
Tool alias examples:
|
|
469
469
|
/codex https://github.com/owner/repo/issues/123 --model gpt-5.5
|
|
@@ -471,6 +471,7 @@ Tool alias examples:
|
|
|
471
471
|
/agent https://github.com/owner/repo/issues/123 --model nemotron-3-super-free
|
|
472
472
|
/gemini https://github.com/owner/repo/issues/123 --model flash
|
|
473
473
|
/qwen https://github.com/owner/repo/issues/123 --model qwen3-coder-plus
|
|
474
|
+
/gemini https://github.com/owner/repo/issues/123 --model gemini-2.5-flash
|
|
474
475
|
|
|
475
476
|
Free Models (with --tool agent):
|
|
476
477
|
/solve https://github.com/owner/repo/issues/123 --tool agent --model nemotron-3-super-free
|
package/README.md
CHANGED
|
@@ -352,7 +352,7 @@ solve <issue-url> [options]
|
|
|
352
352
|
|
|
353
353
|
| Option | Alias | Description | Default |
|
|
354
354
|
| ------------------------ | ----- | ------------------------------------------------------ | ------- |
|
|
355
|
-
| `--tool` | | AI tool (claude, opencode, codex, agent,
|
|
355
|
+
| `--tool` | | AI tool (claude, opencode, codex, agent, qwen, gemini) | claude |
|
|
356
356
|
| `--verbose` | `-v` | Enable verbose logging | false |
|
|
357
357
|
| `--attach-logs` | | Attach logs to PR (⚠️ may expose sensitive data) | false |
|
|
358
358
|
| `--auto-init-repository` | | Auto-initialize empty repos (creates README.md) | false |
|
|
@@ -379,7 +379,7 @@ hive <github-url> [options]
|
|
|
379
379
|
|
|
380
380
|
| Option | Alias | Description | Default |
|
|
381
381
|
| ------------------------ | ----- | ------------------------------------------------------ | ------- |
|
|
382
|
-
| `--tool` | | AI tool (claude, opencode, codex, agent,
|
|
382
|
+
| `--tool` | | AI tool (claude, opencode, codex, agent, qwen, gemini) | claude |
|
|
383
383
|
| `--concurrency` | `-c` | Number of parallel workers | 2 |
|
|
384
384
|
| `--skip-issues-with-prs` | `-s` | Skip issues with existing PRs | false |
|
|
385
385
|
| `--verbose` | `-v` | Enable verbose logging | false |
|
|
@@ -469,8 +469,8 @@ Aliases:
|
|
|
469
469
|
/codex is equivalent to /solve --tool codex
|
|
470
470
|
/opencode is equivalent to /solve --tool opencode
|
|
471
471
|
/agent is equivalent to /solve --tool agent
|
|
472
|
-
/gemini is equivalent to /solve --tool gemini
|
|
473
472
|
/qwen is equivalent to /solve --tool qwen
|
|
473
|
+
/gemini is equivalent to /solve --tool gemini
|
|
474
474
|
|
|
475
475
|
Tool alias examples:
|
|
476
476
|
/codex https://github.com/owner/repo/issues/123 --model gpt-5.5
|
|
@@ -478,6 +478,7 @@ Tool alias examples:
|
|
|
478
478
|
/agent https://github.com/owner/repo/issues/123 --model nemotron-3-super-free
|
|
479
479
|
/gemini https://github.com/owner/repo/issues/123 --model flash
|
|
480
480
|
/qwen https://github.com/owner/repo/issues/123 --model qwen3-coder-plus
|
|
481
|
+
/gemini https://github.com/owner/repo/issues/123 --model gemini-2.5-flash
|
|
481
482
|
|
|
482
483
|
Free Models (with --tool agent):
|
|
483
484
|
/solve https://github.com/owner/repo/issues/123 --tool agent --model nemotron-3-super-free
|
|
@@ -501,6 +502,7 @@ Current tool defaults in Hive Mind:
|
|
|
501
502
|
| `agent` | `nemotron-3-super-free` | No extra thinking prompt is added for the default model |
|
|
502
503
|
| `gemini` | `flash` | No extra thinking prompt is added for the default model |
|
|
503
504
|
| `qwen` | `qwen3-coder-plus` | No extra thinking prompt is added for the default model |
|
|
505
|
+
| `gemini` | `gemini-2.5-flash` | No extra thinking prompt is added for the default model |
|
|
504
506
|
|
|
505
507
|
See [docs/CONFIGURATION.md](./docs/CONFIGURATION.md) for the full per-tool defaults and reasoning mappings.
|
|
506
508
|
|
package/README.ru.md
CHANGED
|
@@ -343,7 +343,7 @@ solve <issue-url> [options]
|
|
|
343
343
|
|
|
344
344
|
| Параметр | Сокр. | Описание | По умолчанию |
|
|
345
345
|
| ------------------------ | ----- | --------------------------------------------------------------------- | ------------ |
|
|
346
|
-
| `--tool` | | Инструмент ИИ (claude, opencode, codex, agent,
|
|
346
|
+
| `--tool` | | Инструмент ИИ (claude, opencode, codex, agent, qwen, gemini) | claude |
|
|
347
347
|
| `--verbose` | `-v` | Включить подробное логирование | false |
|
|
348
348
|
| `--attach-logs` | | Прикрепить логи к PR (⚠️ может раскрыть конфиденциальные данные) | false |
|
|
349
349
|
| `--auto-init-repository` | | Автоматически инициализировать пустые репозитории (создаёт README.md) | false |
|
|
@@ -370,7 +370,7 @@ hive <github-url> [options]
|
|
|
370
370
|
|
|
371
371
|
| Параметр | Сокр. | Описание | По умолчанию |
|
|
372
372
|
| ------------------------ | ----- | ----------------------------------------------------------------- | ------------ |
|
|
373
|
-
| `--tool` | | Инструмент ИИ (claude, opencode, codex, agent,
|
|
373
|
+
| `--tool` | | Инструмент ИИ (claude, opencode, codex, agent, qwen, gemini) | claude |
|
|
374
374
|
| `--concurrency` | `-c` | Количество параллельных воркеров | 2 |
|
|
375
375
|
| `--skip-issues-with-prs` | `-s` | Пропускать задачи с существующими PR | false |
|
|
376
376
|
| `--verbose` | `-v` | Включить подробное логирование | false |
|
|
@@ -463,8 +463,8 @@ Aliases:
|
|
|
463
463
|
/codex эквивалентна /solve --tool codex
|
|
464
464
|
/opencode эквивалентна /solve --tool opencode
|
|
465
465
|
/agent эквивалентна /solve --tool agent
|
|
466
|
-
/gemini эквивалентна /solve --tool gemini
|
|
467
466
|
/qwen эквивалентна /solve --tool qwen
|
|
467
|
+
/gemini эквивалентна /solve --tool gemini
|
|
468
468
|
|
|
469
469
|
Tool alias examples:
|
|
470
470
|
/codex https://github.com/owner/repo/issues/123 --model gpt-5.5
|
|
@@ -472,6 +472,7 @@ Tool alias examples:
|
|
|
472
472
|
/agent https://github.com/owner/repo/issues/123 --model nemotron-3-super-free
|
|
473
473
|
/gemini https://github.com/owner/repo/issues/123 --model flash
|
|
474
474
|
/qwen https://github.com/owner/repo/issues/123 --model qwen3-coder-plus
|
|
475
|
+
/gemini https://github.com/owner/repo/issues/123 --model gemini-2.5-flash
|
|
475
476
|
|
|
476
477
|
Free Models (with --tool agent):
|
|
477
478
|
/solve https://github.com/owner/repo/issues/123 --tool agent --model nemotron-3-super-free
|
package/README.zh.md
CHANGED
|
@@ -343,7 +343,7 @@ solve <issue-url> [options]
|
|
|
343
343
|
|
|
344
344
|
| 选项 | 简写 | 描述 | 默认值 |
|
|
345
345
|
| ------------------------ | ---- | ------------------------------------------------------- | ------ |
|
|
346
|
-
| `--tool` | | AI 工具(claude、opencode、codex、agent、gemini
|
|
346
|
+
| `--tool` | | AI 工具(claude、opencode、codex、agent、qwen、gemini) | claude |
|
|
347
347
|
| `--verbose` | `-v` | 启用详细日志 | false |
|
|
348
348
|
| `--attach-logs` | | 将日志附加到 PR(⚠️ 可能暴露敏感数据) | false |
|
|
349
349
|
| `--auto-init-repository` | | 自动初始化空仓库(创建 README.md) | false |
|
|
@@ -370,7 +370,7 @@ hive <github-url> [options]
|
|
|
370
370
|
|
|
371
371
|
| 选项 | 简写 | 描述 | 默认值 |
|
|
372
372
|
| ------------------------ | ---- | ------------------------------------------------------- | ------ |
|
|
373
|
-
| `--tool` | | AI 工具(claude、opencode、codex、agent、gemini
|
|
373
|
+
| `--tool` | | AI 工具(claude、opencode、codex、agent、qwen、gemini) | claude |
|
|
374
374
|
| `--concurrency` | `-c` | 并行工作进程数量 | 2 |
|
|
375
375
|
| `--skip-issues-with-prs` | `-s` | 跳过已有 PR 的 Issue | false |
|
|
376
376
|
| `--verbose` | `-v` | 启用详细日志 | false |
|
|
@@ -459,8 +459,8 @@ Aliases:
|
|
|
459
459
|
/codex 等同于 /solve --tool codex
|
|
460
460
|
/opencode 等同于 /solve --tool opencode
|
|
461
461
|
/agent 等同于 /solve --tool agent
|
|
462
|
-
/gemini 等同于 /solve --tool gemini
|
|
463
462
|
/qwen 等同于 /solve --tool qwen
|
|
463
|
+
/gemini 等同于 /solve --tool gemini
|
|
464
464
|
|
|
465
465
|
Tool alias examples:
|
|
466
466
|
/codex https://github.com/owner/repo/issues/123 --model gpt-5.5
|
|
@@ -468,6 +468,7 @@ Tool alias examples:
|
|
|
468
468
|
/agent https://github.com/owner/repo/issues/123 --model nemotron-3-super-free
|
|
469
469
|
/gemini https://github.com/owner/repo/issues/123 --model flash
|
|
470
470
|
/qwen https://github.com/owner/repo/issues/123 --model qwen3-coder-plus
|
|
471
|
+
/gemini https://github.com/owner/repo/issues/123 --model gemini-2.5-flash
|
|
471
472
|
|
|
472
473
|
Free Models (with --tool agent):
|
|
473
474
|
/solve https://github.com/owner/repo/issues/123 --tool agent --model 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.64.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",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"@secretlint/secretlint-rule-preset-recommend": "^11.2.5",
|
|
73
73
|
"@sentry/node": "^10.15.0",
|
|
74
74
|
"@sentry/profiling-node": "^10.15.0",
|
|
75
|
-
"agent-commander": "^0.
|
|
75
|
+
"agent-commander": "^0.6.1",
|
|
76
76
|
"dayjs": "^1.11.19",
|
|
77
77
|
"decimal.js-light": "^2.5.1",
|
|
78
78
|
"lino-arguments": "^0.3.0",
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Experimental agent-commander execution adapter.
|
|
4
|
+
*
|
|
5
|
+
* This module is only used when solve is called with --use-agent-commander.
|
|
6
|
+
* Default tool execution remains in claude.lib.mjs, codex.lib.mjs,
|
|
7
|
+
* opencode.lib.mjs, and agent.lib.mjs.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { resolveCodexReasoningEffort } from './codex.options.lib.mjs';
|
|
11
|
+
import { mapModelForTool } from './models/index.mjs';
|
|
12
|
+
import { buildCodexDisable1mContextConfigArgs, buildCodexSubSessionSizeConfigArgs, parseSubSessionSize } from './sub-session-size.lib.mjs';
|
|
13
|
+
import { detectUsageLimit } from './usage-limit.lib.mjs';
|
|
14
|
+
|
|
15
|
+
export const AGENT_COMMANDER_TOOLS = new Set(['claude', 'codex', 'opencode', 'agent', 'qwen', 'gemini']);
|
|
16
|
+
|
|
17
|
+
const TOOL_EXECUTABLE_CONFIG = {
|
|
18
|
+
claude: { argvKey: 'claudePath', envKey: 'CLAUDE_PATH' },
|
|
19
|
+
codex: { argvKey: 'codexPath', envKey: 'CODEX_PATH' },
|
|
20
|
+
opencode: { argvKey: 'opencodePath', envKey: 'OPENCODE_PATH' },
|
|
21
|
+
agent: { argvKey: 'agentPath', envKey: 'AGENT_PATH' },
|
|
22
|
+
qwen: { argvKey: 'qwenPath', envKey: 'QWEN_PATH' },
|
|
23
|
+
gemini: { argvKey: 'geminiPath', envKey: 'GEMINI_PATH' },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const getConfiguredExecutable = (argv = {}, tool) => {
|
|
27
|
+
const config = TOOL_EXECUTABLE_CONFIG[tool];
|
|
28
|
+
if (!config) return null;
|
|
29
|
+
return argv[config.argvKey] || process.env[config.envKey] || null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const appendExtraArgs = (options, args) => {
|
|
33
|
+
if (!Array.isArray(args) || args.length === 0) return;
|
|
34
|
+
options.extraArgs = [...(options.extraArgs || []), ...args];
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const appendExtraEnv = (options, env) => {
|
|
38
|
+
const entries = Object.entries(env || {}).filter(([, value]) => value !== undefined && value !== null && value !== false);
|
|
39
|
+
if (entries.length === 0) return;
|
|
40
|
+
options.extraEnv = {
|
|
41
|
+
...(options.extraEnv || {}),
|
|
42
|
+
...Object.fromEntries(entries.map(([key, value]) => [key, String(value)])),
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const buildClaudeToolOptions = (argv = {}) => {
|
|
47
|
+
const options = {};
|
|
48
|
+
options.verbose = !!argv.verbose;
|
|
49
|
+
if (argv.fallbackModel) options.fallbackModel = argv.fallbackModel;
|
|
50
|
+
|
|
51
|
+
const extraEnv = {};
|
|
52
|
+
if (argv.thinkingBudget !== undefined) extraEnv.MAX_THINKING_TOKENS = argv.thinkingBudget;
|
|
53
|
+
if (argv.disable1mContext) extraEnv.CLAUDE_CODE_DISABLE_1M_CONTEXT = '1';
|
|
54
|
+
if (argv.showThinkingContent) extraEnv.CLAUDE_CODE_SHOW_THINKING = '1';
|
|
55
|
+
if (argv.planModel) extraEnv.ANTHROPIC_DEFAULT_OPUS_MODEL = argv.planModel;
|
|
56
|
+
appendExtraEnv(options, extraEnv);
|
|
57
|
+
|
|
58
|
+
return options;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const buildCodexToolOptions = (argv = {}) => {
|
|
62
|
+
const options = {};
|
|
63
|
+
const { reasoningEffort } = resolveCodexReasoningEffort(argv);
|
|
64
|
+
appendExtraArgs(options, ['-c', `model_reasoning_effort=${reasoningEffort}`, '-c', 'model_reasoning_summary=auto']);
|
|
65
|
+
|
|
66
|
+
appendExtraArgs(options, buildCodexDisable1mContextConfigArgs(!!argv.disable1mContext));
|
|
67
|
+
try {
|
|
68
|
+
appendExtraArgs(options, buildCodexSubSessionSizeConfigArgs(parseSubSessionSize(argv.subSessionSize)));
|
|
69
|
+
} catch {
|
|
70
|
+
// The embedded Codex path logs parse warnings later. Keep agent-commander
|
|
71
|
+
// command construction permissive so validation can still run.
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return options;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const defaultLog = async (message, options = {}) => {
|
|
78
|
+
if (options.verbose && !global.verboseMode) return;
|
|
79
|
+
if (options.level === 'error') {
|
|
80
|
+
console.error(message);
|
|
81
|
+
} else if (options.level === 'warning' || options.level === 'warn') {
|
|
82
|
+
console.warn(message);
|
|
83
|
+
} else {
|
|
84
|
+
console.log(message);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const getAgentCommander = async () => {
|
|
89
|
+
try {
|
|
90
|
+
return await import('agent-commander');
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw new Error(`agent-commander is not installed or cannot be loaded. Install it with: npm install agent-commander\nOriginal error: ${error.message}`);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
export const isAgentCommanderAvailable = async () => {
|
|
97
|
+
try {
|
|
98
|
+
await getAgentCommander();
|
|
99
|
+
return true;
|
|
100
|
+
} catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export const getAgentCommanderToolName = (argv = {}) => argv.tool || 'claude';
|
|
106
|
+
|
|
107
|
+
const resolveAgentCommanderModel = (tool, model) => (model ? mapModelForTool(tool, model) : model);
|
|
108
|
+
|
|
109
|
+
export const buildAgentCommanderToolOptions = (argv = {}, tool = getAgentCommanderToolName(argv)) => {
|
|
110
|
+
const options = tool === 'claude' ? buildClaudeToolOptions(argv) : tool === 'codex' ? buildCodexToolOptions(argv) : {};
|
|
111
|
+
|
|
112
|
+
const executable = getConfiguredExecutable(argv, tool);
|
|
113
|
+
if (executable) {
|
|
114
|
+
options.executable = executable;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (tool === 'gemini' && argv.verbose) options.debug = true;
|
|
118
|
+
|
|
119
|
+
return options;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const buildAgentCommanderControllerOptions = ({ tool, tempDir, prompt, systemPrompt, argv = {} }) => ({
|
|
123
|
+
tool,
|
|
124
|
+
workingDirectory: tempDir,
|
|
125
|
+
prompt,
|
|
126
|
+
systemPrompt,
|
|
127
|
+
model: resolveAgentCommanderModel(tool, argv.model),
|
|
128
|
+
json: tool !== 'agent',
|
|
129
|
+
resume: argv.resume,
|
|
130
|
+
toolOptions: buildAgentCommanderToolOptions(argv, tool),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
export const validateAgentCommanderConnection = async ({ tool, model, log = defaultLog, agentCommanderModule = null }) => {
|
|
134
|
+
try {
|
|
135
|
+
const module = agentCommanderModule || (await getAgentCommander());
|
|
136
|
+
const { agent, isToolSupported } = module;
|
|
137
|
+
const toolName = tool || 'claude';
|
|
138
|
+
|
|
139
|
+
if (!AGENT_COMMANDER_TOOLS.has(toolName) || !isToolSupported({ toolName })) {
|
|
140
|
+
await log(`[agent-commander] Tool '${toolName}' is not supported`, { level: 'error' });
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const controller = agent({
|
|
145
|
+
tool: toolName,
|
|
146
|
+
workingDirectory: process.cwd(),
|
|
147
|
+
prompt: 'connection check',
|
|
148
|
+
model: resolveAgentCommanderModel(toolName, model),
|
|
149
|
+
json: toolName !== 'agent',
|
|
150
|
+
toolOptions: buildAgentCommanderToolOptions({ model }, toolName),
|
|
151
|
+
});
|
|
152
|
+
await controller.start({ dryRun: true, attached: false });
|
|
153
|
+
await log(`[agent-commander] ${toolName} command construction validated`, { verbose: true });
|
|
154
|
+
return true;
|
|
155
|
+
} catch (error) {
|
|
156
|
+
await log(`[agent-commander] Connection validation failed: ${error.message}`, { level: 'error' });
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const getPromptModule = async tool => {
|
|
162
|
+
if (tool === 'claude') return await import('./claude.prompts.lib.mjs');
|
|
163
|
+
if (tool === 'codex') return await import('./codex.prompts.lib.mjs');
|
|
164
|
+
if (tool === 'opencode') return await import('./opencode.prompts.lib.mjs');
|
|
165
|
+
if (tool === 'agent') return await import('./agent.prompts.lib.mjs');
|
|
166
|
+
if (tool === 'qwen') return await import('./qwen.prompts.lib.mjs');
|
|
167
|
+
if (tool === 'gemini') return await import('./gemini.prompts.lib.mjs');
|
|
168
|
+
throw new Error(`Unsupported tool for agent-commander: ${tool}`);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export const getPlaywrightMcpAvailabilityCheck = async tool => {
|
|
172
|
+
if (tool === 'opencode') return (await import('./opencode.lib.mjs')).checkPlaywrightMcpAvailability;
|
|
173
|
+
if (tool === 'codex') return (await import('./codex.lib.mjs')).checkPlaywrightMcpAvailability;
|
|
174
|
+
if (tool === 'agent') return (await import('./agent.lib.mjs')).checkPlaywrightMcpAvailability;
|
|
175
|
+
if (tool === 'qwen') return (await import('./qwen.lib.mjs')).checkPlaywrightMcpAvailability;
|
|
176
|
+
if (tool === 'gemini') return (await import('./gemini.lib.mjs')).checkPlaywrightMcpAvailability;
|
|
177
|
+
return (await import('./claude.lib.mjs')).checkPlaywrightMcpAvailability;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export const resolvePlaywrightMcpForAgentCommander = async ({ argv, log = defaultLog, tool = getAgentCommanderToolName(argv) }) => {
|
|
181
|
+
if (argv.playwrightMcp === false) return;
|
|
182
|
+
if (!argv.promptPlaywrightMcp) {
|
|
183
|
+
await log('ℹ️ Playwright MCP explicitly disabled via --no-prompt-playwright-mcp', { verbose: true });
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const checkFn = await getPlaywrightMcpAvailabilityCheck(tool);
|
|
188
|
+
const available = await checkFn();
|
|
189
|
+
if (available) {
|
|
190
|
+
await log('🎭 Playwright MCP detected - enabling browser automation hints', { verbose: true });
|
|
191
|
+
} else {
|
|
192
|
+
await log('ℹ️ Playwright MCP not detected - browser automation hints will be disabled', { verbose: true });
|
|
193
|
+
argv.promptPlaywrightMcp = false;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
const getParsedMessages = result => {
|
|
198
|
+
const parsed = result?.output?.parsed;
|
|
199
|
+
if (Array.isArray(parsed)) return parsed;
|
|
200
|
+
if (parsed && typeof parsed === 'object') return [parsed];
|
|
201
|
+
return [];
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const extractResultSummary = (messages, plainOutput) => {
|
|
205
|
+
for (const message of [...messages].reverse()) {
|
|
206
|
+
if (typeof message?.result === 'string' && message.result.trim()) return message.result.trim();
|
|
207
|
+
if (typeof message?.summary === 'string' && message.summary.trim()) return message.summary.trim();
|
|
208
|
+
if (typeof message?.text === 'string' && message.text.trim()) return message.text.trim();
|
|
209
|
+
if (typeof message?.message === 'string' && message.message.trim()) return message.message.trim();
|
|
210
|
+
if (typeof message?.item?.content === 'string' && message.item.content.trim()) return message.item.content.trim();
|
|
211
|
+
if (Array.isArray(message?.item?.content)) {
|
|
212
|
+
const text = message.item.content
|
|
213
|
+
.map(part => part?.text || '')
|
|
214
|
+
.join('')
|
|
215
|
+
.trim();
|
|
216
|
+
if (text) return text;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return plainOutput?.trim() ? plainOutput.trim().slice(-4000) : null;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const hasErrorMessage = messages => messages.some(message => message?.is_error === true || message?.type === 'error' || message?.type === 'step_error' || message?.error);
|
|
224
|
+
|
|
225
|
+
export const summarizeAgentCommanderResult = ({ result, tool }) => {
|
|
226
|
+
const plainOutput = result?.output?.plain || '';
|
|
227
|
+
if (result?.metadata && typeof result.metadata === 'object') {
|
|
228
|
+
const metadata = result.metadata;
|
|
229
|
+
return {
|
|
230
|
+
success: metadata.success === true,
|
|
231
|
+
sessionId: metadata.sessionId || result.sessionId || null,
|
|
232
|
+
limitReached: !!metadata.limitReached,
|
|
233
|
+
limitResetTime: metadata.limitResetTime || null,
|
|
234
|
+
limitTimezone: metadata.limitTimezone || null,
|
|
235
|
+
anthropicTotalCostUSD: metadata.anthropicTotalCostUSD ?? null,
|
|
236
|
+
publicPricingEstimate: metadata.publicPricingEstimate ?? metadata.pricingInfo?.totalCostUSD ?? null,
|
|
237
|
+
pricingInfo: metadata.pricingInfo || null,
|
|
238
|
+
resultSummary: metadata.resultSummary || null,
|
|
239
|
+
resultModelUsage: metadata.resultModelUsage || null,
|
|
240
|
+
streamTokenUsage: metadata.streamTokenUsage || result.usage || null,
|
|
241
|
+
subAgentCalls: metadata.subAgentCalls || null,
|
|
242
|
+
errorDuringExecution: metadata.errorDuringExecution === true || result?.exitCode !== 0,
|
|
243
|
+
result: plainOutput,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const messages = getParsedMessages(result);
|
|
248
|
+
const usageLimit = detectUsageLimit(plainOutput);
|
|
249
|
+
const usage = result?.usage || null;
|
|
250
|
+
const resultMessage = [...messages].reverse().find(message => message?.type === 'result') || null;
|
|
251
|
+
const totalCost = typeof resultMessage?.total_cost_usd === 'number' ? resultMessage.total_cost_usd : null;
|
|
252
|
+
const publicPricingEstimate = tool === 'agent' && typeof usage?.totalCost === 'number' ? usage.totalCost : null;
|
|
253
|
+
|
|
254
|
+
return {
|
|
255
|
+
success: result?.exitCode === 0 && !usageLimit.isUsageLimit && !hasErrorMessage(messages),
|
|
256
|
+
sessionId: result?.sessionId || resultMessage?.session_id || null,
|
|
257
|
+
limitReached: usageLimit.isUsageLimit,
|
|
258
|
+
limitResetTime: usageLimit.resetTime,
|
|
259
|
+
limitTimezone: usageLimit.timezone,
|
|
260
|
+
anthropicTotalCostUSD: tool === 'claude' ? totalCost : null,
|
|
261
|
+
publicPricingEstimate,
|
|
262
|
+
pricingInfo: publicPricingEstimate !== null ? { totalCostUSD: publicPricingEstimate, source: 'agent-commander' } : null,
|
|
263
|
+
resultSummary: extractResultSummary(messages, plainOutput),
|
|
264
|
+
resultModelUsage: null,
|
|
265
|
+
streamTokenUsage: usage,
|
|
266
|
+
subAgentCalls: null,
|
|
267
|
+
errorDuringExecution: result?.exitCode !== 0 || hasErrorMessage(messages),
|
|
268
|
+
result: plainOutput,
|
|
269
|
+
};
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
export const executeWithAgentCommander = async params => {
|
|
273
|
+
const { agentCommanderModule = null, promptModule = null, log = defaultLog, argv, tempDir, workspaceTmpDir, ...promptParams } = params;
|
|
274
|
+
const tool = getAgentCommanderToolName(argv);
|
|
275
|
+
const module = agentCommanderModule || (await getAgentCommander());
|
|
276
|
+
|
|
277
|
+
if (!AGENT_COMMANDER_TOOLS.has(tool) || !module.isToolSupported({ toolName: tool })) {
|
|
278
|
+
throw new Error(`agent-commander does not support tool '${tool}'`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const prompts = promptModule || (await getPromptModule(tool));
|
|
282
|
+
const promptBuilderParams = { ...promptParams, tempDir, workspaceTmpDir, argv };
|
|
283
|
+
const prompt = prompts.buildUserPrompt(promptBuilderParams);
|
|
284
|
+
const systemPrompt = prompts.buildSystemPrompt(promptBuilderParams);
|
|
285
|
+
const controllerOptions = buildAgentCommanderControllerOptions({ tool, tempDir, prompt, systemPrompt, argv });
|
|
286
|
+
|
|
287
|
+
if (argv.verbose) {
|
|
288
|
+
await log('\n[agent-commander] Final prompt structure:', { verbose: true });
|
|
289
|
+
await log(` Tool: ${tool}`, { verbose: true });
|
|
290
|
+
await log(` User prompt characters: ${prompt.length}`, { verbose: true });
|
|
291
|
+
await log(` System prompt characters: ${systemPrompt.length}`, { verbose: true });
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const controller = module.agent(controllerOptions);
|
|
295
|
+
const dryRun = !!(argv.dryRun || argv.onlyPrepareCommand);
|
|
296
|
+
await log(`\n[agent-commander] Starting ${tool} execution${dryRun ? ' (dry-run)' : ''}...`);
|
|
297
|
+
await controller.start({
|
|
298
|
+
dryRun,
|
|
299
|
+
attached: true,
|
|
300
|
+
onOutput: chunk => {
|
|
301
|
+
if (chunk.type === 'stderr') process.stderr.write(chunk.data);
|
|
302
|
+
else process.stdout.write(chunk.data);
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
if (dryRun) {
|
|
307
|
+
return {
|
|
308
|
+
success: true,
|
|
309
|
+
sessionId: null,
|
|
310
|
+
limitReached: false,
|
|
311
|
+
limitResetTime: null,
|
|
312
|
+
limitTimezone: null,
|
|
313
|
+
anthropicTotalCostUSD: null,
|
|
314
|
+
publicPricingEstimate: null,
|
|
315
|
+
pricingInfo: null,
|
|
316
|
+
resultSummary: null,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const result = await controller.stop();
|
|
321
|
+
await log(`[agent-commander] ${tool} exited with code ${result.exitCode}`);
|
|
322
|
+
return summarizeAgentCommanderResult({ result, tool });
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
export const checkForUncommittedChanges = async (tempDir, owner, repo, branchName, $, log = defaultLog, autoCommit = false, autoRestartEnabled = true) => {
|
|
326
|
+
await log('\n🔍 Checking for uncommitted changes...');
|
|
327
|
+
const gitStatusResult = await $({ cwd: tempDir })`git status --porcelain 2>&1`;
|
|
328
|
+
const statusOutput = gitStatusResult.stdout?.toString().trim() || '';
|
|
329
|
+
|
|
330
|
+
if (!statusOutput) {
|
|
331
|
+
await log('✅ No uncommitted changes found');
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
await log('📝 Found uncommitted changes');
|
|
336
|
+
await log('Changes:');
|
|
337
|
+
for (const line of statusOutput.split('\n')) await log(` ${line}`);
|
|
338
|
+
|
|
339
|
+
if (autoCommit) {
|
|
340
|
+
await log('💾 Auto-committing changes (--auto-commit-uncommitted-changes is enabled)...');
|
|
341
|
+
const addResult = await $({ cwd: tempDir })`git add -A`;
|
|
342
|
+
if (addResult.code === 0) {
|
|
343
|
+
const commitResult = await $({ cwd: tempDir })`git commit -m ${'Auto-commit: Changes made through agent-commander during problem-solving session'}`;
|
|
344
|
+
if (commitResult.code === 0) {
|
|
345
|
+
const pushResult = await $({ cwd: tempDir })`git push origin ${branchName} 2>&1`;
|
|
346
|
+
await log(pushResult.code === 0 ? '✅ Changes pushed successfully' : `⚠️ Warning: Could not push changes: ${pushResult.stderr?.toString().trim() || pushResult.stdout?.toString().trim()}`, { level: pushResult.code === 0 ? 'info' : 'warning' });
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (autoRestartEnabled) {
|
|
353
|
+
await log('\n⚠️ IMPORTANT: Uncommitted changes detected!');
|
|
354
|
+
await log(' The agent-commander controlled tool made changes that were not committed.');
|
|
355
|
+
await log('\n🔄 AUTO-RESTART: Restarting the tool to handle uncommitted changes...\n');
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
await log('\n⚠️ Uncommitted changes detected but auto-restart is disabled.');
|
|
360
|
+
return false;
|
|
361
|
+
};
|
|
@@ -294,13 +294,13 @@ Plan sub-agent usage.
|
|
|
294
294
|
|
|
295
295
|
Agent Commander usage (unified subagent delegation).
|
|
296
296
|
- When you need to delegate tasks to subagents, use the agent-commander CLI tool (start-agent) instead of the native Task tool.
|
|
297
|
-
- Agent Commander provides a unified API for different agent types (claude, opencode, codex, agent, qwen) and supports various isolation modes.
|
|
297
|
+
- Agent Commander provides a unified API for different agent types (claude, opencode, codex, agent, qwen, gemini) and supports various isolation modes.
|
|
298
298
|
- To delegate a task, use the Bash tool to run start-agent with appropriate parameters:
|
|
299
299
|
\`\`\`bash
|
|
300
300
|
start-agent --tool claude --working-directory "$(pwd)" --prompt "Your task description here"
|
|
301
301
|
\`\`\`
|
|
302
302
|
- Common start-agent parameters:
|
|
303
|
-
--tool <name>: Agent to use (claude, opencode, codex, agent, qwen)
|
|
303
|
+
--tool <name>: Agent to use (claude, opencode, codex, agent, qwen, gemini)
|
|
304
304
|
--working-directory <path>: Execution directory (use current directory for context)
|
|
305
305
|
--prompt <text>: The task to delegate
|
|
306
306
|
--model <name>: Model to use (${[...new Set(Object.values(primaryModelNames).flat())].slice(0, 5).join(', ')}, etc.)
|
|
@@ -277,13 +277,13 @@ Planning workflow usage.
|
|
|
277
277
|
|
|
278
278
|
Agent Commander usage (unified subagent delegation).
|
|
279
279
|
- When you need to delegate tasks to subagents, use the agent-commander CLI tool (start-agent) instead of relying only on native Codex collaboration.
|
|
280
|
-
- Agent Commander provides a unified API for different agent types (claude, opencode, codex, agent, qwen) and supports various isolation modes.
|
|
280
|
+
- Agent Commander provides a unified API for different agent types (claude, opencode, codex, agent, qwen, gemini) and supports various isolation modes.
|
|
281
281
|
- To delegate a task, use a command like:
|
|
282
282
|
\`\`\`bash
|
|
283
283
|
start-agent --tool codex --working-directory "$(pwd)" --prompt "Your task description here"
|
|
284
284
|
\`\`\`
|
|
285
285
|
- Common start-agent parameters:
|
|
286
|
-
--tool <name>: Agent to use (claude, opencode, codex, agent, qwen)
|
|
286
|
+
--tool <name>: Agent to use (claude, opencode, codex, agent, qwen, gemini)
|
|
287
287
|
--working-directory <path>: Execution directory (use the current directory for context)
|
|
288
288
|
--prompt <text>: The task to delegate
|
|
289
289
|
--model <name>: Model to use
|
package/src/hive.config.lib.mjs
CHANGED
|
@@ -47,7 +47,7 @@ const HIVE_CUSTOM_SOLVE_OPTIONS = {
|
|
|
47
47
|
tool: {
|
|
48
48
|
type: 'string',
|
|
49
49
|
description: 'AI tool to use for solving issues',
|
|
50
|
-
choices: ['claude', 'opencode', 'codex', 'agent', '
|
|
50
|
+
choices: ['claude', 'opencode', 'codex', 'agent', 'qwen', 'gemini'],
|
|
51
51
|
default: 'claude',
|
|
52
52
|
},
|
|
53
53
|
};
|
package/src/models/index.mjs
CHANGED
|
@@ -129,19 +129,6 @@ export const codexModels = {
|
|
|
129
129
|
'gpt-4o': 'gpt-4o',
|
|
130
130
|
};
|
|
131
131
|
|
|
132
|
-
// Gemini models (Google Gemini CLI)
|
|
133
|
-
// Keep aliases aligned with the Gemini CLI model aliases documented in
|
|
134
|
-
// docs/cli/cli-reference.md: auto, pro, flash, and flash-lite.
|
|
135
|
-
export const geminiModels = {
|
|
136
|
-
auto: 'auto',
|
|
137
|
-
pro: 'gemini-2.5-pro',
|
|
138
|
-
flash: 'gemini-2.5-flash',
|
|
139
|
-
'flash-lite': 'gemini-2.5-flash-lite',
|
|
140
|
-
'gemini-2.5-pro': 'gemini-2.5-pro',
|
|
141
|
-
'gemini-2.5-flash': 'gemini-2.5-flash',
|
|
142
|
-
'gemini-2.5-flash-lite': 'gemini-2.5-flash-lite',
|
|
143
|
-
};
|
|
144
|
-
|
|
145
132
|
// Qwen Code models
|
|
146
133
|
export const qwenModels = {
|
|
147
134
|
qwen: 'qwen3-coder-plus',
|
|
@@ -154,14 +141,38 @@ export const qwenModels = {
|
|
|
154
141
|
'qwen3.6-coder-plus': 'qwen3.6-coder-plus',
|
|
155
142
|
};
|
|
156
143
|
|
|
144
|
+
// Gemini models (Google Gemini CLI)
|
|
145
|
+
// Keep aliases aligned with the Gemini CLI model aliases documented in
|
|
146
|
+
// docs/cli/cli-reference.md: auto, pro, flash, and flash-lite.
|
|
147
|
+
export const geminiModels = {
|
|
148
|
+
auto: 'auto',
|
|
149
|
+
gemini: 'gemini-2.5-flash',
|
|
150
|
+
flash: 'gemini-2.5-flash',
|
|
151
|
+
'2.5-flash': 'gemini-2.5-flash',
|
|
152
|
+
pro: 'gemini-2.5-pro',
|
|
153
|
+
'2.5-pro': 'gemini-2.5-pro',
|
|
154
|
+
lite: 'gemini-2.5-flash-lite',
|
|
155
|
+
'2.5-lite': 'gemini-2.5-flash-lite',
|
|
156
|
+
'flash-lite': 'gemini-2.5-flash-lite',
|
|
157
|
+
'3-flash': 'gemini-3-flash-preview',
|
|
158
|
+
'3-pro': 'gemini-3-pro-preview',
|
|
159
|
+
'gemini-flash': 'gemini-2.5-flash',
|
|
160
|
+
'gemini-pro': 'gemini-2.5-pro',
|
|
161
|
+
'gemini-2.5-flash': 'gemini-2.5-flash',
|
|
162
|
+
'gemini-2.5-pro': 'gemini-2.5-pro',
|
|
163
|
+
'gemini-2.5-flash-lite': 'gemini-2.5-flash-lite',
|
|
164
|
+
'gemini-3-flash-preview': 'gemini-3-flash-preview',
|
|
165
|
+
'gemini-3-pro-preview': 'gemini-3-pro-preview',
|
|
166
|
+
};
|
|
167
|
+
|
|
157
168
|
// Default model for each tool (Issue #1473: centralized to avoid scattered hardcoded defaults)
|
|
158
169
|
export const defaultModels = {
|
|
159
170
|
claude: 'sonnet',
|
|
160
171
|
agent: 'nemotron-3-super-free', // Issue #1563: changed from qwen3.6-plus-free (free promotion ended) per agent PR #243
|
|
161
172
|
opencode: 'grok-code-fast-1',
|
|
162
173
|
codex: 'gpt-5.5',
|
|
163
|
-
gemini: 'flash',
|
|
164
174
|
qwen: 'qwen3-coder-plus',
|
|
175
|
+
gemini: 'flash',
|
|
165
176
|
};
|
|
166
177
|
|
|
167
178
|
// Models that support 1M token context window via [1m] suffix (Issue #1221, Issue #1238, Issue #1329)
|
|
@@ -241,10 +252,6 @@ export const CODEX_MODELS = {
|
|
|
241
252
|
'gpt-4o': 'gpt-4o',
|
|
242
253
|
};
|
|
243
254
|
|
|
244
|
-
export const GEMINI_MODELS = {
|
|
245
|
-
...geminiModels,
|
|
246
|
-
};
|
|
247
|
-
|
|
248
255
|
export const QWEN_MODELS = {
|
|
249
256
|
...qwenModels,
|
|
250
257
|
'qwen3-coder': 'qwen3-coder',
|
|
@@ -254,6 +261,15 @@ export const QWEN_MODELS = {
|
|
|
254
261
|
'qwen3.6-coder-plus': 'qwen3.6-coder-plus',
|
|
255
262
|
};
|
|
256
263
|
|
|
264
|
+
export const GEMINI_MODELS = {
|
|
265
|
+
...geminiModels,
|
|
266
|
+
'gemini-2.5-flash': 'gemini-2.5-flash',
|
|
267
|
+
'gemini-2.5-pro': 'gemini-2.5-pro',
|
|
268
|
+
'gemini-2.5-flash-lite': 'gemini-2.5-flash-lite',
|
|
269
|
+
'gemini-3-flash-preview': 'gemini-3-flash-preview',
|
|
270
|
+
'gemini-3-pro-preview': 'gemini-3-pro-preview',
|
|
271
|
+
};
|
|
272
|
+
|
|
257
273
|
export const AGENT_MODELS = {
|
|
258
274
|
...agentModels,
|
|
259
275
|
'opencode/grok-code': 'opencode/grok-code',
|
|
@@ -275,7 +291,7 @@ export const AGENT_MODELS = {
|
|
|
275
291
|
|
|
276
292
|
/**
|
|
277
293
|
* Get the model map object for a given tool
|
|
278
|
-
* @param {string} tool - The tool name (claude, agent, opencode, codex,
|
|
294
|
+
* @param {string} tool - The tool name (claude, agent, opencode, codex, qwen, gemini)
|
|
279
295
|
* @returns {Object} The model mapping for the tool
|
|
280
296
|
*/
|
|
281
297
|
export const getModelMapForTool = tool => {
|
|
@@ -299,7 +315,7 @@ export const getModelMapForTool = tool => {
|
|
|
299
315
|
|
|
300
316
|
/**
|
|
301
317
|
* Get the default model for a given tool
|
|
302
|
-
* @param {string} tool - The tool name (claude, agent, opencode, codex,
|
|
318
|
+
* @param {string} tool - The tool name (claude, agent, opencode, codex, qwen, gemini)
|
|
303
319
|
* @returns {string} The default model alias for the tool
|
|
304
320
|
*/
|
|
305
321
|
export const getDefaultModelForTool = tool => {
|
|
@@ -352,7 +368,7 @@ export const resolveRuntimeDefaultModel = async (tool, options = {}) => {
|
|
|
352
368
|
|
|
353
369
|
/**
|
|
354
370
|
* Map model name to full model ID for a specific tool
|
|
355
|
-
* @param {string} tool - The tool name (claude, agent, opencode, codex,
|
|
371
|
+
* @param {string} tool - The tool name (claude, agent, opencode, codex, qwen, gemini)
|
|
356
372
|
* @param {string} model - The model name or alias
|
|
357
373
|
* @returns {string} The full model ID
|
|
358
374
|
*/
|
|
@@ -377,7 +393,7 @@ export const mapModelForTool = (tool, model) => {
|
|
|
377
393
|
|
|
378
394
|
/**
|
|
379
395
|
* Validate if a model is compatible with a tool
|
|
380
|
-
* @param {string} tool - The tool name (claude, agent, opencode, codex,
|
|
396
|
+
* @param {string} tool - The tool name (claude, agent, opencode, codex, qwen, gemini)
|
|
381
397
|
* @param {string} model - The model name or alias
|
|
382
398
|
* @returns {boolean} True if the model is compatible with the tool
|
|
383
399
|
*/
|
|
@@ -432,9 +448,9 @@ export const primaryModelNames = {
|
|
|
432
448
|
claude: ['opus', 'sonnet', 'haiku', 'opusplan'],
|
|
433
449
|
opencode: ['grok', 'gpt4o'],
|
|
434
450
|
codex: ['gpt-5.5', 'gpt-5.4', 'gpt-5.4-mini', 'gpt-5.3-codex', 'gpt-5.3-codex-spark'],
|
|
435
|
-
gemini: ['flash', 'pro', 'flash-lite', 'auto'],
|
|
436
451
|
agent: ['nemotron-3-super-free', 'minimax-m2.5-free', 'big-pickle', 'gpt-5-nano', 'glm-5-free', 'deepseek-r1-free'],
|
|
437
452
|
qwen: ['qwen3-coder-plus', 'qwen3-coder', 'qwen3-coder-flash'],
|
|
453
|
+
gemini: ['flash', 'pro', 'flash-lite', 'auto'],
|
|
438
454
|
};
|
|
439
455
|
|
|
440
456
|
/**
|
|
@@ -474,7 +490,7 @@ export const validateToolModelCompatibility = (tool, model) => {
|
|
|
474
490
|
|
|
475
491
|
/**
|
|
476
492
|
* Get the model map for a given tool (validation-extended version with full ID entries)
|
|
477
|
-
* @param {string} tool - The tool name ('claude', 'opencode', 'codex', 'agent', '
|
|
493
|
+
* @param {string} tool - The tool name ('claude', 'opencode', 'codex', 'agent', 'qwen', 'gemini')
|
|
478
494
|
* @returns {Object} The model mapping for the tool
|
|
479
495
|
*/
|
|
480
496
|
const getValidationModelMapForTool = tool => {
|
|
@@ -497,7 +513,7 @@ const getValidationModelMapForTool = tool => {
|
|
|
497
513
|
|
|
498
514
|
/**
|
|
499
515
|
* Get the list of available model names for a tool (for display in help/error messages)
|
|
500
|
-
* @param {string} tool - The tool name ('claude', 'opencode', 'codex', 'agent', '
|
|
516
|
+
* @param {string} tool - The tool name ('claude', 'opencode', 'codex', 'agent', 'qwen', 'gemini')
|
|
501
517
|
* @returns {string[]} Array of available model short names
|
|
502
518
|
*/
|
|
503
519
|
export const getAvailableModelNames = tool => {
|
|
@@ -636,7 +652,7 @@ export const supports1mContext = (model, tool = 'claude') => {
|
|
|
636
652
|
* Validate a model name against the available models for a tool
|
|
637
653
|
* Supports [1m] suffix for 1 million token context (Issue #1221)
|
|
638
654
|
* @param {string} model - The model name to validate (e.g., "opus", "opus[1m]", "claude-opus-4-6[1m]")
|
|
639
|
-
* @param {string} tool - The tool name ('claude', 'opencode', 'codex', 'agent', '
|
|
655
|
+
* @param {string} tool - The tool name ('claude', 'opencode', 'codex', 'agent', 'qwen', 'gemini')
|
|
640
656
|
* @returns {{ valid: boolean, message?: string, suggestions?: string[], mappedModel?: string, has1mSuffix?: boolean }}
|
|
641
657
|
*/
|
|
642
658
|
export const validateModelName = (model, tool = 'claude') => {
|
|
@@ -709,7 +725,7 @@ export const validateModelName = (model, tool = 'claude') => {
|
|
|
709
725
|
* Validate model name and exit with error if invalid
|
|
710
726
|
* This is the main entry point for model validation in solve.mjs, hive.mjs, etc.
|
|
711
727
|
* @param {string} model - The model name to validate
|
|
712
|
-
* @param {string} tool - The tool name ('claude', 'opencode', 'codex', 'agent', 'qwen')
|
|
728
|
+
* @param {string} tool - The tool name ('claude', 'opencode', 'codex', 'agent', 'qwen', 'gemini')
|
|
713
729
|
* @param {Function} exitFn - Function to call for exiting (default: process.exit)
|
|
714
730
|
* @returns {Promise<boolean>} True if valid, exits process if invalid
|
|
715
731
|
*/
|
|
@@ -744,7 +760,7 @@ export const formatAvailableModelsForHelp = (tool = 'claude') => {
|
|
|
744
760
|
|
|
745
761
|
/**
|
|
746
762
|
* Map tool identifier to user-friendly display name.
|
|
747
|
-
* @param {string|null} tool - The tool identifier (claude, codex, opencode, agent,
|
|
763
|
+
* @param {string|null} tool - The tool identifier (claude, codex, opencode, agent, qwen, gemini)
|
|
748
764
|
* @returns {string} User-friendly display name
|
|
749
765
|
*/
|
|
750
766
|
export const getToolDisplayName = tool => {
|
|
@@ -883,7 +899,7 @@ const doesRequestedMatchActual = (requestedModel, actualModelId, tool) => {
|
|
|
883
899
|
*
|
|
884
900
|
* @param {Object} options - Model info options
|
|
885
901
|
* @param {string|null} options.requestedModel - The model requested via --model flag
|
|
886
|
-
* @param {string|null} options.tool - The tool used (claude, agent, opencode, codex, qwen)
|
|
902
|
+
* @param {string|null} options.tool - The tool used (claude, agent, opencode, codex, qwen, gemini)
|
|
887
903
|
* @param {Object|null} options.pricingInfo - Pricing info from tool result
|
|
888
904
|
* @param {Object|null} options.modelInfo - Pre-fetched model metadata from models.dev
|
|
889
905
|
* @param {Array<{modelId: string, modelInfo: Object|null}>|null} options.modelsUsed - Actual models used from CLI JSON output
|
|
@@ -994,7 +1010,7 @@ export const resolveDefaultFallbackModel = (tool, model) => {
|
|
|
994
1010
|
*
|
|
995
1011
|
* @param {Object} options
|
|
996
1012
|
* @param {string|null} options.requestedModel - The --model flag value
|
|
997
|
-
* @param {string|null} options.tool - The tool used (claude, agent, opencode, codex, qwen)
|
|
1013
|
+
* @param {string|null} options.tool - The tool used (claude, agent, opencode, codex, qwen, gemini)
|
|
998
1014
|
* @param {Object|null} options.pricingInfo - Pricing info from tool result
|
|
999
1015
|
* @param {Array<string>|null} options.actualModelIds - Actual model IDs from CLI JSON output
|
|
1000
1016
|
* @returns {Promise<string>} Formatted markdown model info section
|
package/src/qwen.prompts.lib.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
|
13
13
|
* @returns {string} The formatted user prompt
|
|
14
14
|
*/
|
|
15
15
|
export const buildUserPrompt = params => {
|
|
16
|
-
const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv } = params;
|
|
16
|
+
const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv, tool = 'qwen' } = params;
|
|
17
17
|
|
|
18
18
|
const promptLines = [];
|
|
19
19
|
|
|
@@ -50,7 +50,7 @@ export const buildUserPrompt = params => {
|
|
|
50
50
|
promptLines.push('');
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
const thinkingPromptInstruction = getThinkingPromptInstruction({ tool
|
|
53
|
+
const thinkingPromptInstruction = getThinkingPromptInstruction({ tool, argv });
|
|
54
54
|
if (thinkingPromptInstruction) {
|
|
55
55
|
promptLines.push(thinkingPromptInstruction);
|
|
56
56
|
}
|
package/src/solve.config.lib.mjs
CHANGED
|
@@ -327,7 +327,7 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
327
327
|
tool: {
|
|
328
328
|
type: 'string',
|
|
329
329
|
description: 'AI tool to use for solving issues',
|
|
330
|
-
choices: ['claude', 'opencode', 'codex', 'agent', '
|
|
330
|
+
choices: ['claude', 'opencode', 'codex', 'agent', 'qwen', 'gemini'],
|
|
331
331
|
default: 'claude',
|
|
332
332
|
},
|
|
333
333
|
plan: {
|
|
@@ -352,7 +352,7 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
352
352
|
},
|
|
353
353
|
'enable-workspaces': {
|
|
354
354
|
type: 'boolean',
|
|
355
|
-
description: 'Use separate workspace directory structure with repository/ and tmp/ folders. Works with all tools (claude, opencode, codex, agent,
|
|
355
|
+
description: 'Use separate workspace directory structure with repository/ and tmp/ folders. Works with all tools (claude, opencode, codex, agent, qwen, gemini). Experimental feature.',
|
|
356
356
|
default: false,
|
|
357
357
|
},
|
|
358
358
|
'interactive-mode': {
|
|
@@ -428,7 +428,7 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
428
428
|
},
|
|
429
429
|
'prompt-playwright-mcp': {
|
|
430
430
|
type: 'boolean',
|
|
431
|
-
description: 'Enable Playwright MCP browser automation hints in system prompt (enabled by default, only takes effect if Playwright MCP is installed). Use --no-prompt-playwright-mcp to disable. Supported for --tool claude, --tool codex, --tool opencode, --tool agent, --tool
|
|
431
|
+
description: 'Enable Playwright MCP browser automation hints in system prompt (enabled by default, only takes effect if Playwright MCP is installed). Use --no-prompt-playwright-mcp to disable. Supported for --tool claude, --tool codex, --tool opencode, --tool agent, --tool qwen, and --tool gemini.',
|
|
432
432
|
default: true,
|
|
433
433
|
},
|
|
434
434
|
'prompt-check-sibling-pull-requests': {
|
|
@@ -448,7 +448,7 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
448
448
|
},
|
|
449
449
|
'playwright-mcp': {
|
|
450
450
|
type: 'boolean',
|
|
451
|
-
description: 'Enable Playwright MCP server connection for this session (enabled by default). Use --no-playwright-mcp to physically disable the Playwright MCP server without affecting the global MCP registration. When disabled, also disables --prompt-playwright-mcp and --playwright-mcp-auto-cleanup. Supported for --tool claude, --tool codex, --tool opencode, --tool agent,
|
|
451
|
+
description: 'Enable Playwright MCP server connection for this session (enabled by default). Use --no-playwright-mcp to physically disable the Playwright MCP server without affecting the global MCP registration. When disabled, also disables --prompt-playwright-mcp and --playwright-mcp-auto-cleanup. Supported for --tool claude, --tool codex, --tool opencode, --tool agent, --tool qwen, and --tool gemini.',
|
|
452
452
|
default: true,
|
|
453
453
|
},
|
|
454
454
|
'playwright-mcp-auto-cleanup': {
|
|
@@ -468,9 +468,15 @@ export const SOLVE_OPTION_DEFINITIONS = {
|
|
|
468
468
|
},
|
|
469
469
|
'prompt-subagents-via-agent-commander': {
|
|
470
470
|
type: 'boolean',
|
|
471
|
-
description: 'Guide AI to use agent-commander CLI (start-agent) instead of native tool-specific delegation for subagent work. Allows using any supported agent type (claude, opencode, codex, agent, qwen) with a unified API. Supported for --tool claude and --tool codex and requires agent-commander to be installed.',
|
|
471
|
+
description: 'Guide AI to use agent-commander CLI (start-agent) instead of native tool-specific delegation for subagent work. Allows using any supported agent type (claude, opencode, codex, agent, qwen, gemini) with a unified API. Supported for --tool claude and --tool codex and requires agent-commander to be installed.',
|
|
472
472
|
default: false,
|
|
473
473
|
},
|
|
474
|
+
'use-agent-commander': {
|
|
475
|
+
type: 'boolean',
|
|
476
|
+
description: '[EXPERIMENTAL] Execute the selected AI tool through agent-commander instead of the embedded hive-mind tool adapter. Disabled by default.',
|
|
477
|
+
default: false,
|
|
478
|
+
hidden: true,
|
|
479
|
+
},
|
|
474
480
|
'auto-init-repository': {
|
|
475
481
|
type: 'boolean',
|
|
476
482
|
description: 'Automatically initialize empty repositories by creating a simple README.md file. Only works when you have write access to the repository. This allows branch creation and pull request workflows to proceed on repositories that have no commits.',
|
package/src/solve.mjs
CHANGED
|
@@ -89,8 +89,13 @@ setupStdioLogInterceptor(); // Issue #1549: capture ALL terminal output in log f
|
|
|
89
89
|
|
|
90
90
|
// Early logs go to cwd; custom log dir takes effect after argv is parsed
|
|
91
91
|
// Conditionally import tool-specific functions after argv is parsed
|
|
92
|
+
// If --use-agent-commander is enabled, use agent-commander's checkForUncommittedChanges
|
|
92
93
|
let checkForUncommittedChanges;
|
|
93
|
-
|
|
94
|
+
let agentCommanderLib = null;
|
|
95
|
+
if (argv.useAgentCommander) {
|
|
96
|
+
agentCommanderLib = await import('./agent-commander.lib.mjs');
|
|
97
|
+
checkForUncommittedChanges = agentCommanderLib.checkForUncommittedChanges;
|
|
98
|
+
} else if (argv.tool === 'opencode') {
|
|
94
99
|
const opencodeLib = await import('./opencode.lib.mjs');
|
|
95
100
|
checkForUncommittedChanges = opencodeLib.checkForUncommittedChanges;
|
|
96
101
|
} else if (argv.tool === 'gemini') {
|
|
@@ -667,103 +672,26 @@ try {
|
|
|
667
672
|
|
|
668
673
|
// Execute tool command with all prompts and settings
|
|
669
674
|
let toolResult;
|
|
670
|
-
if (argv.tool === 'opencode') {
|
|
671
|
-
const opencodeLib = await import('./opencode.lib.mjs');
|
|
672
|
-
const { executeOpenCode, checkPlaywrightMcpAvailability: checkOpenCodePlaywrightMcp } = opencodeLib;
|
|
673
|
-
const opencodePath = process.env.OPENCODE_PATH || 'opencode';
|
|
674
|
-
await resolvePlaywrightMcp(checkOpenCodePlaywrightMcp);
|
|
675
675
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
tempDir,
|
|
683
|
-
workspaceTmpDir,
|
|
684
|
-
isContinueMode,
|
|
685
|
-
mergeStateStatus,
|
|
686
|
-
forkedRepo,
|
|
687
|
-
feedbackLines,
|
|
688
|
-
forkActionsUrl,
|
|
689
|
-
owner,
|
|
690
|
-
repo,
|
|
691
|
-
argv,
|
|
692
|
-
log,
|
|
693
|
-
setLogFile,
|
|
694
|
-
getLogFile,
|
|
695
|
-
formatAligned,
|
|
696
|
-
getResourceSnapshot,
|
|
697
|
-
opencodePath,
|
|
698
|
-
$,
|
|
699
|
-
});
|
|
700
|
-
} else if (argv.tool === 'codex') {
|
|
701
|
-
const codexLib = await import('./codex.lib.mjs');
|
|
702
|
-
const { executeCodex, checkPlaywrightMcpAvailability } = codexLib;
|
|
703
|
-
const codexPath = process.env.CODEX_PATH || 'codex';
|
|
704
|
-
await resolvePlaywrightMcp(checkPlaywrightMcpAvailability);
|
|
676
|
+
// If --use-agent-commander is enabled, use agent-commander for all tools
|
|
677
|
+
if (argv.useAgentCommander) {
|
|
678
|
+
// Ensure agent-commander is available
|
|
679
|
+
if (!agentCommanderLib) {
|
|
680
|
+
agentCommanderLib = await import('./agent-commander.lib.mjs');
|
|
681
|
+
}
|
|
705
682
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
workspaceTmpDir,
|
|
714
|
-
isContinueMode,
|
|
715
|
-
mergeStateStatus,
|
|
716
|
-
forkedRepo,
|
|
717
|
-
feedbackLines,
|
|
718
|
-
forkActionsUrl,
|
|
719
|
-
owner,
|
|
720
|
-
repo,
|
|
721
|
-
argv,
|
|
722
|
-
log,
|
|
723
|
-
setLogFile,
|
|
724
|
-
getLogFile,
|
|
725
|
-
formatAligned,
|
|
726
|
-
getResourceSnapshot,
|
|
727
|
-
codexPath,
|
|
728
|
-
$,
|
|
729
|
-
});
|
|
730
|
-
} else if (argv.tool === 'agent') {
|
|
731
|
-
const agentLib = await import('./agent.lib.mjs');
|
|
732
|
-
const { executeAgent, checkPlaywrightMcpAvailability: checkAgentPlaywrightMcp } = agentLib;
|
|
733
|
-
const agentPath = process.env.AGENT_PATH || 'agent';
|
|
734
|
-
await resolvePlaywrightMcp(checkAgentPlaywrightMcp);
|
|
683
|
+
const isAvailable = await agentCommanderLib.isAgentCommanderAvailable();
|
|
684
|
+
if (!isAvailable) {
|
|
685
|
+
await log('\n[agent-commander] agent-commander is not installed.', { level: 'error' });
|
|
686
|
+
await log(' Install it with: npm install agent-commander', { level: 'error' });
|
|
687
|
+
await log(' Or remove the --use-agent-commander flag to use embedded tool logic.', { level: 'error' });
|
|
688
|
+
await safeExit(1, 'agent-commander not available');
|
|
689
|
+
}
|
|
735
690
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
issueNumber,
|
|
739
|
-
prNumber,
|
|
740
|
-
prUrl,
|
|
741
|
-
branchName,
|
|
742
|
-
tempDir,
|
|
743
|
-
workspaceTmpDir,
|
|
744
|
-
isContinueMode,
|
|
745
|
-
mergeStateStatus,
|
|
746
|
-
forkedRepo,
|
|
747
|
-
feedbackLines,
|
|
748
|
-
forkActionsUrl,
|
|
749
|
-
owner,
|
|
750
|
-
repo,
|
|
751
|
-
argv,
|
|
752
|
-
log,
|
|
753
|
-
setLogFile,
|
|
754
|
-
getLogFile,
|
|
755
|
-
formatAligned,
|
|
756
|
-
getResourceSnapshot,
|
|
757
|
-
agentPath,
|
|
758
|
-
$,
|
|
759
|
-
});
|
|
760
|
-
} else if (argv.tool === 'gemini') {
|
|
761
|
-
const geminiLib = await import('./gemini.lib.mjs');
|
|
762
|
-
const { executeGemini, checkPlaywrightMcpAvailability: checkGeminiPlaywrightMcp } = geminiLib;
|
|
763
|
-
const geminiPath = process.env.GEMINI_PATH || 'gemini';
|
|
764
|
-
await resolvePlaywrightMcp(checkGeminiPlaywrightMcp);
|
|
691
|
+
await log(`\n[agent-commander] Using agent-commander for ${argv.tool || 'claude'} execution`);
|
|
692
|
+
await agentCommanderLib.resolvePlaywrightMcpForAgentCommander({ argv, log, tool: argv.tool || 'claude' });
|
|
765
693
|
|
|
766
|
-
toolResult = await
|
|
694
|
+
toolResult = await agentCommanderLib.executeWithAgentCommander({
|
|
767
695
|
issueUrl,
|
|
768
696
|
issueNumber,
|
|
769
697
|
prNumber,
|
|
@@ -784,16 +712,20 @@ try {
|
|
|
784
712
|
getLogFile,
|
|
785
713
|
formatAligned,
|
|
786
714
|
getResourceSnapshot,
|
|
787
|
-
geminiPath,
|
|
788
715
|
$,
|
|
789
716
|
});
|
|
790
|
-
} else if (
|
|
791
|
-
const
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
717
|
+
} else if (['opencode', 'codex', 'agent', 'gemini', 'qwen'].includes(argv.tool)) {
|
|
718
|
+
const toolDispatch = {
|
|
719
|
+
opencode: { lib: './opencode.lib.mjs', execFn: 'executeOpenCode', envVar: 'OPENCODE_PATH', defaultBin: 'opencode', pathKey: 'opencodePath' },
|
|
720
|
+
codex: { lib: './codex.lib.mjs', execFn: 'executeCodex', envVar: 'CODEX_PATH', defaultBin: 'codex', pathKey: 'codexPath' },
|
|
721
|
+
agent: { lib: './agent.lib.mjs', execFn: 'executeAgent', envVar: 'AGENT_PATH', defaultBin: 'agent', pathKey: 'agentPath' },
|
|
722
|
+
gemini: { lib: './gemini.lib.mjs', execFn: 'executeGemini', envVar: 'GEMINI_PATH', defaultBin: 'gemini', pathKey: 'geminiPath' },
|
|
723
|
+
qwen: { lib: './qwen.lib.mjs', execFn: 'executeQwen', envVar: 'QWEN_PATH', defaultBin: 'qwen', pathKey: 'qwenPath' },
|
|
724
|
+
}[argv.tool];
|
|
725
|
+
const toolLib = await import(toolDispatch.lib);
|
|
726
|
+
await resolvePlaywrightMcp(toolLib.checkPlaywrightMcpAvailability);
|
|
727
|
+
|
|
728
|
+
toolResult = await toolLib[toolDispatch.execFn]({
|
|
797
729
|
issueUrl,
|
|
798
730
|
issueNumber,
|
|
799
731
|
prNumber,
|
|
@@ -814,7 +746,7 @@ try {
|
|
|
814
746
|
getLogFile,
|
|
815
747
|
formatAligned,
|
|
816
748
|
getResourceSnapshot,
|
|
817
|
-
|
|
749
|
+
[toolDispatch.pathKey]: process.env[toolDispatch.envVar] || toolDispatch.defaultBin,
|
|
818
750
|
$,
|
|
819
751
|
});
|
|
820
752
|
} else {
|
|
@@ -183,7 +183,34 @@ export const executeToolIteration = async params => {
|
|
|
183
183
|
await cascadePlaywrightMcpDisable(argv, log);
|
|
184
184
|
|
|
185
185
|
let toolResult;
|
|
186
|
-
if (argv.
|
|
186
|
+
if (argv.useAgentCommander) {
|
|
187
|
+
const agentCommanderLib = await import('./agent-commander.lib.mjs');
|
|
188
|
+
await agentCommanderLib.resolvePlaywrightMcpForAgentCommander({ argv, log, tool: argv.tool || 'claude' });
|
|
189
|
+
|
|
190
|
+
toolResult = await agentCommanderLib.executeWithAgentCommander({
|
|
191
|
+
issueUrl,
|
|
192
|
+
issueNumber,
|
|
193
|
+
prNumber,
|
|
194
|
+
prUrl: `https://github.com/${owner}/${repo}/pull/${prNumber}`,
|
|
195
|
+
branchName,
|
|
196
|
+
tempDir,
|
|
197
|
+
workspaceTmpDir: params.workspaceTmpDir,
|
|
198
|
+
isContinueMode: true,
|
|
199
|
+
mergeStateStatus,
|
|
200
|
+
forkedRepo: argv.fork,
|
|
201
|
+
feedbackLines,
|
|
202
|
+
forkActionsUrl: null,
|
|
203
|
+
owner,
|
|
204
|
+
repo,
|
|
205
|
+
argv,
|
|
206
|
+
log,
|
|
207
|
+
formatAligned,
|
|
208
|
+
getResourceSnapshot,
|
|
209
|
+
setLogFile: () => {},
|
|
210
|
+
getLogFile: () => '',
|
|
211
|
+
$,
|
|
212
|
+
});
|
|
213
|
+
} else if (argv.tool === 'opencode') {
|
|
187
214
|
// Use OpenCode
|
|
188
215
|
const opencodeExecLib = await import('./opencode.lib.mjs');
|
|
189
216
|
const { executeOpenCode, checkPlaywrightMcpAvailability } = opencodeExecLib;
|
|
@@ -295,7 +295,18 @@ export const performSystemChecks = async (minDiskSpace = 2048, skipToolConnectio
|
|
|
295
295
|
// Skip tool connection validation if in dry-run mode or explicitly requested
|
|
296
296
|
if (!skipToolConnection) {
|
|
297
297
|
let isToolConnected = false;
|
|
298
|
-
if (argv.
|
|
298
|
+
if (argv.useAgentCommander) {
|
|
299
|
+
const agentCommanderLib = await import('./agent-commander.lib.mjs');
|
|
300
|
+
isToolConnected = await agentCommanderLib.validateAgentCommanderConnection({
|
|
301
|
+
tool: argv.tool || 'claude',
|
|
302
|
+
model,
|
|
303
|
+
log,
|
|
304
|
+
});
|
|
305
|
+
if (!isToolConnected) {
|
|
306
|
+
await log('❌ Cannot proceed without agent-commander tool connection', { level: 'error' });
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
} else if (argv.tool === 'opencode') {
|
|
299
310
|
// Validate OpenCode connection
|
|
300
311
|
const opencodeLib = await import('./opencode.lib.mjs');
|
|
301
312
|
isToolConnected = await opencodeLib.validateOpenCodeConnection(model);
|
package/src/task.mjs
CHANGED
|
@@ -33,7 +33,7 @@ if (earlyArgs.length === 0 || earlyArgs.includes('--help') || earlyArgs.includes
|
|
|
33
33
|
console.log(' --only-decompose Only run decomposition mode');
|
|
34
34
|
console.log(' --split Split a GitHub issue into smaller issues');
|
|
35
35
|
console.log(' --split-count Number of issues to split into [default: 2]');
|
|
36
|
-
console.log(' --tool AI tool for agent-commander read-only mode (claude, codex, opencode, agent,
|
|
36
|
+
console.log(' --tool AI tool for agent-commander read-only mode (claude, codex, opencode, agent, qwen, gemini) [default: claude]');
|
|
37
37
|
console.log(' --model, -m Model to use');
|
|
38
38
|
console.log(' --isolation agent-commander isolation mode [default: screen]');
|
|
39
39
|
console.log(' --dry-run Print split output without creating GitHub issues');
|
|
@@ -13,8 +13,8 @@ export const TOOL_SOLVE_COMMAND_ALIASES = Object.freeze({
|
|
|
13
13
|
codex: 'codex',
|
|
14
14
|
opencode: 'opencode',
|
|
15
15
|
agent: 'agent',
|
|
16
|
-
gemini: 'gemini',
|
|
17
16
|
qwen: 'qwen',
|
|
17
|
+
gemini: 'gemini',
|
|
18
18
|
});
|
|
19
19
|
|
|
20
20
|
export const SOLVE_COMMAND_NAMES = Object.freeze(['solve', 'do', 'continue', ...Object.keys(TOOL_SOLVE_COMMAND_ALIASES)]);
|
|
@@ -76,21 +76,21 @@ export async function getRunningCodexProcesses(verbose = false) {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
|
-
* Count running
|
|
79
|
+
* Count running qwen processes.
|
|
80
80
|
* @param {boolean} verbose - Whether to log verbose output
|
|
81
81
|
* @returns {Promise<{count: number, processes: string[]}>}
|
|
82
82
|
*/
|
|
83
|
-
export async function
|
|
84
|
-
return getRunningProcesses('
|
|
83
|
+
export async function getRunningQwenProcesses(verbose = false) {
|
|
84
|
+
return getRunningProcesses('qwen', verbose);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
-
* Count running
|
|
88
|
+
* Count running gemini processes.
|
|
89
89
|
* @param {boolean} verbose - Whether to log verbose output
|
|
90
90
|
* @returns {Promise<{count: number, processes: string[]}>}
|
|
91
91
|
*/
|
|
92
|
-
export async function
|
|
93
|
-
return getRunningProcesses('
|
|
92
|
+
export async function getRunningGeminiProcesses(verbose = false) {
|
|
93
|
+
return getRunningProcesses('gemini', verbose);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
|
@@ -163,10 +163,10 @@ export function formatWaitingReason(metric, currentValue, threshold) {
|
|
|
163
163
|
return 'Claude process is already running';
|
|
164
164
|
case 'codex_running':
|
|
165
165
|
return 'Codex process is already running';
|
|
166
|
-
case 'gemini_running':
|
|
167
|
-
return 'Gemini process is already running';
|
|
168
166
|
case 'qwen_running':
|
|
169
167
|
return 'Qwen Code process is already running';
|
|
168
|
+
case 'gemini_running':
|
|
169
|
+
return 'Gemini CLI process is already running';
|
|
170
170
|
default:
|
|
171
171
|
return `${metric} threshold exceeded`;
|
|
172
172
|
}
|
|
@@ -149,8 +149,8 @@ export class SolveQueue {
|
|
|
149
149
|
claude: [],
|
|
150
150
|
agent: [],
|
|
151
151
|
codex: [],
|
|
152
|
-
gemini: [],
|
|
153
152
|
qwen: [],
|
|
153
|
+
gemini: [],
|
|
154
154
|
};
|
|
155
155
|
this.processing = new Map();
|
|
156
156
|
this.completed = [];
|
|
@@ -162,8 +162,8 @@ export class SolveQueue {
|
|
|
162
162
|
claude: null,
|
|
163
163
|
agent: null,
|
|
164
164
|
codex: null,
|
|
165
|
-
gemini: null,
|
|
166
165
|
qwen: null,
|
|
166
|
+
gemini: null,
|
|
167
167
|
};
|
|
168
168
|
// Legacy: keep for compatibility with existing code that uses lastStartTime
|
|
169
169
|
this.lastStartTime = null;
|
|
@@ -566,12 +566,12 @@ export class SolveQueue {
|
|
|
566
566
|
const claudeProcessCount = externalProcessing.byTool.claude || 0;
|
|
567
567
|
const codexProcessCount = externalProcessing.byTool.codex || 0;
|
|
568
568
|
const agentProcessCount = externalProcessing.byTool.agent || 0;
|
|
569
|
-
const geminiProcessCount = externalProcessing.byTool.gemini || 0;
|
|
570
569
|
const qwenProcessCount = externalProcessing.byTool.qwen || 0;
|
|
570
|
+
const geminiProcessCount = externalProcessing.byTool.gemini || 0;
|
|
571
571
|
const hasRunningClaude = claudeProcessCount > 0;
|
|
572
572
|
const hasRunningCodex = codexProcessCount > 0;
|
|
573
|
-
const hasRunningGemini = geminiProcessCount > 0;
|
|
574
573
|
const hasRunningQwen = qwenProcessCount > 0;
|
|
574
|
+
const hasRunningGemini = geminiProcessCount > 0;
|
|
575
575
|
|
|
576
576
|
// Calculate total processing count for system resources (all tools)
|
|
577
577
|
// System resources (RAM, CPU, disk) apply to all tools
|
|
@@ -583,8 +583,8 @@ export class SolveQueue {
|
|
|
583
583
|
// See: https://github.com/link-assistant/hive-mind/issues/1159
|
|
584
584
|
const claudeProcessingCount = this.getProcessingCountByTool('claude');
|
|
585
585
|
const codexProcessingCount = this.getProcessingCountByTool('codex');
|
|
586
|
-
const geminiProcessingCount = this.getProcessingCountByTool('gemini');
|
|
587
586
|
const qwenProcessingCount = this.getProcessingCountByTool('qwen');
|
|
587
|
+
const geminiProcessingCount = this.getProcessingCountByTool('gemini');
|
|
588
588
|
|
|
589
589
|
// Track claude_running as a metric (but don't add to reasons yet)
|
|
590
590
|
if (hasRunningClaude) {
|
|
@@ -593,12 +593,12 @@ export class SolveQueue {
|
|
|
593
593
|
if (hasRunningCodex) {
|
|
594
594
|
this.recordThrottle('codex_running');
|
|
595
595
|
}
|
|
596
|
-
if (hasRunningGemini) {
|
|
597
|
-
this.recordThrottle('gemini_running');
|
|
598
|
-
}
|
|
599
596
|
if (hasRunningQwen) {
|
|
600
597
|
this.recordThrottle('qwen_running');
|
|
601
598
|
}
|
|
599
|
+
if (hasRunningGemini) {
|
|
600
|
+
this.recordThrottle('gemini_running');
|
|
601
|
+
}
|
|
602
602
|
|
|
603
603
|
// Check system resources with strategy support
|
|
604
604
|
// System resources apply to ALL tools, not just Claude
|
|
@@ -646,12 +646,12 @@ export class SolveQueue {
|
|
|
646
646
|
if (tool === 'codex' && hasRunningCodex && reasons.length > 0) {
|
|
647
647
|
reasons.push(formatWaitingReason('codex_running', codexProcessCount, 0) + ` (${codexProcessCount} processes)`);
|
|
648
648
|
}
|
|
649
|
-
if (tool === 'gemini' && hasRunningGemini && reasons.length > 0) {
|
|
650
|
-
reasons.push(formatWaitingReason('gemini_running', geminiProcessCount, 0) + ` (${geminiProcessCount} processes)`);
|
|
651
|
-
}
|
|
652
649
|
if (tool === 'qwen' && hasRunningQwen && reasons.length > 0) {
|
|
653
650
|
reasons.push(formatWaitingReason('qwen_running', qwenProcessCount, 0) + ` (${qwenProcessCount} processes)`);
|
|
654
651
|
}
|
|
652
|
+
if (tool === 'gemini' && hasRunningGemini && reasons.length > 0) {
|
|
653
|
+
reasons.push(formatWaitingReason('gemini_running', geminiProcessCount, 0) + ` (${geminiProcessCount} processes)`);
|
|
654
|
+
}
|
|
655
655
|
|
|
656
656
|
const canStart = reasons.length === 0 && !rejected;
|
|
657
657
|
|
|
@@ -673,14 +673,14 @@ export class SolveQueue {
|
|
|
673
673
|
claudeProcesses: claudeProcessCount,
|
|
674
674
|
codexProcesses: codexProcessCount,
|
|
675
675
|
agentProcesses: agentProcessCount,
|
|
676
|
-
geminiProcesses: geminiProcessCount,
|
|
677
676
|
qwenProcesses: qwenProcessCount,
|
|
677
|
+
geminiProcesses: geminiProcessCount,
|
|
678
678
|
isolatedProcesses: externalProcessing.isolatedTotal,
|
|
679
679
|
totalProcessing,
|
|
680
680
|
claudeProcessingCount,
|
|
681
681
|
codexProcessingCount,
|
|
682
|
-
geminiProcessingCount,
|
|
683
682
|
qwenProcessingCount,
|
|
683
|
+
geminiProcessingCount,
|
|
684
684
|
};
|
|
685
685
|
}
|
|
686
686
|
|
|
@@ -1442,8 +1442,8 @@ export default {
|
|
|
1442
1442
|
getRunningClaudeProcesses,
|
|
1443
1443
|
getRunningAgentProcesses,
|
|
1444
1444
|
getRunningCodexProcesses,
|
|
1445
|
-
getRunningGeminiProcesses,
|
|
1446
1445
|
getRunningQwenProcesses,
|
|
1446
|
+
getRunningGeminiProcesses,
|
|
1447
1447
|
getRunningIsolatedSessions,
|
|
1448
1448
|
createQueueExecuteCallback,
|
|
1449
1449
|
formatDuration,
|
|
@@ -11,6 +11,9 @@ export const validateToolConnection = async ({ tool = 'claude', model, verbose =
|
|
|
11
11
|
if (tool === 'qwen') {
|
|
12
12
|
return (await import('./qwen.lib.mjs')).validateQwenConnection(model);
|
|
13
13
|
}
|
|
14
|
+
if (tool === 'gemini') {
|
|
15
|
+
return (await import('./gemini.lib.mjs')).validateGeminiConnection(model);
|
|
16
|
+
}
|
|
14
17
|
const validateClaude = validateClaudeConnection || (await import('./claude.lib.mjs')).validateClaudeConnection;
|
|
15
18
|
return validateClaude(model);
|
|
16
19
|
};
|