Find a file
Shaojin Wen 1cacbb1bcc
ci(shepherd): add Fleet Shepherd — automated unblocking of the bot-PR fleet (#7142)
* ci(shepherd): add Fleet Shepherd — automated unblocking of the bot-PR fleet

A scheduled janitor that applies, per open bot PR, the smallest lever that
unblocks it — each lever was validated by hand across the fleet before being
automated here:

- conflict → dispatch the autofix loop for that PR (its --conflict path
  merges base and resolves), deduped per conflicted head SHA via a marker
  comment, with a bilingual notice on the PR
- stale base → update-branch when ≥25 commits behind main (fresh CI signal
  and propagates workflow/skill fixes to the branch tree; self-limiting
  because behind_by resets to zero after the sync), never while checks run
- known-flake red → rerun failed jobs only when EVERY failing test parsed
  from the job log matches .github/known-flakes.txt (one unknown failure
  blocks the rerun), only after the run fully concluded, max 2 attempts per
  run id; the registry seeds the three repeatedly-observed flakes
- scan liveness → if no autofix full scan ran in 60 minutes and none is in
  flight, dispatch one (GitHub cron was observed silent for 16h on a */10
  schedule)

A single "Fleet Shepherd Dashboard" issue is edited in place each tick for
observability. Safety rails: bot-authored in-repo main-targeting PRs only,
per-tick action caps (3 syncs / 2 dispatches), dry-run dispatch input,
FLEET_SHEPHERD_DISABLED repository-variable kill switch, PAT identity
verified before any write; dispatches/reruns ride the workflow token while
comments/update-branch/dashboard use the bot PAT so synced branches still
trigger CI. Contract test (8 cases) pins triggers, scoping, credential
split, idempotency markers, caps, liveness, dashboard, and validates every
registry line as a compiling test-file regex.

* ci(shepherd): review fixes — gate reruns across ALL failed jobs, behavioral tests

Addresses the /review findings:

- The flake gate now enumerates EVERY failed job of the run via the jobs API
  and parses failing tests from all their logs before deciding — matching the
  header's "every failing test" promise, since `gh run rerun --failed` reruns
  them all. A flaky Ubuntu test can no longer green-light a run that also has
  a genuine Windows failure. Failed jobs exposing no parseable failing tests
  (e.g. a coverage comment failing downstream of the test job) neither allow
  nor block on their own — documented tradeoff. This also removes the URL
  job-id parse entirely, mooting the /job-vs-/jobs concern (for the record:
  statusCheckRollup detailsUrl is the web URL and uses singular /job/, as the
  campaign's live extractions confirmed — but not parsing URLs is better).
- Behavioral tests: the log parser and the flake-gate pipeline are extracted
  VERBATIM from the workflow (the test fails if either drifts) and executed
  under bash — parser fixture → exact file list; gate matrix all-known→RERUN,
  mixed→BLOCK, unknown→BLOCK, empty→SKIP. 10/10.

* ci(shepherd): review round 2 — atomic markers, dashboard lookup fix, hardened plumbing

Two criticals and the standing suggestions from review:

- act() now propagates the wrapped command's real exit status (still set -e
  safe at the if-wrapped call sites), and every dedup marker is posted ONLY
  when its primary action succeeded — a transient dispatch/rerun failure no
  longer plants a marker that freezes the PR at that head or burns phantom
  rerun attempts.
- Dashboard lookup used `--jq --arg`, which gh does not support (single
  expression only) — DASH_NUM was always empty, creating a new dashboard
  issue every tick. Fixed to an exact-title search with a plain expression.
- Scan-liveness signal now counts SCHEDULE runs plus the shepherd's own
  liveness dispatches (recorded as a watermark in the dashboard body), so a
  conflict dispatch — also a workflow_dispatch — can no longer satisfy the
  watchdog and silently starve full scans; one run-list call now feeds both
  the age and in-flight computations.
- Fleet metadata comes from ONE gh pr list call (no N+1 pr view loop);
  actions reads (run state, jobs, logs) ride the workflow token whose
  actions scope is guaranteed; update-branch passes expected_head_sha as a
  compare-and-swap against races with the loop's own pushes; PENDING now
  counts WAITING/REQUESTED; RUN_ID extraction is pipefail-guarded; the
  registry filter is a trichotomy — rc≥2 (invalid pattern) BLOCKS reruns
  instead of falling through to the rerun branch.
- Behavioral replay extended: invalid-ERE registry → ERRBLOCK (fail-safe
  direction proven under bash). 10/10.

* ci(shepherd): review round 3 — cede rerun ownership to CI Failure Patrol; consumer-parity gates

The decisive review finding (raised independently by two reviewers): the
flake-rerun lever created a SECOND scheduled owner for the same failed CI
runs, unserialized against the existing Qwen CI Failure Patrol
(qwen-ci-flaky-rerun.yml) — exactly the rerun-vs-rerun race class observed
live during the fleet campaign. Single-owner principle applied: the lever,
its registry (.github/known-flakes.txt), and its tests are removed; the
shepherd now only REPORTS red CI on the dashboard, and the header documents
the non-goal. This also moots the sibling findings against the lever's
internals (stale first-FAILURE selection, unparseable-job neutrality, missing
fleet-wide rerun budget).

Remaining findings fixed:
- Conflict dispatch now mirrors its consumer's predicate: the autofix scan
  skips PRs with checks in flight, so the shepherd defers the dispatch (and
  its dedup marker) until checks are quiet instead of wasting the dispatch
  and freezing the head behind a marker for a scan that never ran.
- A failed marker read now SKIPS the PR for the tick — an empty comment
  history must never masquerade as "no markers" and double-fire actions.
- New behavioral test: act() is extracted verbatim from the workflow and run
  under bash, proving a failing primary action returns nonzero (marker
  skipped) and a succeeding one returns zero (marker posted). 9/9.

* ci(shepherd): honest budget accounting on marker failure; dry-run behavioral proof

Review round 4 (two suggestions):

- A successful dispatch now counts against MAX_DISPATCHES_PER_TICK the moment
  it happens, with the marker posted afterwards; if the marker post fails the
  note says so honestly and the possible next-tick re-dispatch is absorbed by
  downstream dedup (the autofix scan's busy-PR skip and nothing-new gate).
  The reviewer's ordering — counting only after a successful marker — would
  let a marker outage overspend the per-tick dispatch budget.
- The act() behavioral replay now also proves the dry-run branch: with
  DRY_RUN=true and `false` as the primary command, act() returns 0 without
  executing it (DRY-OK), so an inverted condition or dropped return can no
  longer pass on string presence alone. 9/9.

* ci(shepherd): scope-named conflict cap, wider liveness window, no empty-fleet overwrite

Review body-level confirms addressed:

- MAX_DISPATCHES_PER_TICK renamed to MAX_CONFLICT_DISPATCHES_PER_TICK: the
  cap budgets conflict dispatches only, BY DESIGN — the liveness dispatch is
  separately self-limited (dashboard watermark + 60-minute age + in-flight
  gate ⇒ at most one per tick and usually one per hour), and sharing one
  budget would let a liveness fire starve conflict handling. The name now
  states the scope instead of inviting the bypass reading.
- Liveness run-list window widened to 50 so a review-event storm can't push
  schedule runs or an in-flight scan out of view; the residual worst case
  (one unnecessary dispatch) stays bounded by the watermark and the autofix
  scan's own busy/nothing-new gates.
- A failed fleet enumeration now skips the walk AND the dashboard update —
  the previous dashboard body is preserved instead of being overwritten with
  a misleading empty table. 9/9.

* test(shepherd): rename to qwen-fleet-shepherd-workflow.test.js per convention

Every other workflow contract test in scripts/tests/ carries the -workflow
suffix; match it so workflow-test globs include this file.

* ci(shepherd): if-wrap every act(), exact-title dashboard, drop vestigial checkout

Review round 7:

- act() propagates real exit codes, so under the runner's set -eo pipefail a
  BARE failing call aborts the whole tick — the reviewer's sharpened framing
  of the marker-call issue (that call was already if-wrapped in the previous
  round; the residue was the two dashboard writes). Both are now if-wrapped
  with retry-next-tick warnings, and the test asserts no bare act call
  remains anywhere.
- Dashboard lookup: exact-title equality via standalone jq --arg (in:title is
  a substring search — a bystander issue merely containing the title must
  never be hijacked and overwritten), and a FAILED lookup now skips the
  dashboard for the tick instead of minting a duplicate via create-on-failure
  (same fail-closed rule as the fleet and marker reads).
- Removed the vestigial checkout step: with the rerun lever ceded to the
  Patrol, the run step reads no repo files — every read goes through the API.
  10/10.

* ci(shepherd): fail-closed run snapshot, liveness-scoped in-flight, shepherd-own busy-set

Review round: three Criticals against the run-snapshot section.

- A failed autofix run-list read sets SCAN_RUNS_OK=false and skips the
  liveness lever AND every conflict dispatch for the tick, instead of
  falling back to '[]' — an empty snapshot both zeroed the in-flight
  count and blanked the schedule signal, so the watchdog could stack a
  duplicate scan on top of a live one.
- SCAN_INFLIGHT counts only SCHEDULE runs plus workflow_dispatch runs
  whose createdAt sits within 180s of our recorded liveness watermark.
  Forced conflict dispatches can hold a run in progress for up to two
  hours; counting them starved the watchdog and froze full scans exactly
  when the fleet was busiest.
- The shepherd builds its OWN busy-set from live runs' review-address
  matrix jobs and defers a conflict dispatch while one is live or
  queued (unknown snapshot => every dispatch deferred). The
  marker-failure retry note claimed 'downstream dedup' that lives in an
  unmerged PR; the dedup now travels with this workflow.

Contract tests: 11/11, including a verbatim-extracted jq replay proving
a foreign forced dispatch (created far from the liveness watermark)
neither satisfies nor starves the watchdog.

* ci(shepherd): declare set -eo pipefail explicitly

The act() contract, every if-wrapper, and the behavioral replay all
assume strict-mode semantics. Actions' default bash gives -e (bash -e
{0}) but NOT pipefail — declare both so production matches the tested
contract and a future bare act() call fails loudly instead of silently
posting its marker. The one real pipeline in the tick (the liveness
watermark grep) already carries || true.

* ci(shepherd): platform-blind CI-red on the dashboard; document first-tick edge

Review notes (non-blocking, adopted): the dashboard's ci-red state
matched only 'Test (ubuntu' — a Windows- or macOS-only regression
stayed invisible on the health view. Widen to any Test platform
(reruns stay with the Patrol either way) and pin it. Also record the
first-tick liveness edge at the in-flight computation: with no
watermark yet a manual dispatch in flight is not counted and one
duplicate scan may go out; the scan's own busy/nothing-new gates
absorb it, and every later tick has the watermark.

* ci(shepherd): unknown watermark and partial busy-set fail closed

Review round: two remote-read failure paths still turned unknown state
into actionable empty state.

- The liveness watermark lives in the dashboard body, so a failed body
  read (not just a failed lookup) now flips DASH_LOOKUP_OK: the
  dashboard is not overwritten (that would destroy the stored
  watermark) and the liveness lever is skipped — otherwise, with cron
  already stale, a PAT outage would dispatch a duplicate full scan
  every tick for its duration. The lever now requires known watermark
  state, a good run snapshot, expired age, and zero in-flight.
- Every busy-set jobs read is tracked: BUSY_OK inherits SCAN_RUNS_OK
  and flips false on the first failed gh run view or jq parse, because
  a partial enumeration is unknown busy-state, not a smaller busy-set
  (a gh failure inside process substitution never trips the parent
  shell). Conflict dispatches now gate on BUSY_OK.

New behavioral replay (PATH-stubbed gh over the verbatim-extracted
walk): failed jobs read → BUSY_OK=false with an empty set; successful
read → the queued review-address PR lands in the busy-set; no live
runs → known-empty set with dispatches enabled. 12/12.

* ci(shepherd): fetch markers only for conflicting PRs

Review suggestion, adopted: MY_MARKS was fetched (paginated) for every
PR in the fleet but consumed only by the conflict lever's dedup —
~100 wasted comments fetches per tick on a 50-PR fleet — and a failed
fetch on a NON-conflicting PR dropped the PR from the dashboard,
stale-base sync, and CI-red reporting via the shared continue. The
read now lives inside the CONFLICTING branch behind MARKS_OK: a failed
read defers just the conflict dispatch (empty history still never
masquerades as no markers), and every other lever keeps working.

* ci(shepherd): link the dashboard ci-red state to the failing job

Review suggestion, adopted: FAILED_TEST_URL was extracted per PR and
then only tested for non-emptiness — the dashboard said 'ci red' with
no way to reach the failure, forcing exactly the per-PR navigation the
dashboard exists to eliminate. The state cell is now a markdown link
to the failing Test job.

* ci(shepherd): attribute in-flight liveness by run id, not timestamp proximity

Review round: the ±180s attribution window necessarily miscounted a
conflict dispatch fired later in the SAME tick — created seconds from
the liveness watermark, its two-hour address run then suppressed the
age>=60m dispatch on every later tick, the exact starvation the header
claims to prevent. And the behavioral test only exercised a foreign
dispatch 40 minutes outside the window, masking the reachable case.

The liveness dispatch now records its run id: captured right after the
dispatch (a correlation window in which no other dispatch can exist —
conflict dispatches fire later in the walk), persisted in the
dashboard marker as 'run=<id>', and matched by id equality next tick.
No proximity window exists at all. Unknown id (capture failure or a
pre-id marker) attributes nothing, so the failure mode is one absorbed
duplicate scan — never starvation. The replay now proves the
reviewer's in-window case: a conflict dispatch created 5s after the
watermark counts 0, and our completed run plus that live conflict run
also counts 0. 12/12.

---------

Co-authored-by: wenshao <wenshao@example.com>
2026-07-18 13:02:30 +00:00
.github ci(shepherd): add Fleet Shepherd — automated unblocking of the bot-PR fleet (#7142) 2026-07-18 13:02:30 +00:00
.husky Sync upstream Gemini-CLI v0.8.2 (#838) 2025-10-23 09:27:04 +08:00
.qwen fix(cli): make cancelled prompts editable after streamed output (#7149) 2026-07-18 12:38:49 +00:00
.vscode Merge branch 'main' into feat/sandbox-config-improvements 2026-03-06 14:38:39 +08:00
docs feat(web-shell): git status chip, visual working-tree diff, and sidebar git status (#7054) 2026-07-18 10:06:07 +00:00
docs-site Hide internal docs from docs site (#4357) 2026-06-01 15:55:14 +08:00
eslint-rules pre-release commit 2025-07-22 23:26:01 +08:00
integration-tests feat(web-shell): add a workspace Goals page, and stop losing /goal on daemon resume (#6561) 2026-07-18 08:52:07 +00:00
packages fix(cli): make cancelled prompts editable after streamed output (#7149) 2026-07-18 12:38:49 +00:00
patches feat(cli): mouse text selection and copy in VP mode (#6937) 2026-07-17 08:04:07 +00:00
scripts ci(shepherd): add Fleet Shepherd — automated unblocking of the bot-PR fleet (#7142) 2026-07-18 13:02:30 +00:00
.dockerignore fix(cli): skip stdin read for ACP mode 2026-03-27 11:47:01 +00:00
.editorconfig pre-release commit 2025-07-22 23:26:01 +08:00
.gitattributes feat(installer): add standalone hosted install and uninstall flow (#3828) 2026-05-21 11:57:10 +08:00
.gitignore feat(web-shell): auto-post visual previews (screenshots + flow GIFs) on PRs (#6880) 2026-07-15 06:48:52 +00:00
.npmrc chore: remove google registry 2025-08-08 20:45:54 +08:00
.nvmrc chore(deps): upgrade ink 6.2.3 → 7.0.2 + bump Node engine to 22 (#3860) 2026-05-11 17:29:50 +08:00
.prettierignore feat(acp): support /cd command in ACP sessions (#5903) 2026-06-27 14:47:40 +00:00
.prettierrc.json pre-release commit 2025-07-22 23:26:01 +08:00
.yamllint.yml feat(desktop): Add desktop app package with Qwen ACP SDK integration (#3778) 2026-06-11 21:57:20 +08:00
AGENTS.md fix(serve): Harden multi-workspace ownership guards (#7005) 2026-07-16 17:29:10 +00:00
CHANGELOG.md chore(release): v0.19.11 (#7042) 2026-07-16 15:23:33 +00:00
CLAUDE.md docs: rewrite CLAUDE.md to point to AGENTS.md as authoritative source (#5138) 2026-06-15 15:23:26 +08:00
CONTRIBUTING.md revert: remove local PR verification gate (#7031) 2026-07-16 11:24:38 +00:00
Dockerfile chore(deps): upgrade ink 6.2.3 → 7.0.2 + bump Node engine to 22 (#3860) 2026-05-11 17:29:50 +08:00
esbuild.config.js fix(cli): add bootstrap fast paths (#6188) 2026-07-02 22:28:11 +00:00
eslint.config.js Add harness infrastructure for web-shell package (#6517) 2026-07-09 08:11:58 +00:00
eslint.legacy-filenames.mjs feat(serve): add workspace persisted transcript reader (#6740) 2026-07-12 10:39:05 +00:00
LICENSE Sync upstream Gemini-CLI v0.8.2 (#838) 2025-10-23 09:27:04 +08:00
Makefile feat: update docs 2025-12-22 21:11:33 +08:00
package-lock.json feat(web-shell): add a workspace Goals page, and stop losing /goal on daemon resume (#6561) 2026-07-18 08:52:07 +00:00
package.json chore(release): v0.19.11 (#7042) 2026-07-16 15:23:33 +00:00
README.md docs: Revamp README for clarity and focus (#5257) 2026-06-18 10:27:16 +08:00
SECURITY.md fix: update security vulnerability reporting channel 2026-02-24 14:22:47 +08:00
tsconfig.json # 🚀 Sync Gemini CLI v0.2.1 - Major Feature Update (#483) 2025-09-01 14:48:55 +08:00
vitest.config.ts feat(channel): add QQ Bot (QQ机器人) channel adapter (#5202) 2026-06-19 06:32:52 +08:00

npm version License Node.js Version Downloads

QwenLM%2Fqwen-code | Trendshift

The open-source AI coding agent that lives in your terminal.

中文 | Deutsch | français | 日本語 | Русский | Português (Brasil)

Why Qwen Code?

  • Agentic out of the box — Auto-Memory, Auto-Skills, SubAgents, Agent Teams, and MCP. Dynamic workflows, zero setup.
  • Open-source, inside and out — The framework and the Qwen models are open-source. They evolve together. No vendor lock-in.
  • Multi-protocol — Supports OpenAI, Anthropic, Gemini, and Qwen APIs. Any third-party provider or local model (Ollama / vLLM). Switch at runtime.
  • Beyond the terminal — IDE plugins, Desktop app, daemon mode, SDKs, and IM bots (Telegram / DingTalk / WeChat / Feishu).

Tip

Qwen Code is actively iterating on itself — using its own agent and models to file issues, submit PRs, review code, and run tests. Powered by the community, driven by AI.

Installation

Linux / macOS:

curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen-standalone.sh | bash

Windows:

irm https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen-standalone.ps1 | iex

Restart your terminal after installation to ensure environment variables take effect.

NPM / Homebrew

NPM (requires Node.js 22+):

npm install -g @qwen-code/qwen-code@latest

Homebrew (macOS / Linux):

brew install qwen-code

Quick Start

qwen          # Launch interactive terminal UI
# Inside the session:
/auth         # Configure your provider and API key

See the Authentication Guide and Settings Reference for detailed setup.

Qwen Code

How to Use Qwen Code

Mode Command Use Case
Interactive qwen Terminal UI with rich rendering, @file references, slash commands
Headless qwen -p "..." Scripts, CI/CD, batch processing — no UI
IDE VS Code, Zed, JetBrains
Desktop Qwen Code Desktop — GUI for macOS, Windows, Linux
Daemon qwen serve Shared agent session over HTTP+SSE (ACP). Multiple clients, one agent. (experimental) Docs
SDK TypeScript, Python, Java
IM Bot qwen channel Connect to Telegram, DingTalk, WeChat, or Feishu
SDK example (Python)
import asyncio

from qwen_code_sdk import is_sdk_result_message, query


async def main() -> None:
    result = query(
        "Summarize the repository layout.",
        {
            "cwd": "/path/to/project",
            "path_to_qwen_executable": "qwen",
        },
    )

    async for message in result:
        if is_sdk_result_message(message):
            print(message["result"])


asyncio.run(main())

Capabilities

If you know Claude Code, you already know Qwen Code — and then some. We've put significant effort into bringing Qwen Code to feature parity with Claude Code, improving both breadth and reliability across the board.

Feature Qwen Code Claude Code
SubAgents, Agent Teams, Dynamic Workflows
Auto-Memory, Auto-Skills, Hooks
Built-in Skills (/review, /batch, /loop, /bugfix…)
MCP, Plan Mode, LSP Integration
Auto Mode, Sandbox, Git Worktrees
Computer Use (desktop automation)
IDE Plugins (VS Code / JetBrains / Zed)
SDK
Headless Mode, Session Management
Open-source — model and framework
Multi-protocol (OpenAI / Anthropic / Gemini / Qwen + any provider)
Agent Arena (multi-model head-to-head on same task)
Daemon Mode — qwen serve (multi-client shared agent)
IM Channels (Telegram / DingTalk / WeChat / Feishu)

Ecosystem

  • Qwen Code Desktop — Official desktop app for macOS, Windows, and Linux

  • AionUi — A modern GUI for command-line AI tools including Qwen Code

  • Gemini CLI Desktop — A cross-platform desktop/web/mobile UI for Qwen Code

  • 🦞 Qwen Code Claw — Let other agents (Claude, Codex, etc.) delegate coding tasks to Qwen Code via ACP. Paste this prompt into your agent:

Use Qwen Code as a coding agent. First, fetch and read this skill reference:
https://raw.githubusercontent.com/QwenLM/qwen-code/refs/heads/main/.qwen/skills/qwen-code-claw/SKILL.md
Then follow the instructions inside to install, authenticate, and use Qwen Code via acpx for all coding tasks.

Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines.

Acknowledgments

This project was originally based on Google Gemini CLI v0.8.2. We gratefully acknowledge the Gemini CLI team's excellent work. Starting from Qwen Code v0.1, we stopped syncing with upstream and began independent development as a multi-protocol, multi-platform agent framework with deep integrations for Qwen models and beyond.