@link-assistant/hive-mind 1.61.0 → 1.62.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 -2
- package/README.md +5 -2
- package/README.ru.md +4 -2
- package/README.zh.md +17 -15
- package/package.json +1 -1
- package/src/claude.prompts.lib.mjs +2 -2
- package/src/codex.prompts.lib.mjs +2 -2
- package/src/config.lib.mjs +1 -0
- package/src/hive.config.lib.mjs +1 -1
- package/src/hive.mjs +7 -7
- package/src/models/index.mjs +46 -11
- package/src/qwen.lib.mjs +589 -0
- package/src/qwen.prompts.lib.mjs +253 -0
- package/src/solve.config.lib.mjs +5 -5
- package/src/solve.mjs +34 -0
- package/src/solve.restart-shared.lib.mjs +44 -2
- package/src/solve.validation.lib.mjs +8 -0
- package/src/task.mjs +1 -1
- package/src/telegram-bot.mjs +3 -3
- package/src/telegram-solve-command.lib.mjs +1 -0
- package/src/telegram-solve-queue.helpers.lib.mjs +11 -0
- package/src/telegram-solve-queue.lib.mjs +16 -3
- package/src/tool-connection-validation.lib.mjs +16 -0
- package/src/version-info.lib.mjs +1 -1
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Qwen prompts module
|
|
3
|
+
* Handles building prompts for Qwen Code commands
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { getArchitectureCareSubPrompt } from './architecture-care.prompts.lib.mjs';
|
|
7
|
+
import { getExperimentsExamplesSubPrompt } from './experiments-examples.prompts.lib.mjs';
|
|
8
|
+
import { getThinkingPromptInstruction } from './thinking-prompt.lib.mjs';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Build the user prompt for Qwen Code
|
|
12
|
+
* @param {Object} params - Parameters for building the user prompt
|
|
13
|
+
* @returns {string} The formatted user prompt
|
|
14
|
+
*/
|
|
15
|
+
export const buildUserPrompt = params => {
|
|
16
|
+
const { issueUrl, issueNumber, prNumber, prUrl, branchName, tempDir, workspaceTmpDir, isContinueMode, forkedRepo, feedbackLines, forkActionsUrl, owner, repo, argv } = params;
|
|
17
|
+
|
|
18
|
+
const promptLines = [];
|
|
19
|
+
|
|
20
|
+
if (isContinueMode) {
|
|
21
|
+
promptLines.push(`Issue to solve: ${issueNumber ? `https://github.com/${owner}/${repo}/issues/${issueNumber}` : `Issue linked to PR #${prNumber}`}`);
|
|
22
|
+
} else {
|
|
23
|
+
promptLines.push(`Issue to solve: ${issueUrl}`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
promptLines.push(`Your prepared branch: ${branchName}`);
|
|
27
|
+
promptLines.push(`Your prepared working directory: ${tempDir}`);
|
|
28
|
+
|
|
29
|
+
if (workspaceTmpDir) {
|
|
30
|
+
promptLines.push(`Your prepared tmp directory for logs and downloads: ${workspaceTmpDir}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (prUrl) {
|
|
34
|
+
promptLines.push(`Your prepared Pull Request: ${prUrl}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (argv && argv.fork && forkedRepo) {
|
|
38
|
+
promptLines.push(`Your forked repository: ${forkedRepo}`);
|
|
39
|
+
promptLines.push(`Original repository (upstream): ${owner}/${repo}`);
|
|
40
|
+
|
|
41
|
+
if (branchName && forkActionsUrl) {
|
|
42
|
+
promptLines.push(`GitHub Actions on your fork: ${forkActionsUrl}`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
promptLines.push('');
|
|
47
|
+
|
|
48
|
+
if (isContinueMode && feedbackLines && feedbackLines.length > 0) {
|
|
49
|
+
feedbackLines.forEach(line => promptLines.push(line));
|
|
50
|
+
promptLines.push('');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const thinkingPromptInstruction = getThinkingPromptInstruction({ tool: 'qwen', argv });
|
|
54
|
+
if (thinkingPromptInstruction) {
|
|
55
|
+
promptLines.push(thinkingPromptInstruction);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
promptLines.push(isContinueMode ? 'Continue.' : 'Proceed.');
|
|
59
|
+
|
|
60
|
+
return promptLines.join('\n') + '\n';
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Build the system prompt for Qwen Code
|
|
65
|
+
* @param {Object} params - Parameters for building the prompt
|
|
66
|
+
* @returns {string} The formatted system prompt
|
|
67
|
+
*/
|
|
68
|
+
export const buildSystemPrompt = params => {
|
|
69
|
+
const { owner, repo, issueNumber, prNumber, branchName, workspaceTmpDir, argv, modelSupportsVision, forkedRepo } = params;
|
|
70
|
+
|
|
71
|
+
const screenshotRepoPath = argv?.fork && forkedRepo ? forkedRepo : `${owner}/${repo}`;
|
|
72
|
+
|
|
73
|
+
let workspaceInstructions = '';
|
|
74
|
+
if (workspaceTmpDir) {
|
|
75
|
+
workspaceInstructions = `
|
|
76
|
+
Workspace tmp directory.
|
|
77
|
+
- Use ${workspaceTmpDir} for all temporary files, logs, and downloads.
|
|
78
|
+
- When saving command output to files, save to ${workspaceTmpDir}/command-output.log.
|
|
79
|
+
- When downloading CI logs, save to ${workspaceTmpDir}/ci-logs/.
|
|
80
|
+
- When saving diffs for review, save to ${workspaceTmpDir}/diffs/.
|
|
81
|
+
- When creating debug files, save to ${workspaceTmpDir}/debug/.
|
|
82
|
+
|
|
83
|
+
`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let ciExamples = '';
|
|
87
|
+
if (workspaceTmpDir) {
|
|
88
|
+
ciExamples = `
|
|
89
|
+
CI investigation with workspace tmp directory.
|
|
90
|
+
- When downloading CI run logs:
|
|
91
|
+
gh run view RUN_ID --repo ${owner}/${repo} --log > ${workspaceTmpDir}/ci-logs/run-RUN_ID.log
|
|
92
|
+
- When downloading failed job logs:
|
|
93
|
+
gh run view RUN_ID --repo ${owner}/${repo} --log-failed > ${workspaceTmpDir}/ci-logs/run-RUN_ID-failed.log
|
|
94
|
+
- When listing CI runs with details:
|
|
95
|
+
gh run list --repo ${owner}/${repo} --branch ${branchName} --limit 5 --json databaseId,conclusion,createdAt,headSha > ${workspaceTmpDir}/ci-logs/recent-runs.json
|
|
96
|
+
- When saving PR diff for review:
|
|
97
|
+
gh pr diff ${prNumber} --repo ${owner}/${repo} > ${workspaceTmpDir}/diffs/pr-${prNumber}.diff
|
|
98
|
+
- When saving command output with stderr:
|
|
99
|
+
npm test 2>&1 | tee ${workspaceTmpDir}/test-output.log
|
|
100
|
+
- When investigating issue details:
|
|
101
|
+
gh issue view ${issueNumber} --repo ${owner}/${repo} --json body,comments > ${workspaceTmpDir}/issue-${issueNumber}.json
|
|
102
|
+
|
|
103
|
+
`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return `You are an AI issue solver using Qwen Code.
|
|
107
|
+
|
|
108
|
+
General guidelines.
|
|
109
|
+
- When you execute commands and the output becomes large, save the logs to files for easier review.
|
|
110
|
+
- When running commands, avoid setting a timeout yourself. Let them run as long as needed.
|
|
111
|
+
- When running sudo commands, especially package installations, run them in the background to avoid timeout issues.
|
|
112
|
+
- When CI is failing, download the logs locally and investigate them carefully.
|
|
113
|
+
- When a code or log file has more than 1500 lines, read it in chunks of 1500 lines.
|
|
114
|
+
- When facing a complex problem, do as much tracing as possible and turn on all verbose modes.
|
|
115
|
+
${getExperimentsExamplesSubPrompt(argv)}
|
|
116
|
+
- When you face something extremely hard, use divide and conquer.
|
|
117
|
+
${workspaceInstructions}
|
|
118
|
+
Initial research.
|
|
119
|
+
- When you start, create a detailed plan for yourself and follow your todo list step by step. Add as many relevant points from these guidelines to the todo list as practical so you can track the work clearly.
|
|
120
|
+
- When you read the issue, read all details and comments thoroughly.
|
|
121
|
+
- When you see screenshots or images in issue descriptions, pull request descriptions, comments, or discussions, download the image to a local file first, then use the Read tool to view and analyze it. Before reading downloaded images with the Read tool, verify that the file is a valid image rather than HTML by using a CLI tool such as the 'file' command. When the file command shows "HTML", "text", or "ASCII text", the download failed, so do not call Read on that file. When images are from GitHub issues or PRs, such as URLs containing "github.com/user-attachments", use: curl -L -H "Authorization: token $(gh auth token)" -o <filename> "<url>"
|
|
122
|
+
- When you need issue details, use gh issue view https://github.com/${owner}/${repo}/issues/${issueNumber}.
|
|
123
|
+
- When you need related code, use gh search code --owner ${owner} [keywords].
|
|
124
|
+
- When you need repo context, read files in your working directory.${
|
|
125
|
+
argv?.promptCheckSiblingPullRequests !== false
|
|
126
|
+
? `
|
|
127
|
+
- When you study related work, study the most recent related pull requests.`
|
|
128
|
+
: ''
|
|
129
|
+
}
|
|
130
|
+
- When the issue is not defined clearly enough, write a comment with clarifying questions.
|
|
131
|
+
- When accessing GitHub Gists, use gh gist view command instead of direct URL fetching.
|
|
132
|
+
- When you are fixing a bug, find the actual root cause first and run as many experiments as needed.
|
|
133
|
+
- When you are fixing a bug and the code does not have enough tracing or logs, add them and keep them in the code with the default state switched off.
|
|
134
|
+
- When you need comments on a pull request, note that GitHub has three different comment types with different API endpoints:
|
|
135
|
+
1. PR review comments (inline code comments): gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate
|
|
136
|
+
2. PR conversation comments (general discussion): gh api repos/${owner}/${repo}/issues/${prNumber}/comments --paginate
|
|
137
|
+
3. PR reviews (approve/request changes): gh api repos/${owner}/${repo}/pulls/${prNumber}/reviews --paginate
|
|
138
|
+
Note: The command "gh pr view --json comments" only returns conversation comments and misses review comments.
|
|
139
|
+
- When you need the latest comments on the issue, use gh api repos/${owner}/${repo}/issues/${issueNumber}/comments --paginate.
|
|
140
|
+
|
|
141
|
+
Solution development and testing.
|
|
142
|
+
- When issue is solvable, first create a test that reproduces the problem, then implement the fix.
|
|
143
|
+
- When implementing features, search for similar existing implementations in the codebase and use them as examples instead of implementing everything from scratch.
|
|
144
|
+
- When coding, commit each atomic step that is useful on its own to the pull request branch so interrupted work remains preserved in the pull request.
|
|
145
|
+
- When you test:
|
|
146
|
+
start from testing of small functions using separate scripts;
|
|
147
|
+
write unit tests with mocks for easy and quick start.
|
|
148
|
+
- When you test integrations, use existing framework.
|
|
149
|
+
- When you test solution draft, include automated checks in pr.
|
|
150
|
+
- When you write or modify tests, consider setting reasonable timeouts at test, suite, and CI job levels so failures surface quickly instead of hanging.
|
|
151
|
+
- When you see repeated test timeout patterns in CI, investigate the root cause rather than increasing timeouts.
|
|
152
|
+
- When the issue is unclear, write a comment on the issue with questions.
|
|
153
|
+
- When you encounter any problems that you are unable to solve yourself, write a comment to the pull request asking for help.
|
|
154
|
+
- When you need human help, use gh pr comment ${prNumber} --body "your message" to comment on existing PR.
|
|
155
|
+
|
|
156
|
+
Reproducible testing.
|
|
157
|
+
- When fixing a bug, create a test that reproduces the problem before implementing the fix. When you cannot reproduce the problem, you cannot verify the fix.
|
|
158
|
+
- When encountering logic bugs, write an automated test that fails due to the bug, then implement the fix to make it pass.
|
|
159
|
+
- When encountering UI bugs, capture a screenshot showing the problem state, then create a visual regression test or manual verification screenshot after the fix.
|
|
160
|
+
- When creating tests, prefer minimum reproducible examples, meaning the simplest test case that demonstrates the issue.
|
|
161
|
+
- When submitting a fix, include in the PR description: (1) how to reproduce the issue, (2) the automated test that verifies the fix, (3) before/after screenshots for UI issues.
|
|
162
|
+
- When a bug fix does not have a reproducing test, treat the fix as incomplete because regressions can occur later without notice.
|
|
163
|
+
|
|
164
|
+
Preparing pull request.
|
|
165
|
+
- When you code, follow contributing guidelines.
|
|
166
|
+
- When you commit, write clear message.
|
|
167
|
+
- When you need examples of style, use gh pr list --repo ${owner}/${repo} --state merged --search [keywords].
|
|
168
|
+
- When you open pr, describe solution draft and include tests.
|
|
169
|
+
- When there is a package with version and GitHub Actions workflows for automatic release, update the version in your pull request to prepare for next release.
|
|
170
|
+
- When you update existing pr ${prNumber}, use gh pr edit to modify title and description.
|
|
171
|
+
- When you finalize the pull request:
|
|
172
|
+
check that the pull request title and description are updated (the PR may start with a [WIP] prefix and a placeholder description that should be replaced with the actual title and description of the changes),
|
|
173
|
+
follow style from merged prs for code, title, and description,
|
|
174
|
+
check that no uncommitted changes corresponding to the original requirements are left behind,
|
|
175
|
+
check that the default branch is merged into the pull request branch,
|
|
176
|
+
check that all CI checks are passing if they exist before you finish,
|
|
177
|
+
double-check that all changes in the pull request address the original requirements of the issue,
|
|
178
|
+
check for newly introduced bugs in the pull request by carefully reading gh pr diff,
|
|
179
|
+
check that no previously existing features were removed without an explicit request in the issue description, issue comments, or pull request comments.
|
|
180
|
+
- When you finish implementation, use gh pr ready ${prNumber}.
|
|
181
|
+
|
|
182
|
+
Workflow and collaboration.
|
|
183
|
+
- When you check branch, verify with git branch --show-current.
|
|
184
|
+
- When you push, push only to branch ${branchName}.
|
|
185
|
+
- When you finish, create a pull request from branch ${branchName}.
|
|
186
|
+
- When pr ${prNumber} already exists for this branch, update it instead of creating new one.
|
|
187
|
+
- When you organize workflow, use pull requests instead of direct merges to default branch (main or master).
|
|
188
|
+
- When you manage commits, preserve commit history for later analysis.
|
|
189
|
+
- When you contribute, keep repository history forward-moving with regular commits, pushes, and reverts if needed.
|
|
190
|
+
- When you face conflict that you cannot resolve yourself, ask for help.
|
|
191
|
+
- When you collaborate, respect branch protections by working only on ${branchName}.
|
|
192
|
+
- When you mention a result, include the pull request URL or comment URL.
|
|
193
|
+
- When you need to create pr, remember pr ${prNumber} already exists for this branch.
|
|
194
|
+
|
|
195
|
+
Self review.
|
|
196
|
+
- When you check your solution draft, run all tests locally.
|
|
197
|
+
- When you compare with repo style, use gh pr diff [number].
|
|
198
|
+
- When you finalize, confirm code, tests, and description are consistent.${
|
|
199
|
+
argv && argv.promptEnsureAllRequirementsAreMet
|
|
200
|
+
? `
|
|
201
|
+
- When no explicit feedback or requirements are provided, ensure all changes are correct, consistent, validated, tested, logged, and aligned with all discussed requirements by checking the issue description and all comments on the issue and pull request. Check that all CI or CD checks are passing.`
|
|
202
|
+
: ''
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
GitHub CLI command patterns.
|
|
206
|
+
- When fetching lists from GitHub API, use the --paginate flag to ensure all results are returned (GitHub returns max 30 per page by default).
|
|
207
|
+
- When listing PR review comments (inline code comments), use gh api repos/OWNER/REPO/pulls/NUMBER/comments --paginate.
|
|
208
|
+
- When listing PR conversation comments, use gh api repos/OWNER/REPO/issues/NUMBER/comments --paginate.
|
|
209
|
+
- When listing PR reviews, use gh api repos/OWNER/REPO/pulls/NUMBER/reviews --paginate.
|
|
210
|
+
- When listing issue comments, use gh api repos/OWNER/REPO/issues/NUMBER/comments --paginate.
|
|
211
|
+
- When adding PR comment, use gh pr comment NUMBER --body "text" --repo OWNER/REPO.
|
|
212
|
+
- When adding issue comment, use gh issue comment NUMBER --body "text" --repo OWNER/REPO.
|
|
213
|
+
- When viewing PR details, use gh pr view NUMBER --repo OWNER/REPO.
|
|
214
|
+
- When filtering with jq, use gh api repos/${owner}/${repo}/pulls/${prNumber}/comments --paginate --jq 'reverse | .[0:5]'.${
|
|
215
|
+
argv && argv.promptPlaywrightMcp
|
|
216
|
+
? `
|
|
217
|
+
|
|
218
|
+
Playwright MCP usage (browser automation via MCP tools).
|
|
219
|
+
- When you develop frontend web applications (HTML, CSS, JavaScript, React, Vue, Angular, etc.), use Playwright MCP tools to test the UI in a real browser.
|
|
220
|
+
- When WebFetch tool fails to retrieve expected content (e.g., returns empty content, JavaScript-rendered pages, or login-protected pages), use Playwright MCP tools (browser_navigate, browser_snapshot) as a fallback for web browsing.
|
|
221
|
+
- When WebSearch tool fails or returns insufficient results, use Playwright MCP tools (browser_navigate, browser_snapshot) as a fallback for internet search.
|
|
222
|
+
- When you need to interact with dynamic web pages that require JavaScript execution, use Playwright MCP tools.
|
|
223
|
+
- When you need to visually verify how a web page looks or take screenshots, use browser_take_screenshot from Playwright MCP.
|
|
224
|
+
- When you need to fill forms, click buttons, or perform user interactions on web pages, use Playwright MCP tools (browser_click, browser_type, browser_fill_form).
|
|
225
|
+
- When you need to test responsive design or different viewport sizes, use browser_resize from Playwright MCP.
|
|
226
|
+
- When you finish using the browser, close it with browser_close to free resources.
|
|
227
|
+
- When reproducing UI bugs, use browser_take_screenshot to capture the problem state before implementing any fix.
|
|
228
|
+
- When fixing UI bugs, take before/after screenshots to provide visual evidence of the fix for human verification.
|
|
229
|
+
- When creating UI tests, save baseline screenshots to the repository for visual regression testing.
|
|
230
|
+
- When verifying UI fixes, compare screenshots to ensure the fix does not introduce unintended visual changes.`
|
|
231
|
+
: ''
|
|
232
|
+
}${
|
|
233
|
+
modelSupportsVision
|
|
234
|
+
? `
|
|
235
|
+
|
|
236
|
+
Visual UI work and screenshots.
|
|
237
|
+
- When you work on visual UI changes (frontend, CSS, HTML, design), include a render or screenshot of the final result in the pull request description.
|
|
238
|
+
- When you need to show visual results, take a screenshot and save it to the repository (e.g., in a docs/screenshots/ or assets/ folder).
|
|
239
|
+
- When you save screenshots to the repository, use permanent links in the pull request description markdown (e.g., https://github.com/${screenshotRepoPath}/blob/${branchName}/docs/screenshots/result.png?raw=true).
|
|
240
|
+
- When uploading images, commit them to the branch first, then reference them using the GitHub blob URL format with ?raw=true suffix (works for both public and private repositories).
|
|
241
|
+
- When the visual result is important for review, mention it explicitly in the pull request description with the embedded image.
|
|
242
|
+
- When fixing UI bugs, capture both the "before" (problem) and "after" (fixed) screenshots as evidence for human verification of the fix.
|
|
243
|
+
- When reporting UI bugs, include a screenshot of the problem state to enable visual verification of the fix.
|
|
244
|
+
- When the fix is visual, include side-by-side or sequential comparison of before/after states in the PR description.
|
|
245
|
+
- When possible, create automated visual regression tests to prevent the UI bug from recurring.`
|
|
246
|
+
: ''
|
|
247
|
+
}${ciExamples}${getArchitectureCareSubPrompt(argv)}`;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
export default {
|
|
251
|
+
buildUserPrompt,
|
|
252
|
+
buildSystemPrompt,
|
|
253
|
+
};
|
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'],
|
|
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). Experimental feature.',
|
|
355
|
+
description: 'Use separate workspace directory structure with repository/ and tmp/ folders. Works with all tools (claude, opencode, codex, agent, qwen). 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, and --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, and --tool qwen.',
|
|
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, and --tool
|
|
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, and --tool qwen.',
|
|
452
452
|
default: true,
|
|
453
453
|
},
|
|
454
454
|
'playwright-mcp-auto-cleanup': {
|
|
@@ -468,7 +468,7 @@ 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) 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) 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
474
|
'auto-init-repository': {
|
package/src/solve.mjs
CHANGED
|
@@ -99,6 +99,9 @@ if (argv.tool === 'opencode') {
|
|
|
99
99
|
} else if (argv.tool === 'agent') {
|
|
100
100
|
const agentLib = await import('./agent.lib.mjs');
|
|
101
101
|
checkForUncommittedChanges = agentLib.checkForUncommittedChanges;
|
|
102
|
+
} else if (argv.tool === 'qwen') {
|
|
103
|
+
const qwenLib = await import('./qwen.lib.mjs');
|
|
104
|
+
checkForUncommittedChanges = qwenLib.checkForUncommittedChanges;
|
|
102
105
|
} else {
|
|
103
106
|
checkForUncommittedChanges = claudeLib.checkForUncommittedChanges;
|
|
104
107
|
}
|
|
@@ -751,6 +754,36 @@ try {
|
|
|
751
754
|
agentPath,
|
|
752
755
|
$,
|
|
753
756
|
});
|
|
757
|
+
} else if (argv.tool === 'qwen') {
|
|
758
|
+
const qwenLib = await import('./qwen.lib.mjs');
|
|
759
|
+
const { executeQwen, checkPlaywrightMcpAvailability: checkQwenPlaywrightMcp } = qwenLib;
|
|
760
|
+
const qwenPath = process.env.QWEN_PATH || 'qwen';
|
|
761
|
+
await resolvePlaywrightMcp(checkQwenPlaywrightMcp);
|
|
762
|
+
|
|
763
|
+
toolResult = await executeQwen({
|
|
764
|
+
issueUrl,
|
|
765
|
+
issueNumber,
|
|
766
|
+
prNumber,
|
|
767
|
+
prUrl,
|
|
768
|
+
branchName,
|
|
769
|
+
tempDir,
|
|
770
|
+
workspaceTmpDir,
|
|
771
|
+
isContinueMode,
|
|
772
|
+
mergeStateStatus,
|
|
773
|
+
forkedRepo,
|
|
774
|
+
feedbackLines,
|
|
775
|
+
forkActionsUrl,
|
|
776
|
+
owner,
|
|
777
|
+
repo,
|
|
778
|
+
argv,
|
|
779
|
+
log,
|
|
780
|
+
setLogFile,
|
|
781
|
+
getLogFile,
|
|
782
|
+
formatAligned,
|
|
783
|
+
getResourceSnapshot,
|
|
784
|
+
qwenPath,
|
|
785
|
+
$,
|
|
786
|
+
});
|
|
754
787
|
} else {
|
|
755
788
|
// Default to Claude
|
|
756
789
|
if (argv.tool === 'claude' || !argv.tool) {
|
|
@@ -1150,6 +1183,7 @@ try {
|
|
|
1150
1183
|
prNumber,
|
|
1151
1184
|
branchName,
|
|
1152
1185
|
tempDir,
|
|
1186
|
+
workspaceTmpDir,
|
|
1153
1187
|
mergeStateStatus,
|
|
1154
1188
|
feedbackLines: hintLines,
|
|
1155
1189
|
argv: {
|
|
@@ -167,13 +167,13 @@ export const getUncommittedChangesDetails = async tempDir => {
|
|
|
167
167
|
};
|
|
168
168
|
|
|
169
169
|
/**
|
|
170
|
-
* Execute the AI tool (Claude, OpenCode, Codex, Agent) for a restart iteration
|
|
170
|
+
* Execute the AI tool (Claude, OpenCode, Codex, Agent, Qwen) for a restart iteration
|
|
171
171
|
* This is the shared tool execution logic used by both watch mode and auto-restart-until-mergeable mode
|
|
172
172
|
* @param {Object} params - Execution parameters
|
|
173
173
|
* @returns {Promise<Object>} - Tool execution result
|
|
174
174
|
*/
|
|
175
175
|
export const executeToolIteration = async params => {
|
|
176
|
-
const { issueUrl, owner, repo, issueNumber, prNumber, branchName, tempDir, mergeStateStatus, feedbackLines, argv } = params;
|
|
176
|
+
const { issueUrl, owner, repo, issueNumber, prNumber, branchName, tempDir, workspaceTmpDir, mergeStateStatus, feedbackLines, argv } = params;
|
|
177
177
|
|
|
178
178
|
// Import necessary modules for tool execution
|
|
179
179
|
const memoryCheck = await import('./memory-check.mjs');
|
|
@@ -301,6 +301,48 @@ export const executeToolIteration = async params => {
|
|
|
301
301
|
agentPath,
|
|
302
302
|
$,
|
|
303
303
|
});
|
|
304
|
+
} else if (argv.tool === 'qwen') {
|
|
305
|
+
// Use Qwen Code
|
|
306
|
+
const qwenExecLib = await import('./qwen.lib.mjs');
|
|
307
|
+
const { executeQwen, checkPlaywrightMcpAvailability } = qwenExecLib;
|
|
308
|
+
const qwenPath = argv.qwenPath || 'qwen';
|
|
309
|
+
|
|
310
|
+
if (argv.promptPlaywrightMcp) {
|
|
311
|
+
const playwrightMcpAvailable = await checkPlaywrightMcpAvailability();
|
|
312
|
+
if (playwrightMcpAvailable) {
|
|
313
|
+
await log('🎭 Playwright MCP detected - enabling browser automation hints', { verbose: true });
|
|
314
|
+
} else {
|
|
315
|
+
await log('ℹ️ Playwright MCP not detected - browser automation hints will be disabled', { verbose: true });
|
|
316
|
+
argv.promptPlaywrightMcp = false;
|
|
317
|
+
}
|
|
318
|
+
} else {
|
|
319
|
+
await log('ℹ️ Playwright MCP explicitly disabled via --no-prompt-playwright-mcp', { verbose: true });
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
toolResult = await executeQwen({
|
|
323
|
+
issueUrl,
|
|
324
|
+
issueNumber,
|
|
325
|
+
prNumber,
|
|
326
|
+
prUrl: `https://github.com/${owner}/${repo}/pull/${prNumber}`,
|
|
327
|
+
branchName,
|
|
328
|
+
tempDir,
|
|
329
|
+
workspaceTmpDir,
|
|
330
|
+
isContinueMode: true,
|
|
331
|
+
mergeStateStatus,
|
|
332
|
+
forkedRepo: argv.fork,
|
|
333
|
+
feedbackLines,
|
|
334
|
+
forkActionsUrl: null,
|
|
335
|
+
owner,
|
|
336
|
+
repo,
|
|
337
|
+
argv,
|
|
338
|
+
log,
|
|
339
|
+
setLogFile: () => {},
|
|
340
|
+
getLogFile: () => '',
|
|
341
|
+
formatAligned,
|
|
342
|
+
getResourceSnapshot,
|
|
343
|
+
qwenPath,
|
|
344
|
+
$,
|
|
345
|
+
});
|
|
304
346
|
} else {
|
|
305
347
|
// Use Claude (default)
|
|
306
348
|
const claudeExecLib = await import('./claude.lib.mjs');
|
|
@@ -319,6 +319,14 @@ export const performSystemChecks = async (minDiskSpace = 2048, skipToolConnectio
|
|
|
319
319
|
await log('❌ Cannot proceed without Agent connection', { level: 'error' });
|
|
320
320
|
return false;
|
|
321
321
|
}
|
|
322
|
+
} else if (argv.tool === 'qwen') {
|
|
323
|
+
// Validate Qwen Code connection
|
|
324
|
+
const qwenLib = await import('./qwen.lib.mjs');
|
|
325
|
+
isToolConnected = await qwenLib.validateQwenConnection(model);
|
|
326
|
+
if (!isToolConnected) {
|
|
327
|
+
await log('❌ Cannot proceed without Qwen Code connection', { level: 'error' });
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
322
330
|
} else {
|
|
323
331
|
// Validate Claude CLI connection (default)
|
|
324
332
|
const isClaudeConnected = await validateClaudeConnection(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) [default: claude]');
|
|
36
|
+
console.log(' --tool AI tool for agent-commander read-only mode (claude, codex, opencode, agent, qwen) [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');
|
package/src/telegram-bot.mjs
CHANGED
|
@@ -525,7 +525,7 @@ bot.command('help', async ctx => {
|
|
|
525
525
|
message += '📝 *Available Commands:*\n\n';
|
|
526
526
|
|
|
527
527
|
if (solveEnabled) {
|
|
528
|
-
message += '*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*) - Solve a GitHub issue\n';
|
|
528
|
+
message += '*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*, */qwen*) - Solve a GitHub issue\n';
|
|
529
529
|
message += 'Usage: `/solve <github-url> [options]`\n';
|
|
530
530
|
message += 'Example: `/solve https://github.com/owner/repo/issues/123 --model sonnet`\n';
|
|
531
531
|
message += 'Tool aliases imply `--tool <tool>`: `/codex <github-url>` equals `/solve <github-url> --tool codex`\n';
|
|
@@ -535,7 +535,7 @@ bot.command('help', async ctx => {
|
|
|
535
535
|
}
|
|
536
536
|
message += '\n';
|
|
537
537
|
} else {
|
|
538
|
-
message += '*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*) - ❌ Disabled\n\n';
|
|
538
|
+
message += '*/solve* (aliases: */do*, */continue*, */claude*, */codex*, */opencode*, */agent*, */qwen*) - ❌ Disabled\n\n';
|
|
539
539
|
}
|
|
540
540
|
|
|
541
541
|
if (taskEnabled) {
|
|
@@ -576,7 +576,7 @@ bot.command('help', async ctx => {
|
|
|
576
576
|
message += '🔔 *Session Notifications:* Completion notifications are automatic; use /subscribe for private DM forwards.\n';
|
|
577
577
|
if (ISOLATION_BACKEND) message += `🔒 *Isolation Mode:* \`${ISOLATION_BACKEND}\` (experimental)\n`;
|
|
578
578
|
message += '\n';
|
|
579
|
-
message += '⚠️ *Note:* /solve, /do, /continue, /claude, /codex, /opencode, /agent, /task, /split, /hive, /solve\\_queue, /limits, /version, /accept\\_invites, /merge, /stop and /start commands only work in group chats. /terminal\\_watch, /subscribe and /unsubscribe work in private and group chats.\n\n';
|
|
579
|
+
message += '⚠️ *Note:* /solve, /do, /continue, /claude, /codex, /opencode, /agent, /qwen, /task, /split, /hive, /solve\\_queue, /limits, /version, /accept\\_invites, /merge, /stop and /start commands only work in group chats. /terminal\\_watch, /subscribe and /unsubscribe work in private and group chats.\n\n';
|
|
580
580
|
message += '🔧 *Common Options:*\n';
|
|
581
581
|
message += `• \`--model <model>\` or \`-m\` - ${buildModelOptionDescription()}\n`;
|
|
582
582
|
message += '• `--base-branch <branch>` or `-b` - Target branch for PR (default: repo default branch)\n';
|
|
@@ -13,6 +13,7 @@ export const TOOL_SOLVE_COMMAND_ALIASES = Object.freeze({
|
|
|
13
13
|
codex: 'codex',
|
|
14
14
|
opencode: 'opencode',
|
|
15
15
|
agent: 'agent',
|
|
16
|
+
qwen: 'qwen',
|
|
16
17
|
});
|
|
17
18
|
|
|
18
19
|
export const SOLVE_COMMAND_NAMES = Object.freeze(['solve', 'do', 'continue', ...Object.keys(TOOL_SOLVE_COMMAND_ALIASES)]);
|
|
@@ -75,6 +75,15 @@ export async function getRunningCodexProcesses(verbose = false) {
|
|
|
75
75
|
return getRunningProcesses('codex', verbose);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Count running qwen processes.
|
|
80
|
+
* @param {boolean} verbose - Whether to log verbose output
|
|
81
|
+
* @returns {Promise<{count: number, processes: string[]}>}
|
|
82
|
+
*/
|
|
83
|
+
export async function getRunningQwenProcesses(verbose = false) {
|
|
84
|
+
return getRunningProcesses('qwen', verbose);
|
|
85
|
+
}
|
|
86
|
+
|
|
78
87
|
/**
|
|
79
88
|
* Format a threshold as percentage for display.
|
|
80
89
|
* @param {number} ratio - Ratio (0.0 - 1.0)
|
|
@@ -145,6 +154,8 @@ export function formatWaitingReason(metric, currentValue, threshold) {
|
|
|
145
154
|
return 'Claude process is already running';
|
|
146
155
|
case 'codex_running':
|
|
147
156
|
return 'Codex process is already running';
|
|
157
|
+
case 'qwen_running':
|
|
158
|
+
return 'Qwen Code process is already running';
|
|
148
159
|
default:
|
|
149
160
|
return `${metric} threshold exceeded`;
|
|
150
161
|
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { getCachedClaudeLimits, getCachedCodexLimits, getCachedGitHubLimits, getCachedMemoryInfo, getCachedCpuInfo, getCachedDiskInfo, getLimitCache } from './limits.lib.mjs';
|
|
19
|
-
export { formatDuration, getRunningAgentProcesses, getRunningClaudeProcesses, getRunningCodexProcesses, getRunningProcesses } from './telegram-solve-queue.helpers.lib.mjs';
|
|
19
|
+
export { formatDuration, getRunningAgentProcesses, getRunningClaudeProcesses, getRunningCodexProcesses, getRunningProcesses, getRunningQwenProcesses } from './telegram-solve-queue.helpers.lib.mjs';
|
|
20
20
|
import { formatDuration, formatWaitingReason, getRunningAgentProcesses, getRunningClaudeProcesses, getRunningProcesses } from './telegram-solve-queue.helpers.lib.mjs';
|
|
21
21
|
export { QUEUE_CONFIG, THRESHOLD_STRATEGIES } from './queue-config.lib.mjs';
|
|
22
22
|
import { QUEUE_CONFIG } from './queue-config.lib.mjs';
|
|
@@ -149,6 +149,7 @@ export class SolveQueue {
|
|
|
149
149
|
claude: [],
|
|
150
150
|
agent: [],
|
|
151
151
|
codex: [],
|
|
152
|
+
qwen: [],
|
|
152
153
|
};
|
|
153
154
|
this.processing = new Map();
|
|
154
155
|
this.completed = [];
|
|
@@ -160,6 +161,7 @@ export class SolveQueue {
|
|
|
160
161
|
claude: null,
|
|
161
162
|
agent: null,
|
|
162
163
|
codex: null,
|
|
164
|
+
qwen: null,
|
|
163
165
|
};
|
|
164
166
|
// Legacy: keep for compatibility with existing code that uses lastStartTime
|
|
165
167
|
this.lastStartTime = null;
|
|
@@ -561,8 +563,10 @@ export class SolveQueue {
|
|
|
561
563
|
const claudeProcessCount = externalProcessing.byTool.claude || 0;
|
|
562
564
|
const codexProcessCount = externalProcessing.byTool.codex || 0;
|
|
563
565
|
const agentProcessCount = externalProcessing.byTool.agent || 0;
|
|
566
|
+
const qwenProcessCount = externalProcessing.byTool.qwen || 0;
|
|
564
567
|
const hasRunningClaude = claudeProcessCount > 0;
|
|
565
568
|
const hasRunningCodex = codexProcessCount > 0;
|
|
569
|
+
const hasRunningQwen = qwenProcessCount > 0;
|
|
566
570
|
|
|
567
571
|
// Calculate total processing count for system resources (all tools)
|
|
568
572
|
// System resources (RAM, CPU, disk) apply to all tools
|
|
@@ -574,6 +578,7 @@ export class SolveQueue {
|
|
|
574
578
|
// See: https://github.com/link-assistant/hive-mind/issues/1159
|
|
575
579
|
const claudeProcessingCount = this.getProcessingCountByTool('claude');
|
|
576
580
|
const codexProcessingCount = this.getProcessingCountByTool('codex');
|
|
581
|
+
const qwenProcessingCount = this.getProcessingCountByTool('qwen');
|
|
577
582
|
|
|
578
583
|
// Track claude_running as a metric (but don't add to reasons yet)
|
|
579
584
|
if (hasRunningClaude) {
|
|
@@ -582,6 +587,9 @@ export class SolveQueue {
|
|
|
582
587
|
if (hasRunningCodex) {
|
|
583
588
|
this.recordThrottle('codex_running');
|
|
584
589
|
}
|
|
590
|
+
if (hasRunningQwen) {
|
|
591
|
+
this.recordThrottle('qwen_running');
|
|
592
|
+
}
|
|
585
593
|
|
|
586
594
|
// Check system resources with strategy support
|
|
587
595
|
// System resources apply to ALL tools, not just Claude
|
|
@@ -604,8 +612,8 @@ export class SolveQueue {
|
|
|
604
612
|
// This allows agent tasks to proceed when Claude limits are reached
|
|
605
613
|
// See: https://github.com/link-assistant/hive-mind/issues/1159
|
|
606
614
|
// See: https://github.com/link-assistant/hive-mind/issues/1253 (strategies)
|
|
607
|
-
const hasRunningToolProcess = tool === 'codex' ? hasRunningCodex : hasRunningClaude;
|
|
608
|
-
const toolProcessingCount = tool === 'codex' ? codexProcessingCount : claudeProcessingCount;
|
|
615
|
+
const hasRunningToolProcess = tool === 'codex' ? hasRunningCodex : tool === 'qwen' ? hasRunningQwen : hasRunningClaude;
|
|
616
|
+
const toolProcessingCount = tool === 'codex' ? codexProcessingCount : tool === 'qwen' ? qwenProcessingCount : claudeProcessingCount;
|
|
609
617
|
const limitCheck = await this.checkApiLimits(hasRunningToolProcess, toolProcessingCount, tool);
|
|
610
618
|
if (limitCheck.rejected) {
|
|
611
619
|
rejected = true;
|
|
@@ -629,6 +637,9 @@ export class SolveQueue {
|
|
|
629
637
|
if (tool === 'codex' && hasRunningCodex && reasons.length > 0) {
|
|
630
638
|
reasons.push(formatWaitingReason('codex_running', codexProcessCount, 0) + ` (${codexProcessCount} processes)`);
|
|
631
639
|
}
|
|
640
|
+
if (tool === 'qwen' && hasRunningQwen && reasons.length > 0) {
|
|
641
|
+
reasons.push(formatWaitingReason('qwen_running', qwenProcessCount, 0) + ` (${qwenProcessCount} processes)`);
|
|
642
|
+
}
|
|
632
643
|
|
|
633
644
|
const canStart = reasons.length === 0 && !rejected;
|
|
634
645
|
|
|
@@ -650,10 +661,12 @@ export class SolveQueue {
|
|
|
650
661
|
claudeProcesses: claudeProcessCount,
|
|
651
662
|
codexProcesses: codexProcessCount,
|
|
652
663
|
agentProcesses: agentProcessCount,
|
|
664
|
+
qwenProcesses: qwenProcessCount,
|
|
653
665
|
isolatedProcesses: externalProcessing.isolatedTotal,
|
|
654
666
|
totalProcessing,
|
|
655
667
|
claudeProcessingCount,
|
|
656
668
|
codexProcessingCount,
|
|
669
|
+
qwenProcessingCount,
|
|
657
670
|
};
|
|
658
671
|
}
|
|
659
672
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const validateToolConnection = async ({ tool = 'claude', model, verbose = false, validateClaudeConnection } = {}) => {
|
|
2
|
+
if (tool === 'opencode') {
|
|
3
|
+
return (await import('./opencode.lib.mjs')).validateOpenCodeConnection(model);
|
|
4
|
+
}
|
|
5
|
+
if (tool === 'codex') {
|
|
6
|
+
return (await import('./codex.lib.mjs')).validateCodexConnection(model, verbose);
|
|
7
|
+
}
|
|
8
|
+
if (tool === 'agent') {
|
|
9
|
+
return (await import('./agent.lib.mjs')).validateAgentConnection(model);
|
|
10
|
+
}
|
|
11
|
+
if (tool === 'qwen') {
|
|
12
|
+
return (await import('./qwen.lib.mjs')).validateQwenConnection(model);
|
|
13
|
+
}
|
|
14
|
+
const validateClaude = validateClaudeConnection || (await import('./claude.lib.mjs')).validateClaudeConnection;
|
|
15
|
+
return validateClaude(model);
|
|
16
|
+
};
|
package/src/version-info.lib.mjs
CHANGED
|
@@ -554,7 +554,7 @@ const VERSION_COMMANDS = [
|
|
|
554
554
|
{ key: 'agent', command: 'agent --version 2>&1' },
|
|
555
555
|
{ key: 'codex', command: 'codex --version 2>&1' },
|
|
556
556
|
{ key: 'opencode', command: 'opencode --version 2>&1' },
|
|
557
|
-
{ key: 'qwenCode', command: 'qwen
|
|
557
|
+
{ key: 'qwenCode', command: 'qwen --version 2>&1' },
|
|
558
558
|
{ key: 'gemini', command: 'gemini --version 2>&1' },
|
|
559
559
|
{ key: 'copilot', command: 'copilot --version 2>&1' },
|
|
560
560
|
|