Find a file
Shaojin Wen 6b2f800abd
Some checks are pending
Qwen Code CI / Classify PR (push) Waiting to run
Qwen Code CI / Lint (push) Blocked by required conditions
Qwen Code CI / Test (macos-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (ubuntu-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Test (windows-latest, Node 22.x) (push) Blocked by required conditions
Qwen Code CI / Post Coverage Comment (push) Blocked by required conditions
Qwen Code CI / CodeQL (push) Blocked by required conditions
Qwen Code CI / Integration Tests (CLI, No Sandbox) (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:docker (push) Waiting to run
E2E Tests / E2E Test (Linux) - sandbox:none (push) Waiting to run
E2E Tests / E2E Test - macOS (push) Waiting to run
perf(core): read current git branch directly from .git instead of spawning git (#5432)
* perf(core): read current git branch directly from .git instead of spawning git

Reading the current branch for the CLI status line previously shelled out to `git rev-parse --abbrev-ref HEAD` on the render path. Replace it with a direct read of .git/HEAD.

- Add shared module packages/core/src/utils/gitDirect.ts: resolveBranchName, readGitHead, a reference-counted reflog watcher (watchRepoBranch), ref-name validation (isValidRefName), and a gitDir resolution cache. gitDir resolution reuses the existing gitDiff.resolveGitDir (ancestor walk + worktree gitdir pointer); only HEAD is re-read on reflog changes.
- Rewrite useGitBranchName as a thin wrapper over the shared module; the (cwd) => string | undefined hook signature is unchanged.

* fix(core): handle fs.FSWatcher 'error' and don't cache a watcher-less entry

Addresses review feedback on the reflog watcher:

- Add an 'error' handler to the reflog fs.FSWatcher. fs.FSWatcher is an EventEmitter, so an unhandled 'error' (reflog removed by `git gc`/`reflog expire`, worktree removal, inode change, or a platform watch limit) would crash the process. The watch is now torn down instead, and subscribers simply stop auto-refreshing.
- When there is no reflog yet (unborn repo), return a no-op disposer without caching a watcher-less entry, so a later caller can establish the watch once the reflog appears (e.g. after the first commit).
- Tests: FSWatcher 'error' teardown, reflog-appears-later re-watch, concurrent-caller dedup (post-await re-check), and the hook's [cwd]-change re-subscribe path.

* fix(core): harden gitDirect against out-of-repo reads and fs.watch throws

Addresses security/robustness review:

- Containment guard: resolveTrustedGitDir rejects a .git-FILE `gitdir:` pointer that escapes the repo. After realpath, the resolved gitDir must be the repo's own `<root>/.git`, or live under some `.git/worktrees/` (linked worktree) or `.git/modules/` (submodule). This stops a crafted project from making the status line read/watch an arbitrary out-of-repo path (the old `git rev-parse` path refused such repos with exit 128).
- Refuse a symlinked HEAD (readGitHead) and a symlinked reflog (watchRepoBranch) via lstat, so neither follows a link out of the repo.
- Wrap fs.watch in try/catch: a TOCTOU vanish (git gc / reflog expire / worktree removal) or a platform watch limit makes fs.watch throw synchronously; return a no-op disposer rather than rejecting (which the hook's bare `void init()` would surface as an unhandled rejection). The existing 'error' listener only covers async emitter errors.
- Hook: guard `void init()` with .catch() as belt-and-suspenders.
- Tests: containment (decoy rejected, submodule accepted), symlink HEAD/reflog refused, fs.watch sync-throw no-op, and the hook still rendering when watch setup rejects.

* fix(core): validate the git object store instead of path-shape containment

Replaces the path-segment containment guard — which qqqys showed a crafted `.git/worktrees/x` path could spoof — with git's own validity check: a trusted gitDir must have an object store. A standalone repo has `objects/` + `refs/` directly; a linked worktree / submodule gitdir instead carries a `commondir` file pointing at the main gitdir that does. Incomplete forgeries (a lone HEAD, or a path-shaped `.git/worktrees/x` containing only a HEAD) have neither and are rejected — exactly what `git rev-parse` rejects with 'not a git repository' (exit 128). Verified the criterion against real git across standalone / unborn / worktree / fake structures.

- Addresses qqqys (path-shape `.git/worktrees/fake` with only HEAD) and yiliang114 (fake `.git` dir with only HEAD+logs) — both now return undefined.
- Tests: both PoCs rejected; worktree (via commondir) and submodule (own object store) accepted; makeRepo now creates objects/ + refs/ so fixtures are real git dirs.

* fix(core): harden ref-name gate (C1/length) and stop caching non-repo misses

- isValidRefName: also reject C1 controls (0x80-0x9f) and U+2028/U+2029, and cap length at 255. With git no longer vetting the value, a hand-written HEAD could otherwise carry terminal escape bytes (CSI/OSC) or layout-desyncing line separators into the status line, and string-width undercounts C1.
- getCachedGitDir: cache only successful resolutions. A null (non-repo) result was cached permanently, so a directory that became a repo mid-session (git init / clone) never showed a branch until restart — a regression from the old git rev-parse path which always hit the filesystem.

* fix(core): isolate subscriber callbacks in the shared reflog watcher

In the shared-watcher fan-out (one fs.watch per gitDir, many subscribers), a subscriber whose onChange throws synchronously would halt iteration — later subscribers never fire — and the exception would escape to the event loop as uncaughtException, poisoning every component watching that repo. Wrap each callback in try/catch. The sole current caller (useGitBranchName) can't throw, but watchRepoBranch is exported public API.

* fix(core): close readGitHead symlink TOCTOU with O_NOFOLLOW; per-component ref rules

Addresses review:
- readGitHead: open HEAD with O_NOFOLLOW instead of lstat-then-readFile, so a symlinked HEAD is refused atomically (ELOOP) and can't be swapped in during the check->read gap. Mirrors the existing O_NOFOLLOW use in gitDiff.ts; falls back to plain O_RDONLY where the flag is absent (Windows).
- isValidRefName: also reject names where any slash-separated component starts with a dot or ends with .lock (git's check-ref-format applies per component, not just to the whole name).
- hasGitStore: run the two isDir probes in parallel.
- Reword the module doc (drop the cross-product reference) and note the residual, bounded lstat->watch TOCTOU on logs/HEAD (the watch only ever fires readGitHead, which opens HEAD with O_NOFOLLOW, and never reads logs/HEAD content).

* fix(core): clear watchers in clearGitDirCache; debug-log watcher failures; guard refresh

- clearGitDirCache now also closes the shared reflog watchers. Both maps are gitDir-keyed, so clearing only the resolution cache would leak the watchers' fds.
- Add a debug logger and warn on the unexpected paths (fs.watch synchronous throw, FSWatcher 'error'). The common silent fallbacks (not a repo, no HEAD) stay quiet so a status-line read can't log-spam.
- useGitBranchName: guard the watcher-triggered void refresh() with .catch() — the synchronous try/catch inside watchRepoBranch can't observe an async rejection.

* fix(core): bound the HEAD read, cap ref length per-component, guard fs.constants

- readGitHead: read a bounded 4 KB prefix and parse only the first line instead of loading the whole file — a pathologically large HEAD can no longer be read into memory.
- isValidRefName: the length cap is now per slash-separated component (git's actual filesystem limit), not the whole ref — a valid deeply-nested ref longer than 255 total is no longer wrongly rejected.
- Access fs.constants via optional chaining (O_RDONLY/O_NOFOLLOW/F_OK), matching gitDiff.ts, so a mock or platform without `constants` can't throw.

* fix(core): bound/O_NOFOLLOW the commondir read; block bidi/zero-width + more ref rules

- Share a readFirstLineNoFollow helper between HEAD and commondir, so the commondir read is now also bounded (4 KB) + O_NOFOLLOW — a crafted oversized or symlinked commondir can no longer OOM the status-line path or redirect the validity check out of the repo.
- isValidRefName also rejects: bidi-override (U+202A-202E, U+2066-2069) and zero-width (U+200B-200D, U+FEFF) characters (display spoofing); a slash-separated component ending in a dot (git check-ref-format); and the literal name 'HEAD' (ambiguous with a detached HEAD, which git rejects as a branch).

* fix(core): O_NONBLOCK against FIFO hangs, swallow close errors, test commondir symlink

- readFirstLineNoFollow: open with O_NONBLOCK so a crafted FIFO .git/HEAD or commondir can't block indefinitely and pin a libuv thread-pool slot (the old git rev-parse path had subprocess timeouts; the direct read had none). And `await fh.close().catch(() => {})` so a close error (EIO / stale NFS handle) can't escape — the helper promises null on any failure — matching gitDiff.ts / fileHistoryService.ts.
- Test: a symlinked commondir is refused via O_NOFOLLOW, closing the coverage gap alongside the existing symlinked HEAD and reflog tests.
2026-06-21 07:09:19 +08:00
.github ci(release): report required Test checks on release PRs and auto-approve (#5250) 2026-06-17 22:30:04 +08:00
.husky Sync upstream Gemini-CLI v0.8.2 (#838) 2025-10-23 09:27:04 +08:00
.qwen feat(skills): add desktop-pet skill for pixel-art companions (#4808) 2026-06-19 06:57:04 +08:00
.vscode Merge branch 'main' into feat/sandbox-config-improvements 2026-03-06 14:38:39 +08:00
docs feat(core): add Requesty provider (#5478) 2026-06-21 06:37:24 +08: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 refactor(tools): rename TodoWrite tool display name to TodoList (#5319) 2026-06-19 08:32:10 +08:00
packages perf(core): read current git branch directly from .git instead of spawning git (#5432) 2026-06-21 07:09:19 +08:00
patches fix(input): restore IME cursor positioning reverted in #4779 (#4993) 2026-06-19 08:00:26 +08:00
scripts feat(cli): serve the Web Shell UI from qwen serve (#5392) 2026-06-19 19:41:33 +08: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(cli): add persistent history collapse on resume with refined commands (#4085) 2026-06-19 19:22:48 +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(desktop): Add desktop app package with Qwen ACP SDK integration (#3778) 2026-06-11 21:57:20 +08: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 docs(agents,pr-template): add Working Principles and restructure PR template (#4496) 2026-05-25 19:15:35 +08:00
CHANGELOG.md chore(release): v0.18.3 [skip ci] 2026-06-18 00:28:48 +08: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 feat(installer): verify release assets + switch public docs to standalone entrypoint (#3855) 2026-06-04 17:23:04 +08: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 perf(filesearch): move AsyncFzf index construction to a worker thread (#4621) 2026-06-12 11:47:16 +08:00
eslint.config.js feat(web-shell): add extension management (#5398) 2026-06-20 08:37:22 +08: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(config): add settings file change detection via chokidar watcher (#3696) (#4933) 2026-06-19 15:00:44 +08:00
package.json fix(input): restore IME cursor positioning reverted in #4779 (#4993) 2026-06-19 08:00:26 +08: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.