feat: add Qwen Code CLI support#28
Conversation
furqanshaikh
commented
Mar 29, 2026
- Add .qwen/install.sh and .qwen/README.md for Qwen skill installation
- Add QWEN.md template and QWEN-append.md for project configuration
- Update cli/metaswarm.js with --qwen flag support
- Update lib/platform-detect.js to detect Qwen Code CLI
- Update README.md, AGENTS.md, GETTING_STARTED.md with Qwen documentation
- Update package.json with qwen-cli keyword and .qwen/ in files
- Add .qwen/install.sh and .qwen/README.md for Qwen skill installation - Add QWEN.md template and QWEN-append.md for project configuration - Update cli/metaswarm.js with --qwen flag support - Update lib/platform-detect.js to detect Qwen Code CLI - Update README.md, AGENTS.md, GETTING_STARTED.md with Qwen documentation - Update package.json with qwen-cli keyword and .qwen/ in files Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Summary by CodeRabbit
WalkthroughThis PR adds comprehensive Qwen Code CLI support to the metaswarm multi-agent orchestration framework. Changes include new installation infrastructure, platform detection logic, CLI command support, comprehensive documentation, and project templates enabling Qwen users to discover and invoke metaswarm skills. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR adds Qwen Code CLI as a fourth supported platform for metaswarm, following the same pattern established by the Codex CLI integration. It introduces an installer script, README, project-context document, templates, platform detection, and CLI flag support consistently across the codebase. The integration is functionally correct but has three issues worth addressing:
Confidence Score: 4/5Safe to merge after fixing the stale platform message in One P1 defect (stale platform list in the direct-CLI path of
|
| Filename | Overview |
|---|---|
| cli/metaswarm.js | Adds installQwen() and wires --qwen flag throughout; installQwen() is a verbatim copy of installCodex() — a significant DRY/OCP violation that will compound with every future platform. |
| lib/platform-detect.js | Adds detectQwen() correctly, but the CLI-mode fallback message at line 129 was not updated to include qwen, giving users wrong guidance when no tools are found. |
| QWEN.md | New project-context file for Qwen Code CLI users; Requirements section omits Qwen Code CLI from the list of supported CLIs. |
| .qwen/install.sh | New Bash installer for Qwen; mirrors the .codex/install.sh pattern with correct set -euo pipefail, clone/update logic, and symlink handling. |
| .qwen/README.md | New README covering install, skill invocation, limitations, and troubleshooting for Qwen Code CLI — well-structured and complete. |
| templates/QWEN.md | New project-level QWEN.md template with placeholder sections; consistent with existing CLAUDE.md and AGENTS.md templates. |
| templates/QWEN-append.md | New append template for injecting a metaswarm section into existing QWEN.md files; correct and concise. |
| package.json | Adds .qwen/ to the files array and qwen-cli keyword; description updated — all correct. |
| AGENTS.md | Updates description and quick-reference blurb to include Qwen Code CLI — accurate one-liner changes. |
| GETTING_STARTED.md | Adds Qwen Code CLI install snippet and $setup/$start entries alongside other platforms — consistent with existing structure. |
| README.md | Adds Qwen Code CLI to descriptions, repo structure diagram, platform table, and requirements — all accurate. |
Comments Outside Diff (2)
-
lib/platform-detect.js, line 129-130 (link)Stale platform list in CLI mode output
detectQwen()was added todetectPlatforms(), but the fallback message in the CLI-mode block was not updated. A user runningnode lib/platform-detect.jsdirectly who has no tools installed will be told only aboutclaude,codex, andgemini—qwenis silently omitted, making the tool misleading after this change.Prompt To Fix With AI
This is a comment left during a code review. Path: lib/platform-detect.js Line: 129-130 Comment: **Stale platform list in CLI mode output** `detectQwen()` was added to `detectPlatforms()`, but the fallback message in the CLI-mode block was not updated. A user running `node lib/platform-detect.js` directly who has no tools installed will be told only about `claude`, `codex`, and `gemini` — `qwen` is silently omitted, making the tool misleading after this change. How can I resolve this? If you propose a fix, please make it concise.
-
QWEN.md, line 532 (link)Requirements section omits Qwen Code CLI
This file is the project context document for Qwen Code CLI users, yet the Requirements section says "One of: Claude Code, Gemini CLI, or Codex CLI" —
Qwen Code CLIis missing from the list. A Qwen user reading this will incorrectly think they need one of the other CLIs.Prompt To Fix With AI
This is a comment left during a code review. Path: QWEN.md Line: 532 Comment: **Requirements section omits Qwen Code CLI** This file is the project context document for Qwen Code CLI users, yet the Requirements section says "One of: Claude Code, Gemini CLI, or Codex CLI" — `Qwen Code CLI` is missing from the list. A Qwen user reading this will incorrectly think they need one of the other CLIs. How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: lib/platform-detect.js
Line: 129-130
Comment:
**Stale platform list in CLI mode output**
`detectQwen()` was added to `detectPlatforms()`, but the fallback message in the CLI-mode block was not updated. A user running `node lib/platform-detect.js` directly who has no tools installed will be told only about `claude`, `codex`, and `gemini` — `qwen` is silently omitted, making the tool misleading after this change.
```suggestion
console.log('Install one of: claude, codex, gemini, qwen');
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: cli/metaswarm.js
Line: 125-179
Comment:
**`installQwen()` is a verbatim copy of `installCodex()`**
The entire 55-line body of `installQwen()` is identical to `installCodex()` — only the env var name (`QWEN_HOME` vs `CODEX_HOME`), the default home directory (`.qwen` vs `.codex`), and the skills directory (`.qwen/skills` vs `.agents/skills`) differ. This is a direct violation of the Open/Closed Principle: adding Qwen required copy-pasting working code rather than extending an abstraction.
Extract a shared `installSkillBased(config)` helper and call it from both:
```js
function installSkillBased({ name, envVar, defaultDir, skillsDir }) {
console.log(`\n Installing for ${name}...\n`);
const installDir = path.join(process.env[envVar] || path.join(os.homedir(), defaultDir), 'metaswarm');
const resolvedSkillsDir = path.join(os.homedir(), skillsDir);
// ... shared clone/pull/symlink logic ...
}
function installCodex() {
return installSkillBased({ name: 'Codex CLI', envVar: 'CODEX_HOME', defaultDir: '.codex', skillsDir: path.join('.agents', 'skills') });
}
function installQwen() {
return installSkillBased({ name: 'Qwen Code CLI', envVar: 'QWEN_HOME', defaultDir: '.qwen', skillsDir: path.join('.qwen', 'skills') });
}
```
The same duplication will compound again when the next platform is added.
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: QWEN.md
Line: 532
Comment:
**Requirements section omits Qwen Code CLI**
This file is the project context document for Qwen Code CLI users, yet the Requirements section says "One of: Claude Code, Gemini CLI, or Codex CLI" — `Qwen Code CLI` is missing from the list. A Qwen user reading this will incorrectly think they need one of the other CLIs.
```suggestion
- One of: Claude Code, Gemini CLI, Codex CLI, or Qwen Code CLI
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "feat: add Qwen Code CLI support" | Re-trigger Greptile
| function installQwen() { | ||
| console.log('\n Installing for Qwen Code CLI...\n'); | ||
| const installDir = path.join(process.env.QWEN_HOME || path.join(os.homedir(), '.qwen'), 'metaswarm'); | ||
| const skillsDir = path.join(os.homedir(), '.qwen', 'skills'); | ||
|
|
||
| if (fs.existsSync(installDir)) { | ||
| console.log(` Updating existing installation at ${installDir}...`); | ||
| try { | ||
| execSync('git pull --rebase origin main', { cwd: installDir, stdio: 'inherit' }); | ||
| info('Updated metaswarm'); | ||
| } catch (e) { | ||
| warn(`git pull failed: ${e.message || e}`); | ||
| return; | ||
| } | ||
| } else { | ||
| console.log(` Cloning metaswarm to ${installDir}...`); | ||
| mkdirp(path.dirname(installDir)); | ||
| try { | ||
| execSync(`git clone https://github.com/dsifry/metaswarm.git "${installDir}"`, { stdio: 'inherit' }); | ||
| info('Cloned metaswarm'); | ||
| } catch (e) { | ||
| warn(`Clone failed: ${e.message}`); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| // Symlink skills | ||
| mkdirp(skillsDir); | ||
| const skillsPath = path.join(installDir, 'skills'); | ||
| if (fs.existsSync(skillsPath)) { | ||
| let linked = 0; | ||
| for (const dir of fs.readdirSync(skillsPath)) { | ||
| const srcDir = path.join(skillsPath, dir); | ||
| if (!fs.statSync(srcDir).isDirectory()) continue; | ||
| const linkName = `metaswarm-${dir}`; | ||
| const linkPath = path.join(skillsDir, linkName); | ||
|
|
||
| try { | ||
| if (fs.lstatSync(linkPath).isSymbolicLink()) { | ||
| fs.unlinkSync(linkPath); | ||
| } else if (fs.existsSync(linkPath)) { | ||
| warn(`${linkPath} exists as a directory, skipping`); | ||
| continue; | ||
| } | ||
| } catch (e) { | ||
| if (e.code !== 'ENOENT') warn(`Unexpected error checking ${linkPath}: ${e.message}`); | ||
| } | ||
|
|
||
| fs.symlinkSync(srcDir, linkPath); | ||
| linked++; | ||
| } | ||
| info(`Linked ${linked} skills into ${skillsDir}`); | ||
| } | ||
| console.log(' Next: In your project, run $setup'); | ||
| } |
There was a problem hiding this comment.
installQwen() is a verbatim copy of installCodex()
The entire 55-line body of installQwen() is identical to installCodex() — only the env var name (QWEN_HOME vs CODEX_HOME), the default home directory (.qwen vs .codex), and the skills directory (.qwen/skills vs .agents/skills) differ. This is a direct violation of the Open/Closed Principle: adding Qwen required copy-pasting working code rather than extending an abstraction.
Extract a shared installSkillBased(config) helper and call it from both:
function installSkillBased({ name, envVar, defaultDir, skillsDir }) {
console.log(`\n Installing for ${name}...\n`);
const installDir = path.join(process.env[envVar] || path.join(os.homedir(), defaultDir), 'metaswarm');
const resolvedSkillsDir = path.join(os.homedir(), skillsDir);
// ... shared clone/pull/symlink logic ...
}
function installCodex() {
return installSkillBased({ name: 'Codex CLI', envVar: 'CODEX_HOME', defaultDir: '.codex', skillsDir: path.join('.agents', 'skills') });
}
function installQwen() {
return installSkillBased({ name: 'Qwen Code CLI', envVar: 'QWEN_HOME', defaultDir: '.qwen', skillsDir: path.join('.qwen', 'skills') });
}The same duplication will compound again when the next platform is added.
Prompt To Fix With AI
This is a comment left during a code review.
Path: cli/metaswarm.js
Line: 125-179
Comment:
**`installQwen()` is a verbatim copy of `installCodex()`**
The entire 55-line body of `installQwen()` is identical to `installCodex()` — only the env var name (`QWEN_HOME` vs `CODEX_HOME`), the default home directory (`.qwen` vs `.codex`), and the skills directory (`.qwen/skills` vs `.agents/skills`) differ. This is a direct violation of the Open/Closed Principle: adding Qwen required copy-pasting working code rather than extending an abstraction.
Extract a shared `installSkillBased(config)` helper and call it from both:
```js
function installSkillBased({ name, envVar, defaultDir, skillsDir }) {
console.log(`\n Installing for ${name}...\n`);
const installDir = path.join(process.env[envVar] || path.join(os.homedir(), defaultDir), 'metaswarm');
const resolvedSkillsDir = path.join(os.homedir(), skillsDir);
// ... shared clone/pull/symlink logic ...
}
function installCodex() {
return installSkillBased({ name: 'Codex CLI', envVar: 'CODEX_HOME', defaultDir: '.codex', skillsDir: path.join('.agents', 'skills') });
}
function installQwen() {
return installSkillBased({ name: 'Qwen Code CLI', envVar: 'QWEN_HOME', defaultDir: '.qwen', skillsDir: path.join('.qwen', 'skills') });
}
```
The same duplication will compound again when the next platform is added.
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.qwen/install.sh:
- Around line 24-25: Save the caller's original working directory to a variable
before changing directories (e.g., PREV_PWD="$PWD" or PROJECT_DIR="$PWD") so the
subsequent QWEN.md existence test doesn't rely on the current directory; after
the cd "$INSTALL_DIR" and git pull lines, update the QWEN.md check (the block
that currently tests for QWEN.md at lines 64-71) to reference the saved variable
(e.g., [ -f "$PREV_PWD/QWEN.md" ] or "$PROJECT_DIR/QWEN.md") instead of the CWD,
and use that saved path anywhere else the script needs to inspect the original
project tree.
- Around line 48-57: The script currently overwrites any existing regular file
at target with ln -sf; modify the install logic around target so you explicitly
detect and skip regular files (e.g., add an elif [ -f "$target" ] branch that
echoes a warning and continues) instead of falling through to ln -sf, and ensure
linked is only incremented after a successful ln -sf operation for the symlink
creation.
- Around line 12-14: SKILLS_DIR is hardcoded to "$HOME/.qwen/skills" while
INSTALL_DIR uses INSTALL_DIR="${QWEN_HOME:-$HOME/.qwen}/metaswarm", causing
inconsistent bases; change SKILLS_DIR to use the same QWEN_HOME fallback (e.g.
SKILLS_DIR="${QWEN_HOME:-$HOME/.qwen}/skills") so both INSTALL_DIR and
SKILLS_DIR honor QWEN_HOME consistently (update references to SKILLS_DIR if
any).
In `@cli/metaswarm.js`:
- Around line 125-179: installQwen() duplicates installCodex() logic; extract a
shared helper like installForPlatform(repoUrl, installDir, skillsDir, repoName)
that performs clone-or-pull (uses execSync with cwd and stdio), creates dirs
(mkdirp), and symlinks skills (fs.readdirSync, fs.lstatSync, fs.symlinkSync)
while logging via info/warn; then rewrite installQwen() and installCodex() to
build their platform-specific paths (QWEN_HOME vs CODex_HOME or defaults) and
call installForPlatform with the repo URL and names (e.g., 'metaswarm' and the
skills target) to remove the ~55-line duplication.
In `@lib/platform-detect.js`:
- Around line 87-103: The test's grep pattern doesn't include the new Qwen
platform name returned by detectQwen() (name: 'Qwen Code CLI'); update the
test's grep/regex that currently matches "Claude Code\|Codex CLI\|Gemini CLI" to
also include "\|Qwen Code CLI" so the installer test validates Qwen detection;
ensure the updated pattern escapes the pipe correctly in the shell grep command.
In `@QWEN.md`:
- Line 262: The prerequisites list currently reads "One of: Claude Code, Gemini
CLI, or Codex CLI" and omits Qwen; update that list to include "Qwen Code CLI"
(e.g., "One of: Claude Code, Gemini CLI, Qwen Code CLI, or Codex CLI") so the
Qwen CLI is listed alongside the other CLIs and adjust punctuation/ordering for
consistent grammar; locate the exact string "One of: Claude Code, Gemini CLI, or
Codex CLI" in QWEN.md and modify it accordingly.
- Around line 72-84: Replace all slash-invocation examples with the Qwen skill
token form so they call skills correctly: change occurrences of `/start-task`,
`/setup`, `/status`, `/prime`, `/review-design`, `/plan-review-gate`,
`/pr-shepherd`, `/handle-pr-comments`, `/create-issue`, `/self-reflect` (and the
mentions at lines referenced in the review) to their `$name` equivalents
(`$start`, `$setup`, `$status`, `$prime`, `$review-design <path>` as
`$review-design <path>`, `$plan-review-gate`, `$pr-shepherd <pr-number>`,
`$handle-pr-comments <pr-number>`, `$create-issue`, `$self-reflect`) so the
guide consistently uses SKILL.md `name` syntax; ensure argument placeholders
remain and update any prose that instructs users to "use slash commands" to say
"use the `$name` skill token" instead.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c45c3307-1145-4f82-93da-b8e7c21a1b9d
📒 Files selected for processing (11)
.qwen/README.md.qwen/install.shAGENTS.mdGETTING_STARTED.mdQWEN.mdREADME.mdcli/metaswarm.jslib/platform-detect.jspackage.jsontemplates/QWEN-append.mdtemplates/QWEN.md
| INSTALL_DIR="${QWEN_HOME:-$HOME/.qwen}/metaswarm" | ||
| SKILLS_DIR="$HOME/.qwen/skills" | ||
| REPO_URL="https://github.com/dsifry/metaswarm.git" |
There was a problem hiding this comment.
Honor QWEN_HOME consistently for both install and skills paths.
Line 13 hardcodes ~/.qwen/skills, so setting QWEN_HOME installs repo in one base directory but links skills into another.
Proposed fix
-INSTALL_DIR="${QWEN_HOME:-$HOME/.qwen}/metaswarm"
-SKILLS_DIR="$HOME/.qwen/skills"
+QWEN_BASE_DIR="${QWEN_HOME:-$HOME/.qwen}"
+INSTALL_DIR="$QWEN_BASE_DIR/metaswarm"
+SKILLS_DIR="$QWEN_BASE_DIR/skills"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| INSTALL_DIR="${QWEN_HOME:-$HOME/.qwen}/metaswarm" | |
| SKILLS_DIR="$HOME/.qwen/skills" | |
| REPO_URL="https://github.com/dsifry/metaswarm.git" | |
| QWEN_BASE_DIR="${QWEN_HOME:-$HOME/.qwen}" | |
| INSTALL_DIR="$QWEN_BASE_DIR/metaswarm" | |
| SKILLS_DIR="$QWEN_BASE_DIR/skills" | |
| REPO_URL="https://github.com/dsifry/metaswarm.git" |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.qwen/install.sh around lines 12 - 14, SKILLS_DIR is hardcoded to
"$HOME/.qwen/skills" while INSTALL_DIR uses
INSTALL_DIR="${QWEN_HOME:-$HOME/.qwen}/metaswarm", causing inconsistent bases;
change SKILLS_DIR to use the same QWEN_HOME fallback (e.g.
SKILLS_DIR="${QWEN_HOME:-$HOME/.qwen}/skills") so both INSTALL_DIR and
SKILLS_DIR honor QWEN_HOME consistently (update references to SKILLS_DIR if
any).
| cd "$INSTALL_DIR" | ||
| git pull --rebase origin main 2>/dev/null || { |
There was a problem hiding this comment.
QWEN.md detection should not depend on the update-path working directory.
After Line 24, the check at Lines 64-71 may evaluate the installer repo directory instead of the user’s original project directory.
Proposed fix
+ORIGINAL_PWD="$(pwd)"
...
if [ -d "$INSTALL_DIR" ]; then
echo " Updating existing installation at $INSTALL_DIR..."
cd "$INSTALL_DIR"
git pull --rebase origin main 2>/dev/null || {
...
}
fi
...
-if [ -f "QWEN.md" ] && grep -q "metaswarm" "QWEN.md" 2>/dev/null; then
+if [ -f "$ORIGINAL_PWD/QWEN.md" ] && grep -q "metaswarm" "$ORIGINAL_PWD/QWEN.md" 2>/dev/null; then
echo " QWEN.md already has metaswarm section."
-elif [ -f "QWEN.md" ]; then
+elif [ -f "$ORIGINAL_PWD/QWEN.md" ]; then
echo " Note: QWEN.md exists but doesn't reference metaswarm."Also applies to: 64-71
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.qwen/install.sh around lines 24 - 25, Save the caller's original working
directory to a variable before changing directories (e.g., PREV_PWD="$PWD" or
PROJECT_DIR="$PWD") so the subsequent QWEN.md existence test doesn't rely on the
current directory; after the cd "$INSTALL_DIR" and git pull lines, update the
QWEN.md check (the block that currently tests for QWEN.md at lines 64-71) to
reference the saved variable (e.g., [ -f "$PREV_PWD/QWEN.md" ] or
"$PROJECT_DIR/QWEN.md") instead of the CWD, and use that saved path anywhere
else the script needs to inspect the original project tree.
| if [ -L "$target" ]; then | ||
| # Update existing symlink | ||
| rm "$target" | ||
| elif [ -d "$target" ]; then | ||
| echo " Warning: $target exists as a directory, skipping" | ||
| continue | ||
| fi | ||
|
|
||
| ln -sf "$skill_dir" "$target" | ||
| linked=$((linked + 1)) |
There was a problem hiding this comment.
Prevent clobbering existing regular files in the skills directory.
If a non-directory, non-symlink file exists at target, Line 56 will overwrite it via ln -sf.
Proposed fix
if [ -L "$target" ]; then
# Update existing symlink
rm "$target"
elif [ -d "$target" ]; then
echo " Warning: $target exists as a directory, skipping"
continue
+ elif [ -e "$target" ]; then
+ echo " Warning: $target exists and is not a symlink, skipping"
+ continue
fi📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if [ -L "$target" ]; then | |
| # Update existing symlink | |
| rm "$target" | |
| elif [ -d "$target" ]; then | |
| echo " Warning: $target exists as a directory, skipping" | |
| continue | |
| fi | |
| ln -sf "$skill_dir" "$target" | |
| linked=$((linked + 1)) | |
| if [ -L "$target" ]; then | |
| # Update existing symlink | |
| rm "$target" | |
| elif [ -d "$target" ]; then | |
| echo " Warning: $target exists as a directory, skipping" | |
| continue | |
| elif [ -e "$target" ]; then | |
| echo " Warning: $target exists and is not a symlink, skipping" | |
| continue | |
| fi | |
| ln -sf "$skill_dir" "$target" | |
| linked=$((linked + 1)) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.qwen/install.sh around lines 48 - 57, The script currently overwrites any
existing regular file at target with ln -sf; modify the install logic around
target so you explicitly detect and skip regular files (e.g., add an elif [ -f
"$target" ] branch that echoes a warning and continues) instead of falling
through to ln -sf, and ensure linked is only incremented after a successful ln
-sf operation for the symlink creation.
| function installQwen() { | ||
| console.log('\n Installing for Qwen Code CLI...\n'); | ||
| const installDir = path.join(process.env.QWEN_HOME || path.join(os.homedir(), '.qwen'), 'metaswarm'); | ||
| const skillsDir = path.join(os.homedir(), '.qwen', 'skills'); | ||
|
|
||
| if (fs.existsSync(installDir)) { | ||
| console.log(` Updating existing installation at ${installDir}...`); | ||
| try { | ||
| execSync('git pull --rebase origin main', { cwd: installDir, stdio: 'inherit' }); | ||
| info('Updated metaswarm'); | ||
| } catch (e) { | ||
| warn(`git pull failed: ${e.message || e}`); | ||
| return; | ||
| } | ||
| } else { | ||
| console.log(` Cloning metaswarm to ${installDir}...`); | ||
| mkdirp(path.dirname(installDir)); | ||
| try { | ||
| execSync(`git clone https://github.com/dsifry/metaswarm.git "${installDir}"`, { stdio: 'inherit' }); | ||
| info('Cloned metaswarm'); | ||
| } catch (e) { | ||
| warn(`Clone failed: ${e.message}`); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| // Symlink skills | ||
| mkdirp(skillsDir); | ||
| const skillsPath = path.join(installDir, 'skills'); | ||
| if (fs.existsSync(skillsPath)) { | ||
| let linked = 0; | ||
| for (const dir of fs.readdirSync(skillsPath)) { | ||
| const srcDir = path.join(skillsPath, dir); | ||
| if (!fs.statSync(srcDir).isDirectory()) continue; | ||
| const linkName = `metaswarm-${dir}`; | ||
| const linkPath = path.join(skillsDir, linkName); | ||
|
|
||
| try { | ||
| if (fs.lstatSync(linkPath).isSymbolicLink()) { | ||
| fs.unlinkSync(linkPath); | ||
| } else if (fs.existsSync(linkPath)) { | ||
| warn(`${linkPath} exists as a directory, skipping`); | ||
| continue; | ||
| } | ||
| } catch (e) { | ||
| if (e.code !== 'ENOENT') warn(`Unexpected error checking ${linkPath}: ${e.message}`); | ||
| } | ||
|
|
||
| fs.symlinkSync(srcDir, linkPath); | ||
| linked++; | ||
| } | ||
| info(`Linked ${linked} skills into ${skillsDir}`); | ||
| } | ||
| console.log(' Next: In your project, run $setup'); | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider extracting shared installation logic with installCodex().
The installQwen() function is nearly identical to installCodex() (lines 55-109). Both follow the same pattern: clone/update repo, then symlink skills to a platform-specific directory. This is ~55 lines of duplicated logic.
♻️ Proposed refactor to reduce duplication
+function installCloneAndSymlink(platformName, installDir, skillsDir, nextStep) {
+ console.log(`\n Installing for ${platformName}...\n`);
+
+ if (fs.existsSync(installDir)) {
+ console.log(` Updating existing installation at ${installDir}...`);
+ try {
+ execSync('git pull --rebase origin main', { cwd: installDir, stdio: 'inherit' });
+ info('Updated metaswarm');
+ } catch (e) {
+ warn(`git pull failed: ${e.message || e}`);
+ return;
+ }
+ } else {
+ console.log(` Cloning metaswarm to ${installDir}...`);
+ mkdirp(path.dirname(installDir));
+ try {
+ execSync(`git clone https://github.com/dsifry/metaswarm.git "${installDir}"`, { stdio: 'inherit' });
+ info('Cloned metaswarm');
+ } catch (e) {
+ warn(`Clone failed: ${e.message}`);
+ return;
+ }
+ }
+
+ mkdirp(skillsDir);
+ const skillsPath = path.join(installDir, 'skills');
+ if (fs.existsSync(skillsPath)) {
+ let linked = 0;
+ for (const dir of fs.readdirSync(skillsPath)) {
+ const srcDir = path.join(skillsPath, dir);
+ if (!fs.statSync(srcDir).isDirectory()) continue;
+ const linkName = `metaswarm-${dir}`;
+ const linkPath = path.join(skillsDir, linkName);
+
+ try {
+ if (fs.lstatSync(linkPath).isSymbolicLink()) {
+ fs.unlinkSync(linkPath);
+ } else if (fs.existsSync(linkPath)) {
+ warn(`${linkPath} exists as a directory, skipping`);
+ continue;
+ }
+ } catch (e) {
+ if (e.code !== 'ENOENT') warn(`Unexpected error checking ${linkPath}: ${e.message}`);
+ }
+
+ fs.symlinkSync(srcDir, linkPath);
+ linked++;
+ }
+ info(`Linked ${linked} skills into ${skillsDir}`);
+ }
+ console.log(` Next: In your project, run ${nextStep}`);
+}
+
function installCodex() {
- console.log('\n Installing for Codex CLI...\n');
const installDir = path.join(process.env.CODEX_HOME || path.join(os.homedir(), '.codex'), 'metaswarm');
const skillsDir = path.join(os.homedir(), '.agents', 'skills');
- // ... 50+ lines of clone/symlink logic
+ installCloneAndSymlink('Codex CLI', installDir, skillsDir, '$setup');
}
function installQwen() {
- console.log('\n Installing for Qwen Code CLI...\n');
const installDir = path.join(process.env.QWEN_HOME || path.join(os.homedir(), '.qwen'), 'metaswarm');
const skillsDir = path.join(os.homedir(), '.qwen', 'skills');
- // ... 50+ lines of identical clone/symlink logic
+ installCloneAndSymlink('Qwen Code CLI', installDir, skillsDir, '$setup');
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cli/metaswarm.js` around lines 125 - 179, installQwen() duplicates
installCodex() logic; extract a shared helper like installForPlatform(repoUrl,
installDir, skillsDir, repoName) that performs clone-or-pull (uses execSync with
cwd and stdio), creates dirs (mkdirp), and symlinks skills (fs.readdirSync,
fs.lstatSync, fs.symlinkSync) while logging via info/warn; then rewrite
installQwen() and installCodex() to build their platform-specific paths
(QWEN_HOME vs CODex_HOME or defaults) and call installForPlatform with the repo
URL and names (e.g., 'metaswarm' and the skills target) to remove the ~55-line
duplication.
| function detectQwen() { | ||
| const installed = commandExists('qwen'); | ||
| const installDir = path.join(process.env.QWEN_HOME || path.join(os.homedir(), '.qwen'), 'metaswarm'); | ||
| const skillsDir = path.join(os.homedir(), '.qwen', 'skills'); | ||
|
|
||
| return { | ||
| installed, | ||
| name: 'Qwen Code CLI', | ||
| command: 'qwen', | ||
| installDir, | ||
| skillsDir, | ||
| installMethod: 'clone+symlink', | ||
| installCommand: 'bash .qwen/install.sh', | ||
| setupCommand: '$setup', | ||
| instructionFile: 'QWEN.md', | ||
| }; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if Qwen is included in the platform detection test
rg -n "Qwen" tests/Repository: dsifry/metaswarm
Length of output: 42
🏁 Script executed:
# Check the exact content of the test file
fd test-installer.sh tests/ && wc -l tests/cli/test-installer.shRepository: dsifry/metaswarm
Length of output: 119
🏁 Script executed:
# Read the specific lines mentioned in the review comment
sed -n '38,46p' tests/cli/test-installer.shRepository: dsifry/metaswarm
Length of output: 381
🏁 Script executed:
# Search for any other test files that might cover platform detection
fd ".*test.*\.sh$|.*\.test\.(js|ts)$|.*\.spec\.(js|ts)$" tests/ | head -20Repository: dsifry/metaswarm
Length of output: 261
Update the test to include Qwen detection.
The detectQwen() function is correctly implemented, but the test in tests/cli/test-installer.sh (lines ~42) only checks for "Claude Code\|Codex CLI\|Gemini CLI" in the grep pattern. Since detectQwen() returns name: 'Qwen Code CLI', update the grep pattern to include \|Qwen Code CLI so the test validates Qwen platform detection.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@lib/platform-detect.js` around lines 87 - 103, The test's grep pattern
doesn't include the new Qwen platform name returned by detectQwen() (name: 'Qwen
Code CLI'); update the test's grep/regex that currently matches "Claude
Code\|Codex CLI\|Gemini CLI" to also include "\|Qwen Code CLI" so the installer
test validates Qwen detection; ensure the updated pattern escapes the pipe
correctly in the shell grep command.
| | Command | Description | | ||
| |---|---| | ||
| | `/start-task` or `$start` | Begin tracked work on a task | | ||
| | `/setup` or `$setup` | Interactive guided project setup | | ||
| | `/status` or `$status` | Run 9 diagnostic checks | | ||
| | `/prime` | Load relevant knowledge before work | | ||
| | `/review-design <path>` | Trigger 6-agent Design Review Gate | | ||
| | `/plan-review-gate` | Adversarial plan review (3 reviewers) | | ||
| | `/pr-shepherd <pr-number>` | Monitor PR through to merge | | ||
| | `/handle-pr-comments <pr-number>` | Handle PR review comments | | ||
| | `/create-issue` | Create well-structured GitHub Issue | | ||
| | `/self-reflect` | Extract learnings from recent PRs | | ||
|
|
There was a problem hiding this comment.
Use $name commands consistently in this Qwen-specific guide.
Lines 72-84 and Line 98 still direct users to slash commands; in this file, command guidance should use Qwen skill tokens ($start, $setup, etc.) to avoid failed invocations.
Proposed fix
-| `/start-task` or `$start` | Begin tracked work on a task |
-| `/setup` or `$setup` | Interactive guided project setup |
-| `/status` or `$status` | Run 9 diagnostic checks |
+| `$start` | Begin tracked work on a task |
+| `$setup` | Interactive guided project setup |
+| `$status` | Run 9 diagnostic checks |
...
-1. **Start a task**: Run `/start-task` and describe what you want in plain English
+1. **Start a task**: Run `$start` and describe what you want in plain EnglishAlso applies to: 98-99
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@QWEN.md` around lines 72 - 84, Replace all slash-invocation examples with the
Qwen skill token form so they call skills correctly: change occurrences of
`/start-task`, `/setup`, `/status`, `/prime`, `/review-design`,
`/plan-review-gate`, `/pr-shepherd`, `/handle-pr-comments`, `/create-issue`,
`/self-reflect` (and the mentions at lines referenced in the review) to their
`$name` equivalents (`$start`, `$setup`, `$status`, `$prime`, `$review-design
<path>` as `$review-design <path>`, `$plan-review-gate`, `$pr-shepherd
<pr-number>`, `$handle-pr-comments <pr-number>`, `$create-issue`,
`$self-reflect`) so the guide consistently uses SKILL.md `name` syntax; ensure
argument placeholders remain and update any prose that instructs users to "use
slash commands" to say "use the `$name` skill token" instead.
|
|
||
| ## Requirements | ||
|
|
||
| - One of: Claude Code, Gemini CLI, or Codex CLI |
There was a problem hiding this comment.
Add Qwen Code CLI to the prerequisites list.
Line 262 currently excludes Qwen from the “One of” list, which is inconsistent with this file’s purpose.
Proposed fix
-- One of: Claude Code, Gemini CLI, or Codex CLI
+- One of: Qwen Code CLI, Claude Code, Gemini CLI, or Codex CLI📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - One of: Claude Code, Gemini CLI, or Codex CLI | |
| - One of: Qwen Code CLI, Claude Code, Gemini CLI, or Codex CLI |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@QWEN.md` at line 262, The prerequisites list currently reads "One of: Claude
Code, Gemini CLI, or Codex CLI" and omits Qwen; update that list to include
"Qwen Code CLI" (e.g., "One of: Claude Code, Gemini CLI, Qwen Code CLI, or Codex
CLI") so the Qwen CLI is listed alongside the other CLIs and adjust
punctuation/ordering for consistent grammar; locate the exact string "One of:
Claude Code, Gemini CLI, or Codex CLI" in QWEN.md and modify it accordingly.