Skip to content

feat: add Qwen Code CLI support#28

Open
furqanshaikh wants to merge 1 commit into
dsifry:mainfrom
furqanshaikh:main
Open

feat: add Qwen Code CLI support#28
furqanshaikh wants to merge 1 commit into
dsifry:mainfrom
furqanshaikh:main

Conversation

@furqanshaikh

Copy link
Copy Markdown
  • 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>
@coderabbitai

coderabbitai Bot commented Mar 29, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features

    • Added Qwen Code CLI as a supported platform for multi-agent orchestration
  • Documentation

    • New installation guides and setup instructions for Qwen Code CLI
    • Project-specific workflow documentation and quality gate guidelines
    • Updated framework documentation to include Qwen across all setup resources

Walkthrough

This 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

Cohort / File(s) Summary
Documentation & Setup Guides
.qwen/README.md, AGENTS.md, GETTING_STARTED.md, README.md, QWEN.md
Added Qwen Code CLI to project scope, documented installation methods, platform commands, skill structure, and project-specific workflow conventions.
Installation Infrastructure
.qwen/install.sh, cli/metaswarm.js, lib/platform-detect.js
Implemented Qwen installer script with git clone/pull logic and symlink creation; added installQwen() function and --qwen flag support to CLI; introduced detectQwen() platform detector checking for qwen command and configuring metadata.
Project Templates
templates/QWEN.md, templates/QWEN-append.md
Added Markdown templates providing Qwen-specific skill invocation guidance, quality-gate requirements, and development conventions.
Package Distribution
package.json
Updated metadata to include Qwen assets and keywords for distribution and discoverability.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Qwen arrives with skills to share,
Metaswarm orchestrates with care,
Symlinks dance in .qwen/skills,
Multi-agents climb the development hills! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: add Qwen Code CLI support' directly and clearly summarizes the main change: adding support for Qwen Code CLI across the metaswarm framework.
Description check ✅ Passed The description is well-related to the changeset, providing a structured bullet-point summary of all major changes including new files, CLI updates, platform detection, and documentation updates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented Mar 29, 2026

Copy link
Copy Markdown

Greptile Summary

This 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:

  • installQwen() is a verbatim copy of installCodex() (cli/metaswarm.js lines 125–179). The 55-line body differs only in env var name and directory paths. This violates the Open/Closed Principle — adding a new platform should extend an abstraction, not duplicate code. A shared installSkillBased(config) helper would eliminate the duplication and make adding future platforms trivial.
  • Stale fallback message in lib/platform-detect.js (line 129): the CLI-mode block's "no tools found" message still reads Install one of: claude, codex, gemini and omits qwen, giving users incorrect guidance when running node lib/platform-detect.js directly.
  • QWEN.md Requirements section (line 532) omits Qwen Code CLI from its own "One of:" list, which is directly misleading to the users this file targets.

Confidence Score: 4/5

Safe to merge after fixing the stale platform message in lib/platform-detect.js and the missing Qwen entry in QWEN.md; the code-duplication issue is a quality concern but not a blocker.

One P1 defect (stale platform list in the direct-CLI path of platform-detect.js actively misleads users who run that script) keeps this below 5. The two P2 issues (duplication and doc copy error) are easy fixes but not merge-blockers on their own. All other files are clean.

lib/platform-detect.js (stale message) and cli/metaswarm.js (code duplication) need the most attention.

Important Files Changed

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)

  1. lib/platform-detect.js, line 129-130 (link)

    P1 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 geminiqwen is 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.
  2. QWEN.md, line 532 (link)

    P2 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.

    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

Comment thread cli/metaswarm.js
Comment on lines +125 to +179
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');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 03ce5da and 4802a68.

📒 Files selected for processing (11)
  • .qwen/README.md
  • .qwen/install.sh
  • AGENTS.md
  • GETTING_STARTED.md
  • QWEN.md
  • README.md
  • cli/metaswarm.js
  • lib/platform-detect.js
  • package.json
  • templates/QWEN-append.md
  • templates/QWEN.md

Comment thread .qwen/install.sh
Comment on lines +12 to +14
INSTALL_DIR="${QWEN_HOME:-$HOME/.qwen}/metaswarm"
SKILLS_DIR="$HOME/.qwen/skills"
REPO_URL="https://github.com/dsifry/metaswarm.git"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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).

Comment thread .qwen/install.sh
Comment on lines +24 to +25
cd "$INSTALL_DIR"
git pull --rebase origin main 2>/dev/null || {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Comment thread .qwen/install.sh
Comment on lines +48 to +57
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))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
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.

Comment thread cli/metaswarm.js
Comment on lines +125 to +179
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');
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.

Comment thread lib/platform-detect.js
Comment on lines +87 to +103
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',
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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.sh

Repository: 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.sh

Repository: 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 -20

Repository: 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.

Comment thread QWEN.md
Comment on lines +72 to +84
| 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 |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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 English
As per coding guidelines, "Invoke skills using the `$name` syntax as defined in SKILL.md `name` field."

Also 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.

Comment thread QWEN.md

## Requirements

- One of: Claude Code, Gemini CLI, or Codex CLI

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
- 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant