Skip to content

quack

Any model.
Any provider.
No leaks.

A terminal coding agent that gets the best out of whatever model you point it at: llama.cpp on your desk, vLLM in your lab, an open-model API, or Claude and GPT.

It would be worth using if it phoned home. It doesn't.

Where to find it

$ curl -fsSL https://github.com/paulhovey/quack/releases/latest/download/install.sh | sh

Then run quack; it finds model servers on this machine or walks you through adding one.

  1. Repairs messy tool calls Qwen3-XML, GLM, DeepSeek, Kimi and more
  2. Keeps prompt caches warm the system prompt never changes
  1. Checks every edit tree-sitter, then your language server
  2. Contacts only what you set up tested in a loopback-only network

Same model, two harnesses

Open models are good at code. Most harnesses grind them up anyway: tool calls go missing, caches go cold, broken edits slip through. Pick a failure to see what each side does with it.

The generic grinder

one harness for every model
QwenDeepSeekGLMKimiMiniMaxgpt-ossMistral
same prompt,
same tools,
for everyone

What comes out · pick one

_(o)> quack

a profile per model family
qwen-codedshzcodekimi-codecc-stylecodexvibe
tool names,
prompts and edit format
that suit the model

What comes out

tool calls handled cache handled JSON handled syntax handled context handled egress handled uploads handled

Tool call printed as text

The model writes its tool call as Hermes or Qwen3-XML markup in the reply instead of a real call.

In a generic harness

The harness shows the markup to you as prose. Nothing runs, and the turn stalls or loops.

<tool_call>
{"name": "read", "arguments": {"path": "src/main.rs"}}
</tool_call>

In quack

Calls printed as text in the Hermes, Qwen3-XML, GLM, DeepSeek or Kimi formats become real calls, and the raw markup never shows. Loose arguments are parsed and duplicate call ids are fixed.

✓ read src/main.rs · 212 lines

Seen in the wild

A full-screen terminal UI that works over SSH, in any terminal, on Linux, macOS and Windows. These are drawn the way quack draws them; the model names and output are examples.
quack 0.1.0 · ~/src/calendar ⎇ main
› the date parser test fails on leap years, fix it 
 
✻ thought for 41 words · ctrl-t shows 
✓ grep fn days_in_month · 2 matches in 1 file 
✓ read src/date.rs:40-72 
✓ edit src/date.rs · +3 -1 
▎@@ -51,7 +51,9 @@ 
▎- 2 => 28, 
▎+ 2 if is_leap(year) => 29, 
▎+ 2 => 28, 
 
╭ Run this command? ─────────────────────────────────╮ 
│ $ cargo test date:: │ 
│ y allow a allow for this session n deny │ 
╰─────────────────────────────────────────────────────╯ 
›▏
ask charm/glm-5.3 · 12.3k/104kwaiting for approval /help
Every change shows its diff, and commands ask first unless you allow them.

Field marks

Everything else it does today, one line each. The full list is in the README.

Models and providers

Local servers.
llama.cpp, Ollama, vLLM, SGLang and LM Studio, found on this machine by /setup.
Hosted services.
12 open-model APIs, from OpenRouter and DeepSeek to Kimi, Z.ai and Mistral, or any OpenAI-compatible URL.
Claude and GPT.
The Anthropic and OpenAI APIs with a key, or a ChatGPT plan through quack login chatgpt.
Reasoning kept.
Thinking survives tool calls and /resume, sent back the way each API expects.
Switch any time.
/models lists every model; switching keeps the history and approvals.
A planner model.
Plan with a big model, carry it out with your usual one, and keep its cache warm.

Editing code

Five edit formats.
hashline, str_replace, apply_patch, search/replace blocks or whole files, chosen per model.
A forgiving edit.
Matches past stray whitespace, copied line numbers and smart quotes; keeps CRLF and BOMs.
All or nothing.
A multi-file change that fails partway rolls every file back.
Undo and rewind.
/undo the last call, or /rewind the conversation, the files, or both.
Code intelligence.
Language servers for 11 languages, plus an lsp tool for definitions, references and hover.
Outlines.
index outlines a file or folder with tree-sitter; read a symbol by name.

Long tasks

A live todo list.
The model keeps a checklist and sees it at every step.
Sub-agents.
explore scouts the repo in its own context and hands back paths and line ranges.
Loop guards.
Repeated calls, edit-test-edit cycles and empty replies are caught.
Changed-file reminders.
The model is told which files it has touched, without rewriting history.
Small windows.
Old reads and outputs are pruned first; compaction keeps your request word for word.
Sessions.
Saved as they happen, crash-proof, and resumed with exactly the same context.

Safety

Sandboxed shell.
Commands write only in the workspace and have no network: Landlock + seccomp, or Seatbelt.
Five permission modes.
From read-only to yolo, with allow and deny rules over a floor you can’t remove.
Commands parsed.
In git status && rm -rf x the rm still asks, even inside subshells and heredocs.
Project trust.
A cloned repo’s .quack/ runs nothing until you trust it.

Making it yours

MCP.
Local and remote servers, deferred tool search, prompts as slash commands, forms and sampling.
Hooks.
Programs in any language on seven agent events; they can add context, block or rewrite calls.
Skills and commands.
SKILL.md skills, and any markdown file in commands/ becomes a slash command.
Your agent files.
Reads AGENTS.md, CLAUDE.md, GEMINI.md, .cursorrules and Copilot instructions.
Memory.
Notes per project and global, with approval before one is saved.
Your own queries.
Drop in tags.scm or highlights.scm to outline or colour a framework’s macros.

Everywhere

Three systems.
Linux, macOS and Windows on x64 and ARM64; PowerShell and Job Objects on Windows.
Starts at once.
The first frame draws in about <10 ms on Linux.
Headless.
quack exec "…" runs one turn for scripts and CI.
Sees itself.
/context and /stats chart tokens, cache hits, cost and time.
Over SSH.
Mouse selection and /copy go through the terminal (OSC 52).

Measured, not guessed

quack eval runs a task suite against any model and profile and counts passes, steps, bad calls, failed edits, tokens and cost. A family profile is switched on only when it clearly beats generic.
quack's own eval runs
2026-10-21mistral-large-4, vibe vs generic, one hard pass14/14 each · 0 vs 12 bad calls · 1 vs 23 failed edits · $0.94 vs $1.70
2026-10-20glm-5.3-flash, zcode vs generic, hard suite ×337/42 vs 35/42 · 482 vs 539 steps · half the tool errors
2026-10-20deepseek-v4.1-flash, dsh vs generic, hard suite ×342/42 each · 367 vs 424 steps · $0.36 vs $0.45
2026-10-19Per-session context moved out of the system promptUncached prompt tokens down 45% on deepseek-v4.1-flash
2026-10-198 models × 2 profiles, 21 small tasks (Charm Hyper)All 21/21, no unparsable calls, $2.42 for the lot

From the roadmap. Results files are in evals/results/.

Why the harness matters

FrontierHarness Eval held one model, Kimi K3, fixed across 30 tasks and changed only the harness. Pass rates ran from 50% to 67%, and the median cost of a task from $1.05 to $18.34.

quack hasn’t been run on it yet. When it has, the result goes here, whatever it is.

frontierharness.org
FrontierHarness Eval: pass rate against median cost per task for 12 harness configurations running Kimi K3
The numbers behind the chart
HarnessPass rateMedian costMedian cache hit
Codex66.7%$3.4788.0%
DSH Creator63.3%$3.2884.3%
Claude Code63.3%$18.3467.8%
Pi60.0%$2.4379.4%
DSH PTC60.0%$4.5887.2%
DSH Standard60.0%$3.4686.5%
Oh My Pi56.7%$4.7582.2%
Kimi Code56.7%$3.6588.0%
DSH Minimal56.7%$4.7284.6%
Exo Harness53.3%$1.0570.3%
OpenCode50.0%$3.2478.4%
Hermes50.0%$2.9085.9%
Chart and figures: FrontierHarness Eval 1.0.

For teams rolling it out

The same agent, with the controls an administrator needs to say yes to it.
Nothing built in that reaches out
Every provider and MCP server is a small TOML file. Delete one and that capability is gone; a test checks startup stays clean for every default file. Only the core prompt and sub-agents are compiled in.
Hosts you allow
On top of contacting only what’s configured, [policy.providers] hosts and [policy.mcp] hosts narrow where providers and MCP servers may point, and quack doctor lists every host it can reach.
Settings you can lock
Config layers from system to user, project, environment and command line. An administrator’s locks hold in /config, and quack config show --origin says where each value came from.
Hooks users can’t remove
Hooks in the system config folder are enforced: block a tool call, add context, or check a prompt, in any language.
Ship your own files
quack-plucked is the duck without its feathers: the whole agent minus the hosted-service providers, for teams with their own model server. The Config plate below walks through it.
Apache-2.0, few crates
Every dependency is counted per OS before it’s added. The keyring adds no crates on Linux.

providers.d/lab.toml

base_url = "http://gpu-01.lab:8000/v1"

/etc/quack/config.toml

[policy.providers]
hosts = ["gpu-01.lab"]
A provider needs only its URL; models are discovered. The policy keeps every provider to the lab’s own server.

Your server. Nothing else.

Install quack-plucked, write two TOML files for https://<internal-server>:8000/v1, and you have the whole harness, with that server as the only host it will ever contact.
  1. Install the plucked duck

    The whole agent (prompts, sub-agents, MCP, ACP, language servers) minus the hosted-service providers, so nothing in it points off the machine. Offline, use --archive, or the .deb and .rpm packages.

    shell

    $ curl -fsSL https://github.com/paulhovey/quack/releases/latest/download/install.sh \
      | sh -s -- --plucked
  2. Describe your server

    Anything OpenAI-compatible: vLLM, SGLang, llama.cpp. base_url is the only required line; leave out models and they’re listed from the server.

    providers.d/corp.toml

    # /etc/quack/providers.d/corp.toml
    name = "Corp LLM"
    base_url = "https://llm.internal.example:8000/v1"
    api_key = { env = "CORP_LLM_KEY" }   # leave out if no key
    models = [
      { id = "qwen3-coder-480b", alias = "coder" },
      { id = "qwen3-coder-30b", alias = "fast" },
    ]
  3. Lock it down

    The system layer’s policy holds against user files, a repo’s .quack/, environment variables and the command line.

    config.toml

    # /etc/quack/config.toml
    [model]
    default = "corp/coder"
    small = "corp/fast"
    
    [network]
    offline = true
    
    [sandbox]
    mode = "workspace-write"     # commands: no network
    
    [permissions]
    deny = ["shell:curl *", "shell:wget *"]
    
    [policy]
    locked = [
      "model.default", "network",
      "sandbox.mode", "permissions.deny",
    ]
    max_permission_mode = "full-auto"   # no yolo
    project_config = "trusted-only"
    
    [policy.providers]
    allowed = ["corp"]
    hosts = ["llm.internal.example"]
    
    [policy.mcp]
    hosts = ["*.internal.example"]
  4. Check it

    There’s nothing else to install. quack doctor makes no model call; a user’s provider pointing elsewhere shows as blocked, and the only host listed is yours.

    terminal

    $ quack doctor
    ok    defaults   /usr/share/quack/defaults
    warn  providers  1 of 2 usable
    ok      corp     2 declared models
    warn    rogue    not allowed by policy
    ok    model      corp/coder
    ok    agents     explore (on corp/fast), general
    ok    sandbox    workspace-write · Landlock + seccomp
    info  network    llm.internal.example · policy allows only
                     llm.internal.example, *.internal.example

What’s guaranteed

Only what the files name
No telemetry, update checks or model downloads. A recording-proxy test and a CI run in a loopback-only network namespace check every build.
Hosts are a policy, not a hope
With [policy.providers] hosts set, a provider pointing anywhere else doesn’t load, whoever wrote it: a user, a project or a bundle.
Locks hold everywhere
User files, a repo’s .quack/, environment variables, -c and /config can’t change a locked key. QUACK_SYSTEM_DIR is ignored while the real system folder holds a policy.
Commands stay offline too
The model’s shell commands run in the OS sandbox with no network on Linux and macOS. Windows has no sandbox yet, so there, deny rules are the only control on commands.

Where it lives

Each layer overrides the one before. Every layer’s folder has the same shape, one file per item, and a file with the same name in a higher layer replaces the lower one. quack config paths prints the folders for this machine.

Configuration layers, lowest first
Defaults bundleshare/quack/defaults/ beside the binary, or QUACK_DEFAULTS_DIRquack-plucked leaves out hosted services
System/etc/quack/ · /Library/Application Support/quack/ · %ProgramData%\quack\The only layer whose [policy] counts
User~/.config/quack/ · %APPDATA%\quack\Your own files
Project.quack/ at the repository rootLoads only once you trust the repo
EnvironmentQUACK_MODELPicks the model, like -m
Command line-m, --profile, --permission-mode, -c key=valueThis run only

any layer’s folder

config.toml          settings
providers.d/*.toml   model servers and services
mcp.d/*.toml         MCP servers
lsp.d/*.toml         language servers
hooks.d/*.toml       programs that run on agent events
agents.d/*.toml      sub-agents for the task tool
profiles.d/<id>/     prompts and tool shapes per model family
commands/*.md        your slash commands
skills/<name>/       SKILL.md skills
bundles/<name>/      groups of the above
AGENTS.md            instructions for every project

Every option

Any subset works; everything left out keeps its default. quack config check validates every file against its JSON schema, /config edits settings in the UI, and the README has the full list.

Models
default ""
Model to start with: <provider>/<model> or <provider>/<alias>
small ""
Titles, summaries and the explore sub-agent. Empty: default
planner ""
Writes the plan in plan mode; your usual model carries it out
remember_last true
Start with the model you last switched to
profile ""
One profile for every model, e.g. generic or qwen-code
Permissions
mode "ask"
read-only, ask, auto-edit, full-auto (sandbox and deny rules still apply) or yolo
allow []
Rules that run without asking: shell:cargo test*, edit:src/**, mcp:github/get_*
ask []
Rules that always ask, even in full-auto
deny []
Rules that never run, in any mode. A built-in deny list always applies too
Sandbox
mode "auto"
auto, workspace-write (no network), workspace-write-net, read-only or off. A mode you set fails closed
writable []
More folders commands may write to, e.g. ["~/.cargo"]
Network
offline false
Remote MCP servers must be on this machine or in egress_allow
egress_allow []
Host globs allowed while offline
proxy ""
Proxy for model requests. Empty: HTTPS_PROXY or the system’s
ca_bundle ""
PEM file of extra root certificates, for an internal CA
Policy
locked []
Dotted keys no other layer can change; a table locks all of it
max_permission_mode unset
The most permissive mode allowed, --yolo included
project_config "trusted-only"
trusted-only, never or always: whether a repo’s .quack/ loads
providers.allowed []
Provider id globs that may load. Empty: all
providers.hosts []
Host globs a provider’s base_url must match. Empty: any
mcp.allowed []
MCP server id globs that may load
mcp.hosts []
Host globs a remote MCP server’s url must match
Context
window unset
Use at most this many tokens of the model’s window
budget_ratio 0.85
Share of the window quack plans to use
compaction_threshold 0.80
Share of the budget at which pruning, then compaction, starts
cache_strategy "auto"
auto or preserve keep the prompt cache warm; aggressive prunes every request
Tools
edit_format "auto"
str_replace, hashline, apply_patch, search_replace_blocks or whole_file
shell "auto"
bash, zsh, pwsh, powershell, cmd, or a path
default_timeout_secs 120
When a shell command’s process tree is killed
read_outline_tokens 2000
Larger source files are read as an outline first. 0: off
Interface
theme "auto"
dark or light
show_reasoning "collapsed"
collapsed, expanded or hidden
startup_panel "auto"
auto, always or never
ascii false
ASCII instead of box-drawing glyphs
mouse true
false leaves the mouse to the terminal
reduced_motion false
Static indicators instead of spinners
terminal_title true
Window title shows the duck and the session title. false: leave it alone
Instructions, skills, memory, sessions
instructions.files ["AGENTS.md"]
Instruction file names, in order of preference
instructions.other_agents true
Read CLAUDE.md, GEMINI.md, .cursorrules where there’s no AGENTS.md
instructions.max_bytes 32768
Cap on each instruction file
skills.compat_paths []
More skill folders, e.g. .claude/skills
skills.disabled []
Skill names to skip
memory.path ""
Where notes live, e.g. a synced folder
memory.project_scope "data-dir"
repo keeps project notes in .quack/memory
memory.writes "auto"
ask makes each note ask first
memory.nudge true
Remind the model to save lasting preferences
sessions.path ""
Where sessions are saved
defaults.dir ""
The defaults bundle’s location; "none" turns it off
A provider
base_url required
The API root, e.g. https://llm.internal.example:8000/v1
api "openai-chat"
openai-chat, openai-responses, anthropic-messages or ollama
api_key none
A secret: { env = … }, { file = … }, { cmd = […] } or { keyring = … }
models []
Ids or tables. Empty: listed from the server
headers {}
Extra headers; values may be secrets
extra_body {}
Merged into every request
tls.ca_file none
An extra CA for this server
tls.client_cert / client_key none
Mutual TLS
http.timeout_idle_secs 300
How long the stream may go quiet
http.max_retries 3
Retries, honouring Retry-After
http.proxy ""
A proxy for this provider only
A model
alias none
A short name: corp/coder
context_window / max_output from the server
Token limits
profile "auto"
A profile id, or auto to pick by its [match] globs
vision false
Offer images to the model
tool_calling.mode "native"
native, text or grammar (schema-guided decoding)
tool_calling.text_format "auto"
hermes, qwen3-xml, glm, json-block, deepseek or kimi
reasoning.effort ""
e.g. low, medium, high
sampling from the profile
temperature, top_p, top_k, min_p, repetition_penalty
cost from the server
Dollars per million tokens, for /stats
An MCP server
command / args
A local server, over stdio
url
A remote streamable HTTP server, instead
headers / env {}
Values may be secrets
sandbox false
Run a local server in the OS sandbox
tools.allow / deny []
Tool name globs to offer or hide
tools.auto_approve []
Tool name globs that run without asking
A language server
command / args required
Used only when it’s on PATH; nothing is installed
extensions required
File extensions it covers, without the dot
root_markers []
Files that mark a project root
settings {}
Answers to workspace/configuration
diagnostics.min_severity "warning"
error, warning, info or hint
A hook
event required
session_start, user_prompt, pre_tool, post_tool, turn_end, session_end, pre_compact
command required
Program and arguments; {dir} is the file’s folder, {python} the Python on PATH
match {}
tool and pattern globs for tool events, a prompt regex for prompts
timeout_secs 10
How long it may run
on_error "warn"
warn, allow or deny
A sub-agent
description required
What it’s for; the model reads it to choose
prompt ""
Added to the system prompt for its run
tools all
Tool names it may use
read_only false
Change nothing; may run in plan mode and in parallel
model the caller’s
small or <provider>/<model>
max_steps none
Model calls before it reports

Where to find it

One command installs the binary and the defaults: provider files for local servers and hosted services, model profiles, language servers and MCP examples. Then run quack.
$ curl -fsSL https://github.com/paulhovey/quack/releases/latest/download/install.sh | sh

  • Plucked: the duck without its feathers. The whole agent, with MCP, ACP, profiles and sub-agents, minus the hosted-service providers, for teams with their own model server. --binary-only updates just the binary.
  • Options: --prefix, --version, --archive FILE for offline installs, and --uninstall.
  • Archives for every platform are on the releases page.

In development and usable day to day. Not there yet: sandboxing on Windows, /fork --worktree, config auto-reload and MCP OAuth.