← Discover MCPs and Agents
d
MCPAI & MLGitHub

dao-code

Open-source TypeScript terminal coding agent for DeepSeek-V4 — builds on DeepSeek's strong price-performance and ultra-cheap cache pricing, engineering byte-stable prefixes and cache-reusing forks so cross-session memory and a continuous self-correction layer add almost no token cost; 1M context, Skills/MCP/Hooks, Claude Code config compatible.

Links

README

From the repo.

Dao Code 道

English · 中文

CI License: MIT Node PRs welcome

A terminal coding agent built around cost, experience, and availability — squeezing the most capability and the lowest cost out of the high-value DeepSeek V4.

Dao Code demo

Dao Code (command dao) is a terminal-native AI coding assistant: it reads code, writes code, runs commands, and fixes bugs right in your terminal — streaming its reasoning and tool calls while executing safely behind an approval gate, until the task is done. It targets DeepSeek V4 (1M context), is Chinese-first, and is inspired by Claude Code — but takes a different road: rather than buying experience with an expensive model, it builds on DeepSeek's strong price-performance and ultra-cheap cache pricing, engineering byte-stable prefixes and cache-reusing forks so cross-session memory and a continuous self-correction layer add almost no token cost.


Why Dao Code?

🌐 Availability

A coding agent is only useful if you can actually run it.

  • Claude Code needs an Anthropic account and network access — a high bar to use out of the box in mainland China;
  • GLM's Coding Plan has scarce quota that's often hard to grab;
  • Dao Code is fully open source (MIT), and its base DeepSeek is register-and-go, pay-as-you-go, directly reachable in mainland China — no network gymnastics, no quota grabbing, no waitlist.

💰 Cost

  • Low unit price — DeepSeek sits at the lowest price tier among mainstream capable models; both input and output prices are far below the top-tier closed models.

  • Cache cuts it further — DeepSeek's prefix-cache hit price is ≈ 1/120 of a miss (~two orders of magnitude). Dao Code keeps the system prefix / tool table / memory byte-stable, and runs reflection & memory on cache-reusing forks, so the hit rate keeps climbing.

  • Measured on real OSS bug-fixes (not toy demos) — 7 SWE-bench-style tasks (valibot / date-fns / es-toolkit / sqlglot / hono), 3.89M input tokens total, 95.8% aggregate cache hit (85.4%–97.7% per task). At DeepSeek V4 Pro current pricing, a full feature (read + edit + test + self-review) runs ¥0.07–0.21, avg ¥0.15; all 7 total ¥1.07. Every figure traces back to evals/runs/<task>/run-1/agent.log; replay any time with /cost.

  • Cost vs Claude Code — pricing the same token trace of these 7 tasks under each vendor's official rates (and crediting Dao Code's high hit rate to Claude too, in its favor), total cost is still ~30× cheaper than Claude Opus 4.8, ~18× cheaper than Sonnet 4.6.

    Task (real OSS repo)Input tokHit %DeepSeek Provs Opusvs Sonnet
    t7-sqlglot-sqlite-autoinc1,218,38597.7%¥0.21337×22×
    t6-sqlglot-comment-on625,77296.3%¥0.14432×19×
    t9-hono-compress699,47996.0%¥0.20931×18×
    t8-hono-cookie-dup502,86694.9%¥0.13628×17×
    t4-estoolkit-omitby445,47594.4%¥0.15928×17×
    L1-nodeps-toolkit289,98993.2%¥0.14027×16×
    t5-estoolkit-uniqwith104,07185.4%¥0.06821×12×
    Total3,886,03795.8%¥1.0730×18×

    Prices as of 2026-06 official rates: DeepSeek V4 Pro hit/miss/output = $0.003625 / $0.435 / $0.87 per 1M; Claude Opus 4.8 = $5 / $25 (hits at 0.1× cache-read), Sonnet 4.6 = $3 / $15. Multiples are USD-on-USD, exchange-rate-independent; ¥ converted at ¥7.1/$. Cross-check: re-pricing L1 at current rates gives ¥0.140 ≈ the ¥0.136 the in-log /cost reported.

  • Verify the cache mechanism live — npm run accept:cache runs a multi-turn conversation against the live API so you can watch the hit rate climb from cold start to steady state (mechanism demo; cost figures above come from the real eval suite).

🧠 Experience

  • Memory you can trust + a reflection layer — remembers your preferences and project conventions across sessions, and re-verifies them against your live code on every startup: stale facts pruned, changed ones flagged, not blindly piled up (others remember, but misremember). Self-reviews when stuck and pulls back when drifting. All three run as forks that reuse the main prefix cache — better quality at almost no extra spend.
  • Long tasks don't drift or hit the wall — auto-compaction carries context past the limit, and periodic refocusing curbs scope creep, so it stays on track even running autonomously for a long time.
  • Constitution-style priority — safety & truth > your current instruction > Dao Code's core policy (model / cache discipline) > skills / memory. A third-party skill you install can change how work is done, but never the safety and cache bottom line.

✅ Verified

  • On a SWE-bench-style benchmark drawn from recent real-world open-source bug fixes (dual-track fail2pass + pass2pass judging, with test files hidden from the agent to prevent reward-hacking): 13/14 solved reliably.
  • On Terminal-Bench 2.1 (89 third-party agentic-coding tasks run via Harbor, spanning sysadmin, security, data science, scientific computing, debugging, ML, and more): 70/89 (78.7%) passed with deepseek-v4-pro at the benchmark's official 1× timeout. The interesting part isn't the score — it's that the iteration to get there was evaluation-driven engineering: reading real failure traces surfaced and fixed actual framework bugs, not prompt tuning. See below.

See Testing & evaluation.


✨ Features

🗜️ Context & cache engineering

A byte-stable system prefix rides DeepSeek's prefix cache to the max; reflection and memory run on forks that reuse the main cache (without breaking the prefix); near the limit it auto-compacts (reactive retry + in-place clearing of stale tool results + incremental summary + hard-truncate fallback if the summarizer fails); oversized output spills to disk, leaving only a pointer in context. /cost shows hit rate & spend; /audit cache pinpoints "what broke the cache" via a four-dimension fingerprint.

🧠 Cross-session memory (self-verifying)

At session end it distills your preferences, project conventions, and key facts; at startup it deterministically verifies them against the current code — stale ones dropped, changed ones flagged, rather than blindly piling up history. A decay GC clears dead memories; the model can memory_read on demand.

🔍 Reflection layer (self-correct when stuck / drifting)

Challenger: on a failure streak or recurring error, a skeptical independent review that questions the premise. Refocuser: every N turns on a long task, restate the original goal and catch scope creep. Reply-challenger: kicks in when you re-raise the same problem. All three run as cache-reusing forks — at almost no extra spend.

🪢 Long-task robustness

Session log + crash recovery (dao -c); shadow-git checkpoints (/restore /rewind, a separate snapshot that never touches your .git); todo list survives compaction to prevent goal drift; Definition-of-Done verification (/dod + verify_done); stuck detection with a circuit breaker; parallel / background / worktree-isolated subagents with two-way child↔parent messaging; --goal autonomous long-task mode.

🎐 A Taoist-aesthetic terminal experience

Rich Ink rendering + a Taiji splash + light/dark adaptation; @ file references, slash-command Tab completion, steering (type while a turn is running, queued), diffs with line numbers + syntax highlighting, thinking blocks, todo checklists, a Taoist-verb spinner; ESC interrupts (model stream and shell stop together); non-TTY auto-fallback to a plain-text REPL.

Basics (mirror CC, all shipped): 24 tools · layered allow/ask/deny permissions + auto smart approval + defense-in-depth (secret scanning / SSRF / sandbox / keychain) · Skills (incl. auto-adapting foreign skills' tool names & model tiers) · MCP (stdio + HTTP/SSE, tools/resources/prompts) · Hooks (5 lifecycle events) · custom subagents / slash commands / plugins · multi-account profiles (/account) · OS cron scheduling (/schedule). See Extension system and the tool overview below.


📦 Install

A. One-line install (no Node):

curl -fsSL https://raw.githubusercontent.com/tigicion/dao-code/master/install.sh | sh

Or download manually from Releases: macOS dao-darwin-arm64 (Apple silicon) / dao-darwin-x64 (Intel), Linux dao-linux-arm64/dao-linux-x64, Windows dao-windows-x64.exe. On Unix chmod +x then run; on Windows just double-click the .exe.

B. npm (Node ≥ 20, all platforms):

npx dao-code        # zero-install trial
npm i -g dao-code   # global install, command name dao

C. From source:

git clone https://github.com/tigicion/dao-code.git && cd dao-code
npm install && npm run build && npm link   # then dao is global
# or run directly in dev: npm run dev

🚀 Quick start

  1. Get a DeepSeek API key: https://platform.deepseek.com/api_keys

  2. Launch → follow the prompt to enter your key:

    dao                # installed (binary / global); or npx dao-code
    

    On first run with no key detected, it walks you through pasting one and saves it to ~/.dao/config.json (auto-read next time — no env setup needed).

  3. Or use headless one-shot with a key:

    dao --api-key sk-xxx --provider deepseek "tell me a joke"
    
  4. Light terminals: type /theme at runtime, or set DAO_THEME=light before launch.

Common slash commands (full list via /help):

CommandEffect
/initScan the repo and generate DAO.md (project overview/conventions, auto-loaded in future sessions)
/model [id]Switch model (no arg toggles deepseek-v4-pro / deepseek-v4-flash)
/mode [x]Permission mode default / auto (smart approval) / yolo (also Shift+Tab to cycle)
/planQuick toggle plan (read-only + propose) / normal
/goal <objective>Autonomous long-task mode (auto-approve + keep going; large tasks auto-staged)
/costToken usage & cache hit rate
/skillsList / toggle skills
/compactCompact the conversation · /clear clear · /help command list · /exit (also /quit) quit

Add --yolo at launch (e.g. dao --yolo / dao --yolo "task") to start in auto-approve; toggle anytime with /yolo. dao --verbose (or --debug) enters verbose mode at startup: full tool results, full thinking, and raw tool arguments. Plain dao truncates by default; at runtime press Ctrl+O to expand/collapse full output (mirrors CC). History already printed to the scroll region can't be edited in place, so expanding re-prints the most recent collapsed block once.


⌨️ Usage

Interactive mode (default):

dao
  • Type a message and press Enter; ↑/↓ browse history; Esc interrupts the current turn; lines starting with / are slash commands (with completion hints).
  • Inline editing: ←/→ move cursor, Ctrl-A/E line start/end, Ctrl-W delete word, Backspace/Delete delete at cursor; paste supported (no auto-submit).
  • @ to reference a file: type @ + a path fragment to list matches, Tab to complete.
  • Write/exec operations go through the approval gate ([y] once [a] remember (write allow rule) [n] deny); you can also pre-allow or block with allow/ask/deny rules in .dao/settings.json (see "Extension system · Permissions"); /yolo or --yolo auto-approves everything (deny still blocks).

One-shot mode (task as an argument, exits when done, no memory distillation, good for scripts):

dao "make formatDate in src/utils.ts timezone-aware"

🧠 How it works

You ──▶ Ink TUI ──▶ agent loop ──▶ DeepSeek V4
                       │  streamChat (streaming reasoning + answer)
                       │  ▶ model requests a tool call
                       │  ▶ approval gate (write/exec needs clearance)
                       │  ▶ run tool → feed result back
                       └─ loop until the model stops requesting tools
  • agent loop (src/agent/loop.ts): each turn calls streamChat to stream reasoning + answer; if the model requests a tool, it runs through the approval gate, feeds the result back, and loops until done or max turns; AbortSignal is threaded through the model stream and tools for ESC interrupts.
  • Modes: in plan mode, even a write/exec tool request is denied for the turn by the per-turn allow table (read-only + propose); normal mode runs as usual.
  • Memory (src/memory/): at startup migrate → load → deterministically verify against live code → inject into the fixed prefix; on exit distill new facts with the cheap flash model and upsert after dedup.
  • Cache & compaction: the system prefix is pinned to ride DeepSeek's prefix cache; near the 1M context limit early messages are auto-compacted into a summary.

🪢 Long-task robustness

Built for long tasks that "run autonomously for a long time without drifting, are recoverable, and can be verified":

  • Session log + crash recovery: each turn writes events to .dao/sessions/<id>/events.jsonl and a state snapshot to state.json; after a crash/abnormal exit, dao -c resumes the last session (src/session/log.ts).
  • Shadow git checkpoints: a separate .dao/shadow.git snapshots the working tree (never touches your .git / never rewrites your history); /restore reverts the last turn's changes in one step (src/session/checkpoint.ts).
  • Todo list survives compaction: the list maintained by todo_write is re-injected as a system message after compaction, preventing goal drift on long tasks.
  • Definition-of-Done verification: /dod <command> (or DAO_VERIFY_CMD) sets an executable acceptance command; verify_done runs it — only success (exit 0) counts as done; if unset, the model self-judges from evidence.
  • Stuck detection + circuit breaker: repeating the same tool call / hitting the same error past a threshold → first a nudge to change approach, then a stop, so it doesn't spin and burn budget (src/agent/stuck.ts).
  • Large output spilled to disk: when tool output exceeds a threshold it's spilled in full to .dao/spill/; the context keeps only a truncation + pointer, fetched back on demand via read_file.
  • Parallel / background subagents + notification queue: pass tasks[] to run in parallel, or background:true to run in the background (returns immediately, doesn't block the main loop); on completion the result is auto-injected as a <task-notification> to continue (src/agent/tasks.ts).
  • On-demand memory retrieval: memory_read lets the model actively retrieve cross-session memory (startup injects only top-K; truncated or just-written entries are still findable).
  • Long-task autonomous mode: dao --goal (legacy --task / --coordinator still accepted) or /goal <objective> at runtime — auto-approve + autonomous continuous progress + higher turn cap; large tasks auto-stage (parallel research → synthesize → implement → verify_done), asking you only when truly stuck.

🧩 Extension system

  • Permissions: three-state rules allow / ask / deny, syntax Tool(specifier) — Bash(npm run test:*) (command prefix), Edit(src/**)/Read(//etc/**) (gitignore-style path glob), WebFetch(domain:example.com), bare tool names, mcp__server__tool. Priority deny > dangerous commands > bypass > sensitive targets > ask > allow > mode/capability default (deny is a hard blacklist, blocking even under YOLO; dangerous commands like rm -rf / also always require confirmation).
    • Layering (low→high priority): ~/.dao/settings.json (user) < .dao/settings.json (project, committed) < .dao/settings.local.json (local, not committed) < CLI (--allow/--deny/--add-dir/--permission-mode) < enterprise managed policy (/etc/dao/managed-settings.json etc., not overridable by lower layers).
    • Compound commands checked per-segment: cd /tmp && rm -rf x is split on &&/||/;/|; any sub-command hitting deny blocks the whole line (no bypass).
    • Permission modes (/mode <x> or Shift+Tab to cycle; shown in the status bar): default (approve on demand) / auto (AI-classifier smart approval: read-only and in-workspace edits auto-pass, sensitive targets and uncertain calls go to the classifier, which blocks secrets like ~/.ssh/id_rsa and asks a human when unsure) / bypassPermissions (= YOLO, no approval except deny + dangerous commands; switchable in-session via /mode yolo or /yolo). plan (read-only planning, write/exec denied) is also a permission mode but not in the /mode cycle — enter via /plan, settings.defaultMode, or --permission-mode plan.
    • Four approval choices: [y] once / [s] this session / [a] remember (write an allow rule to .dao/settings.local.json) / [n] deny.
    • additionalDirectories: pre-authorized directories outside the workspace, read without prompting.
    • Engine: src/permissions/ (rules / identity / settings / engine / gate), with end-to-end tests.
  • Custom subagent types: .dao/agents/<name>.md (frontmatter: name/description/tools allowlist/model + body prompt). Pick with the agent tool's agent_type; each has its own role and tools.
  • Custom slash commands: .dao/commands/<name>.md (body is a prompt template, $ARGUMENTS/$1). /<name> args expands into a single turn.
  • Skills (ready-to-use skills): .dao/skills/<name>/SKILL.md. Progressive disclosure: startup lists only name+description; the model loads the body on demand via the skill tool.
  • Hooks (lifecycle hooks): .dao/hooks.json. PreToolUse (can block) / PostToolUse (e.g. auto-format) / UserPromptSubmit (inject context / block) / SessionStart / End.
  • MCP: .dao/mcp.json. Connects to stdio MCP servers; tools auto-register as mcp__<server>__<tool>.
  • Subagent orchestration: parallel tasks[], async background:true, isolate:true git-worktree isolation, task_send to append instructions to a running task, foreground timeout auto-converts to background, transcripts spilled to .dao/subagents/.
  • Steering: type during a running turn; Enter queues it, processed automatically once the current turn ends.

Compatible with Claude Code: settings.json, SKILL.md, hooks.json, and mcp.json use the same formats as CC (tool names auto-map, e.g. Bash↔exec_shell), so existing CC configs/skills work as-is.

🛠️ Tool overview

Registry in src/index.ts, implementations in src/tools/.

ToolEffect
read_fileRead a text file, returns numbered content (supports offset/limit)
list_dirList directory entries
write_fileCreate or wholesale-rewrite a file (must have read it before overwriting)
edit_file / multi_editExact string replacement (single / many at once)
notebook_editEdit Jupyter notebook cells
exec_shell (+_poll/_kill)Run shell; foreground/background (background=true), read output, terminate
grep_files / file_searchSearch by content regex / by filename glob
ask_userAsk the user one clarifying question and wait
fetch_url / web_searchFetch web page as text / DuckDuckGo web search
todo_writeMaintain a single-level task list (whole-table replace)
verify_doneRun the DoD acceptance command to decide if the task is complete
memory_write / memory_readRecord a cross-session memory / retrieve on demand
skill / skill_installLoad a skill body / install an external skill
agent / task_send / message_parentDispatch a subagent / append to a running one / child→parent reply
scheduleCreate an OS crontab scheduled task

🧪 Testing & evaluation

Unit tests (Vitest):

npm test          # run once
npm run test:watch
npm run typecheck

npm audit warnings all come from the dev test toolchain (vitest / vite / esbuild), are not shipped in the release artifact (dist), and don't affect the dao runtime; the critical one is a vitest --ui server vulnerability (unused by this project). CI: .github/workflows/ci.yml.

Agent end-to-end evaluation lives in evals/: SWE-bench-style, drawn from recent real open-source bug fixes, with dual-track fail2pass / pass2pass verification (after the fix the target test flips from fail to pass, and existing functional tests aren't broken); test files are hidden from the agent and injected only after the run, to prevent reward-hacking.

# evals use --api-key and --provider; set them in evals/run.mjs or pass via env
node evals/run.mjs            # default 3 runs per task, see pass^k reliability
EVAL_RUNS=1 node evals/run.mjs # smoke test

Evaluation makes real model calls and incurs cost; each task runs in a throwaway temp dir — set DAO_AUTO_APPROVE=1 for unattended runs. See evals/README.md.

Terminal-Bench 2.1 — evaluation-driven engineering

Terminal-Bench is a third-party benchmark of real terminal/coding tasks judged by hidden verifiers inside sandboxed containers, run here via Harbor. 89 tasks (4 easy / 55 medium / 30 hard) span system administration, security, data science, scientific computing, debugging, ML, and more. Dao Code passes 70/89 (78.7%) with deepseek-v4-pro, at the benchmark's official timeout (1×, never inflated to make a task easier).

The score is downstream of a closed loop, not "read the log, patch the code": diagnose → design → re-verify.

  • Diagnose — don't stop at the surface symptom ("0 tool calls in 900s"); establish why: the model didn't know what to do, knew but wouldn't, or tried and failed on the merits. Each calls for a different fix, and a fix only qualifies if it explains at least two tasks sharing the same root cause — single-task quirks don't count, to keep from overfitting the suite.
  • Design — pick an intervention at the strength the diagnosis actually earned, escalating only as far as the evidence demands: a text nudge before a hard runtime gate, never straight to the heaviest tool.
  • Re-verify — a real re-run against the benchmark, staying skeptical of "it looks like it worked": a mechanism firing correctly is not the same as the task passing. The loop isn't closed until the actual reward and the causal story both check out.

Case in point — the TodoWrite gate. Headless sessions kept reasoning for the full 15-minute budget without ever calling a tool. Diagnose: the system prompt already told the model, in the literal text it received (confirmed byte-for-byte in the transcript), to plan before acting — so the problem wasn't a missing instruction, it was that a soft text instruction carries no real weight with the model. Design: escalate from suggestion to enforcement — a runtime gate that hard-rejects a headless session's first tool-call batch unless it includes TodoWrite. Re-verify: a real re-run confirmed the gate does what it's supposed to (the model's first move is now TodoWrite) — but re-verification didn't stop there. On one task the gate fired correctly and reward was still 0, because the real blocker had moved to an unrelated memory-corruption bug in the model's own C code. Stopping at "the mechanism fired" would have logged a false win; the loop only closes once the reward and the causal chain agree.

The same diagnose-design-re-verify discipline caught things a prompt tweak alone never would: a background snapshot loop so a forced timeout no longer destroys its own diagnostic evidence; a native-build fix for a qemu-family task that was actually being killed by the host (a Rosetta 2 gap), not the model; and an explicit switch to disable web access in eval mode after it let a model fetch a leaked reference solution instead of solving the task.

The remaining failures are triaged into named root-cause categories (real engineering scope beyond the time budget, implementation bugs in an otherwise-correct approach, domain-insight gaps, spec/grader mismatches) rather than left as an unexplained score, and every environment modification that could affect comparability (e.g. a shortened password dictionary for one crypto task) is disclosed rather than folded silently into the pass rate. Full per-task results, the failure-mode journal, and the harness itself: evals/terminal-bench/.


⚙️ Configuration

VariableDescriptionDefault
—API key: run dao interactively to set via onboarding, or /account to manage—
DEEPSEEK_MODELDefault model (deprecated)deepseek-v4-pro
DAO_THEMEForce terminal background light / darkdetected from COLORFGBG / OSC 11, else dark
DAO_REASONING_EFFORTReasoning effortmax
DAO_MAX_TURNSMax tool turns per turn50
DAO_AUTO_APPROVESkip all approvals (sandbox/eval only)off
DAO_REFOCUS_EVERYRefocuser: re-check direction every N turns on long tasks (0=off; only in --goal long-task mode)3
DAO_FAIL_STREAKChallenger: review progress after this many consecutive failed turns (interactive only)3
DAO_REPEAT_ERRChallenger: review progress after the same error recurs this many times (interactive only)2
DAO_CHALLENGE_REPEAT_SIMChallenger: similarity threshold for "user re-raising the same problem" → async challenger (0=off; interactive only)0.1
DAO_REFLECTSet 0 to globally disable the reflection layer (challenger + refocuser)on

🗺️ Status

Released v0.3.0 (npm dao-code + multi-platform binaries on Releases). Core is complete: Ink TUI and Taiji splash, streaming agent loop, 24 tools, layered permissions, persistent memory, cache engineering, the reflection layer, long-task robustness, Skills/MCP/Hooks/subagent extensions, and a real OSS evaluation harness. Actively iterating — issues/PRs welcome.


🎨 Built with Dao Code

Open-source projects built entirely with Dao Code:

  • redis-rs — a Redis-compatible server in Rust (RESP2, ~80 commands), completed from scratch autonomously in dao --goal long-task mode.
  • magic-canvas — an iPad finger-painting app for toddlers (rainbow lines + stickers, SwiftUI + SpriteKit).
  • bubble-machine — an iPad bubble-blowing app for toddlers (long-press to grow / rapid-fire, procedural audio).

🤝 Contributing

Issues and PRs welcome! Onboarding, scripts, and commit conventions are in CONTRIBUTING.md; the community guidelines are in CODE_OF_CONDUCT.md. Do not file security vulnerabilities via public issues — report them privately per SECURITY.md. Changelog: CHANGELOG.md.


📄 License

MIT © tigicion

Collected info

  • ★ 1,083 stars
  • ⎇ 45 forks
  • Language: TypeScript
  • Source updated: 9/22/2026

Config for your environment

Replace {MCP_ENDPOINT_URL} with this MCP’s endpoint URL (from its repo or docs above). No API key — you connect directly.

Tool

OS

Config file: ~/.cursor/mcp.json

{
  "mcpServers": {
    "mcp-server": {
      "url": "{MCP_ENDPOINT_URL}"
    }
  }
}

Paste into mcpServers in the config file. Restart Cursor after saving.

If this MCP is also published on mcpchannel.ai, you can subscribe from Browse and use the gateway config there instead.