⚡ Zap Coding Agent

Website · Docs · Roadmap · Changelog · Install · Demo

Crates.io License: MIT GitHub release

ZAP is a terminal-first, local AI coding agent built in Rust. It uses AST-powered codebase indexing and lazy-loaded skills to completely eliminate prompt bloat and minimize context token costs — single binary, no runtime.

  ╭────────────────────────────────────────────────────────────────────╮
  │                                                                    │
  │   _____     _     ___                                              │
  │    ___/    /_\   | _ \   fast AI coding agent  v0.15.19           │
  │   /       / _ \  |  _/                                            │
  │  /_____  /_/ \_\ |_|                                              │
  │                                                                    │
  ├──────────────────────────────────────┬─────────────────────────────┤
  │  model     claude-sonnet-4-6         │  Tips for getting started   │
  │  backend   Anthropic API             │    Tab  ↑↓  autocomplete    │
  │  mode      ask                       │    /          commands      │
  │                                      │    /provider  switch LLM    │
  │  ~/my-project                        │    /help      all commands  │
  ╰──────────────────────────────────────┴─────────────────────────────╯

Context Visibility in zap


Eradicating Prompt Bloat in AI Coding Agents

Open any popular AI coding agent and inspect the raw request it sends to the LLM. You'll find hundreds — sometimes thousands — of lines of system prompt sent on every single turn, regardless of what you're actually doing.

We measured this. Here's what Gemini CLI and OpenCode send when you ask them to write a Spring Boot service vs. a React component — two completely different languages, frameworks, and conventions:

LLM Token Efficiency Matrix (Spring Boot vs. React Tasks)

AI Coding Agent Spring Boot Request React Request Identical Base Prompt? Language Context Injected?
Gemini CLI 4,096 tokens 4,096 tokens ✅ Yes ❌ None (0 mentions of Java/React)
OpenCode 2,003 tokens 2,003 tokens ✅ Yes ❌ None (baseAnthropicCoderPrompt)
ZAP (Rust) 1,889 tokens 1,661 tokens No Yes (+650 Java / +422 React tokens)

Gemini CLI sends the same 4,096-token prompt for both. The word "java" does not appear anywhere in its 68,410-character prompt file. Neither does "react", "kotlin", or any other language. The LLM writing your Spring Boot service and the LLM writing your React component receive identical instructions. (source)

OpenCode uses a single static string constantbaseAnthropicCoderPrompt — sent verbatim on every turn, every task type. Zero mentions of Java, TypeScript, Rust, Python, React, or any specific language. (source)

This isn't just waste. It's why these agents give inconsistent output — the model has no language-specific guidance, so it invents its own conventions turn by turn.

zap sends a different prompt for different tasks — the Java skill fires for Spring Boot, the React skill fires for components — and a greeting costs 12 tokens, not 2,000–4,000.

Full methodology, raw token counts, and source links: content/evidence/system-prompt-comparison.md Medium series: Introducing ZAP — The Open-Source AI Coding Agent That Doesn't Bloat Your LLM Context


Context Quality is Supreme

Every design decision in zap follows one principle: every token in the LLM's context window must earn its place. Context that doesn't improve output quality is waste — it dilutes attention, burns budget, and produces inconsistent results.

This principle drives everything:

Mechanism What it ensures
Skill injection Only language- and task-specific guidance is sent, not a one-size-fits-all monolith
AST code index The agent knows what exists before it decides what to create — no blind writes
/init command Auto-generates project-specific context files so every session starts informed
Context files ZAP.md, .zap/understanding.md, .zap/context.md, .zap/session_log.md — maintained automatically, loaded on demand
Casual-turn detection Greetings cost ~31 tokens, not 2,000–4,000
Lazy MCP Server tool schemas stay out of context until the model explicitly connects them
Token-cost display Every turn shows exactly what went into context — skills, message, system, and estimated $

What gets updated and when

zap maintains four project context files, each updated at a specific time to keep the agent current without polluting the context window:

File Updated when What it contains Loaded when
ZAP.md /init (manual, once) Project overview, build commands, architecture, do-not-touch list Every session (on-demand)
.zap/understanding.md /init + /understand Navigation map + business-domain map Every session (on-demand)
.zap/context.md End of every session (auto) Last session: goal, files touched, what's next Session start (on-demand)
.zap/session_log.md Every session (auto) History of all past sessions, indexed by date On request (on-demand)

These files are never pre-loaded into the context window — the model reads them only when relevant, using read_file. This means a session about fixing a typo doesn't pay the cost of loading the entire project architecture.

The result

  • First session: /init analyses your repo and bootstraps all project knowledge in ~30 seconds
  • Run /understand: One LLM call maps every business domain to the files that own it — the agent navigates straight to the right module with no guessing
  • Returning sessions: The agent already knows what you were working on, which files changed, and what was left unfinished
  • Every turn: Skills inject only what's relevant, the index tells the agent what exists, and casual messages skip the overhead entirely

Real output: /understand run on the zap repo itself

You: /understand
zap: ⚡ Extracting business domain map (one LLM call)…

  ↳ code_map '.'     → 2000 symbols  (6ms)
  ↳ code_map 'src'   → 1356 symbols  (3ms)
  ↳ edit_file '.zap/understanding.md'  ← domain map written

What it wrote into .zap/understanding.md:

## Domain Map

### Business Domains

| Domain                   | Owns                                     | Key entry points                    |
|--------------------------|------------------------------------------|-------------------------------------|
| Agent runtime            | agent_core, cli, main, lib               | authenticate, Session::new          |
| LLM provider integration | llm_client/*, http                       | send, stream                        |
| Tool execution + safety  | tools/*, permission_manager, shell_runner| ToolRegistry::execute               |
| Project context + prompt | context_manager, project, plan_execution | build_system_prompt, handle_user_turn|
| Code intelligence        | code_index/*                             | index_dir, global_callers_of        |
| MCP integration          | mcp                                      | mcp_connect, list_tools             |
| Persistence + memory     | persistence, project                     | init, save_session_context          |
| Skills + workflow        | skill_manager, default_skills/*          | detect_domain_scope                 |
| Remote session sharing   | remote, remote_channel                   | spawn_tunnel, broadcast             |

### Cross-Cutting Concerns
- Error handling: `anyhow::Result` everywhere; tool errors surfaced as text responses
- Logging: `crate::log::write` (file-only, never stdout)
- Config: `Config` struct in src/config.rs, loaded once at startup
- Security: `permission_manager`, `secret_scanner`, `audit` enforce approval and logging

### Dependency Direction
tools → session → agent_core; llm_client ← session only; nothing imports session from tools

Two code_map calls, zero source file reads, one edit_file. The domain map is injected into every future session automatically.


code indexing demo


What makes zap different

1. Skill-First Approach — Context That Earns Its Place

Most AI coding agents front-load a massive system prompt every request — language conventions, architecture notes, team rules, API patterns, all of it, whether it's relevant or not. zap replaces that wall with a skill system: markdown files that are injected surgically, only when your message triggers them.

Two kinds of skills:

Kind When injected Example
Always-on Every turn, baked into the base system prompt karpathy-guidelines — Andrej Karpathy's 4 coding principles
Triggered Only when your message matches keywords rust fires on "cargo", "fn ", "trait "; git fires on "commit", "push"

Built-in skills (compiled into the binary, zero config):

Skill Type Triggers on
karpathy-guidelines always-on every turn
rust triggered rust, cargo, crate, async fn, clippy…
python triggered python, pip, pytest, dataclass…
typescript triggered typescript, tsx, interface, npm…
react triggered react, component, jsx, hook, useState…
go triggered go, goroutine, chan, go.mod…
git triggered commit, branch, merge, pull request…
code-review triggered review, pr review, lgtm, critique…
debugging triggered debug, error, crash, panic, stacktrace…
security triggered auth, password, token, jwt, xss, sql injection…

Stack auto-detection fires the right language skill on startup — Rust project with Cargo.toml gets the rust skill loaded automatically.

Example — a Rust project, custom api-conventions skill also loaded:

You type Skills injected Base + skills
"refactor this async fn to use channels" karpathy + rust ~2.4k tokens
"commit these changes" karpathy + git ~2.0k tokens
"add a new REST endpoint" karpathy + api-conventions ~2.2k tokens
"explain what this function does" karpathy only ~1.8k tokens

Honest baseline: the always-on karpathy-guidelines skill and the base system prompt together run ~1.8k tokens — much leaner than Claude Code (~10k) or Gemini CLI (~8k), but not the "200 token" figure you might see in older docs.

Custom skills override built-in ones of the same name. Write a skill once and zap injects it exactly when needed:

---
name: api-conventions
description: REST endpoint conventions for this project.
trigger: ["endpoint", "route", "handler", "REST"]
tokens: ~400
---
All endpoints must validate input with ValidateRequest(), return structured
errors as {"error": "...", "code": N}, and use snake_case JSON keys.

Always-on skill (no trigger: field — injected every turn):

---
name: our-principles
description: Team engineering principles.
---
We ship small, reversible changes. Every PR needs a test. No console.log in prod.

Where to put them:

Path Scope Priority
.zap/skills/ project — check into git, team-shared highest
~/.zap/skills/ personal — all projects middle
binary built-in defaults lowest

On first launch zap writes all built-in skills to ~/.zap/skills/ automatically — open any file there to read or edit it. Same-name files you create override the built-in version on the next run.

/skill list              # see all skills with source and always-on/triggered label
/skill show <name>       # preview content + description + license
/skill export <name>     # re-export a built-in to ~/.zap/skills/ (if you deleted it)
/skill export --all      # re-export every built-in skill
/skill create <name>     # scaffold a new skill in .zap/skills/
/skill capture <name>    # extract instructions from this session into a reusable skill

2. AST-Powered Code Indexing vs. Agentic Search

Most agents navigate code the same way a shell script does — grep for a string, hope the result is what you meant. zap builds a real AST symbol index at startup using tree-sitter + SQLite, giving the model genuine structural understanding of your codebase.

The problem: agents that write without looking

Ask most coding agents to "add all the API layers for user management" in an existing project and you'll see a predictable set of mistakes:

  • Duplicate files createdsrc/user_repository.rs already exists, but the agent creates src/repositories/user_repo.rs alongside it because it never checked
  • Existing patterns ignored — the project uses a Repository<T> trait with a specific error type; the agent invents its own DB access style from scratch
  • Scaffolding over existing codesrc/routes/, src/models/, src/db/ already exist with boilerplate; the agent recreates them
  • Missed abstractions — a BaseRepository or shared AppError type already exists; the agent writes a duplicate

These aren't model failures — they're context failures. The agent is writing blind because its context window never contained the files it needed to check.

How the index fixes it

When you ask zap the same question, before writing a single line it queries the index:

-- Does a user repository already exist?
SELECT path, line, kind FROM symbols WHERE name LIKE '%UserRepo%' OR name LIKE '%UserStore%';

-- What repository pattern does this project use?
SELECT name, path, line, signature FROM symbols WHERE kind = 'trait' AND name LIKE '%Repository%';

-- What's already in the db/ directory?
SELECT name, kind, line FROM symbols WHERE path LIKE '%/db/%' ORDER BY path, line;

This runs in milliseconds against the local SQLite index — no file reads, no grep, no context stuffing. The model knows what exists before it decides what to create. It adds to src/user_repository.rs instead of creating a new one. It implements the existing Repository<T> trait instead of inventing a new pattern.

When you ask zap to "refactor the UserStore struct", it doesn't search for the string "UserStore" — it looks up the symbol in the index, finds the exact file and line number, reads only that section, and makes a precise edit. No false matches, no reading entire files to find one function.

The index is incremental — on subsequent runs, only files that changed since the last session are re-parsed. A background indexer runs every 120s during interactive sessions so the index stays fresh as you edit. Cold-indexing a 50k-line repo takes a few seconds; warm starts are near-instant.

Always current during edits — every time zap writes a file, it immediately reindexes that file before the next LLM turn. The model never queries a stale index for files it just changed.

Index usage is logged per turn — every time a tool call is answered by the index (rather than falling back to grep), zap logs it to ~/.zap/zap.log and ~/.zap/audit.jsonl:

[INDEX] hit · find_definition · 'UserRepository' · 3 result(s)
[INDEX] hit · code_map · 'src/db/' · 42 symbol(s)
[INDEX] miss · find_definition · 'legacy_fn' · grep fallback

This makes it auditable — you can see exactly when the index was used vs. when the agent had to fall back to text search.

Supported languages: Rust, Python, TypeScript, JavaScript, Go, Java

Powered tools:

Tool What it does
code_map Structural outline of any file or directory — functions, structs, classes, enums, with line numbers
find_definition Jump directly to where a symbol is defined — AST index first, ripgrep fallback
find_references Every call site of a symbol across the entire codebase
who_calls All callers of a function, optionally filtered by qualifier
file_imports Everything a file imports
where_imported Every file that imports a given name — blast radius for renames
find_subtypes / find_supertypes Type hierarchy traversal
pack_context Auto-selects the most load-bearing files for a task and returns them in a token budget
ripple_analysis BFS blast radius: who calls a symbol, who calls those callers, and so on — instant, no compiler
get_diagnostics Compiler errors/warnings via language server — same as cargo check without running it
lsp_definition Type-resolved go-to-definition for cross-crate symbols (std, deps, generics)
lsp_type_at Exact inferred type of any expression — the tooltip your editor shows, in the agent

The model is instructed to always use code_map or find_definition before reaching for read_file — so it reads only the lines it actually needs, not whole files.

How the index powers every LLM turn:

You: "refactor the UserStore struct"

  zap (tool call)  →  find_definition("UserStore")
  SQLite index     →  src/db/user_store.rs:42  ← instant, no file scan
  zap (tool call)  →  read_file("src/db/user_store.rs", offset=40, limit=60)
  zap (tool call)  →  edit_file(...)            ← precise edit, right lines

Without index: grep entire repo → read 3 wrong files → hallucinate location
With index:    SQLite lookup → read 20 lines → done

Blast radius before you touch anything:

You: "what breaks if I change the signature of parse_config?"

  zap (tool call)  →  ripple_analysis("parse_config", depth=3)
  call graph BFS   →  Depth 1 — 4 direct callers across 3 files
                       Depth 2 — 11 callers of callers across 6 files
                       Depth 3 — 2 more files indirectly affected
  zap              →  "Here's what's at risk — want me to check each caller?"

Compiler errors without running cargo check:

You: "check if my edit to src/lsp/client.rs is correct"

  zap (tool call)  →  get_diagnostics("src/lsp/client.rs")
  rust-analyzer    →  src/lsp/client.rs:47 error[E0308]: mismatched types
  zap              →  "Line 47 has a type mismatch — here's the fix..."

Code quality report — the same SQLite index powers /index quality, a human-readable health report run directly in the TUI:

Code Health  ·  27 files  ·  1043 symbols  ·  ⚡ 74/100
────────────────────────────────────────────────────────────

File sizes  (lines)
────────────────────────────────────────────────────────────
  ⚠ 2382  src/session/commands.rs    ████████████████████  37 sym
  ⚠ 2266  src/tui/render.rs          ████████████████████  48 sym
  ⚠ 1789  src/session/mod.rs         █████████████         45 sym
  ⚡ 1177  src/tui/mod.rs             ████████
  ·   527  src/tui/app.rs             ███
  ·   312  src/code_index.rs          ██

  ⚠ >1000 lines   ⚡ 500–1000   · healthy

God objects  (>15 methods — split candidates)
────────────────────────────────────────────────────────────
  Session                        45 methods  (mod.rs)
  ToolRegistry                   18 methods  (tool_registry.rs)

Dead code candidates  (pub fn, ≤1 reference)
────────────────────────────────────────────────────────────
  export_skill                   (skill_manager.rs:599)

Explore the index yourself

The index is a plain SQLite database at .zap/code.db — you can query it directly at any time, no zap session required.

macOS / Linux — interactive explorer script:

./scripts/explore-db.sh

Launches an interactive menu with 10 ready-made queries (overview, top files by symbol count, search by name, raw SQL, and more). Requires only sqlite3, which ships with macOS. On Linux: sudo apt install sqlite3.


Why zap indexes when Claude Code deliberately doesn't

Claude Code (Anthropic's own CLI) has no built-in code indexing. No tree-sitter, no SQLite, no ctags. It uses pure agentic search — grep + glob + read, chosen at runtime by the model. This was a deliberate, tested decision.

Boris Cherny (Claude Code's creator) confirmed publicly that Anthropic built and benchmarked a RAG/vector-index approach early on and dropped it because agentic search won "by a lot." The reasons:

  • Grep finds exact matches; embeddings introduce false positives
  • No index to build or maintain
  • Index drift — code changes constantly during editing sessions
  • Simpler architecture with fewer failure modes

Sources: Claude Code Doesn't Index Your Codebase — vadim.blog · Building Claude Code with Boris Cherny — Pragmatic Engineer · Official Claude Code docs

The community has noticed the gap — multiple open-source MCP servers exist to bolt indexing onto Claude Code:

And open feature requests asking Anthropic to add this natively: #4556 · #9277

zap makes the opposite bet. Agentic search solves semantic questions well ("find code related to payment processing"). A persistent AST index solves structural questions better — "what already exists in this module?", "which files implement this pattern?", "is there already a UserRepository?" These are exactly the questions that matter when an agent is about to write new code. Without an index, the agent can only search for what it knows to look for. With an index, it knows what exists before it decides what to create.

The two approaches solve different failure modes. Agentic search avoids index drift. AST indexing avoids blind writes into a codebase the agent hasn't fully read.

Does the index trade quality for speed?

No — it trades one search strategy for a better one, with a full fallback for the cases the index doesn't own.

The concern usually raised: "agentic search won by a lot" — Boris Cherny said this when Anthropic dropped their RAG/vector approach early on. That's true, and the reasoning is sound: vector embeddings introduce false positives (semantically similar but wrong matches), require a build step, and drift as the codebase changes. But zap doesn't use vectors or RAG. It uses an AST symbol index — fundamentally different. A symbol lookup either returns the exact definition or it doesn't. No hallucinated matches, no similarity threshold to tune, no embedding model to maintain.

The index owns structural questions — where agentic search pays full price:

Every time Claude Code answers "where is UserRepository defined?", the model spends tokens deciding which files to read, issues multiple grep calls, reads partial file content, and synthesizes the answer. The index answers the same question in a single SQLite lookup. On a 50k-line repo, that's the difference between 1 tool call and 5.

Grep handles what the index doesn't:

The index knows symbol names, kinds, and locations. It doesn't do open-ended semantic search — "find everything related to the checkout flow" is a grep question, not a symbol question. zap's model uses search_code (ripgrep) for those. The index and grep are complementary layers, not competing ones. Every find_definition miss automatically falls back to ripgrep — so zap never does worse than pure agentic search, only better when a symbol is indexed.

Question zap Pure agentic search
"Does UserRepository already exist?" instant SQL lookup grep scan + file reads
"What pattern do existing implementations follow?" schema query across all types read multiple files
"Find all code related to the checkout flow" ripgrep (same as agentic) ripgrep
Large codebase, cold start index pre-built, no file reads cold grep every turn

The index doesn't reduce quality — it reduces the number of file reads required to answer structural questions, which directly reduces token cost and the chance of the model writing over something that already exists.


3. Built in Rust: A Local AI Coding Agent in a Single Binary

zap is written entirely in Rust and ships as a single statically-linked binary.

  • No Python venv. No Node.js. No Docker. No dependency hell.
  • Instant startup — cold start in milliseconds, not seconds.
  • Low memory footprint — the process sits at ~20 MB idle.
  • Memory-safe by construction — no buffer overflows, no use-after-free, no data races.
  • Compile once, run anywhere — drop the binary on your PATH and it works.
cargo build --release
cp target/release/zap ~/.local/bin/zap
# that's it

4. MCP Support — Lazy-Loaded, Cross-Agent Compatible

MCP (Model Context Protocol) is an open standard. Any MCP server you configure in zap also works in Claude Code, Cursor, Kiro, and other agents — the config format is shared. zap adds two optional fields (description, toolsHint) that other agents silently ignore, so your config file is fully portable.

Config file locations

File Scope
~/.zap/mcp.json Global — applies to every session
.mcp.json (project root) Project-local — checked into git, takes precedence

How lazy loading works

Most agents connect to every configured server at startup and dump all tool schemas into the LLM's context on every turn. Ten servers × five tools each = 10 000+ wasted tokens per request, whether you use them or not.

zap keeps every server in a pending state at startup. Instead of their tool schemas, the LLM gets one lightweight stub:

mcp_connect(server)
  - filesystem: Read/write files in /tmp and the project  [tools: read_file, write_file, list_directory…]
  - fetch: Fetch web pages as markdown                    [tools: fetch]
  - memory: Persistent knowledge graph                    [tools: create_entities, search_nodes…]

When the LLM decides it needs a server, it calls mcp_connect("filesystem"). zap spawns the process, runs the handshake, fetches the real tools/list, and registers those tools — all within the same agentic turn. The very next LLM call sees the full tool schema and can invoke the tools directly.

Stage Other agents zap
Startup Spawns all server processes Reads config only — zero processes
LLM tool list per turn All tool schemas, always One mcp_connect stub until needed
First use of a server Already connected Spawns process on demand, ~200 ms
After first use Real schemas in context, mcp_connect gone

MCP commands

/mcp list              list all servers — connected, pending, or failed
/mcp edit              open ~/.zap/mcp.json in $EDITOR
/mcp edit project      open .mcp.json (project-level config)
/mcp path              print both config file paths

5. Security is a First-Class Concern

zap handles your source code, credentials, and shell — so it treats security as a core feature, not an afterthought.

The agent cannot execute anything destructive without your explicit approval

In the default ask mode, every write operation and shell command is blocked until you approve it. Read-only tools run freely. Only the tools that can cause damage require your sign-off:

Tool class Ask mode Auto mode Deny mode
read_file, search_code, code_map, git_status ✓ always allowed ✗ blocked
edit_file, write_file, batch_edit prompt
shell prompt
spawn_agent prompt

Three modes, your choice:

Mode When to use
ask (default) Any interactive session — you stay in control
auto Sandboxed CI, scripts, or headless runs where you control the environment
deny Completely read-only — the agent can read and reason but cannot write a single byte or run any command

Switch at any time: /permissions ask, /permissions auto, /permissions deny.

Shell sandboxworkdir mode confines shell commands to the project root; container mode wraps commands via Docker/Podman with --network none for full isolation.

Secret scanner — 25+ patterns, blocks before sending

Before any content is sent to a cloud LLM, zap scans it for secrets:

  • API keys: Anthropic (sk-ant-), OpenAI (sk-proj-), Stripe live and test keys
  • VCS tokens: GitHub (ghp_, ghs_, github_pat_), GitLab (glpat-)
  • Cloud credentials: AWS access keys (AKIA), AWS secret key fields, GCP service account JSON
  • Cryptographic material: PEM private key blocks (-----BEGIN), JWT tokens
  • Generic credential fields: password=, api_key=, secret=, access_token= in config files

Matches are blocked and you're warned with the line number and a redacted preview — content is never silently forwarded.

Full audit trail

Every tool call is appended to ~/.zap/audit.jsonl as a structured JSON record with a timestamp, tool name, and outcome.

/audit 20       # show last 20 audit entries in the TUI

Undo for every edit

Before modifying any file, zap snapshots the previous content in memory.

/undo src/main.rs      # restore file to its pre-edit state

6. /init — Zero to Context-Aware in 30 Seconds

Most agents start every session blind. They don't know your project structure, your build commands, your architecture, or what you worked on last time.

/init fixes this once, permanently.

/init
  1. Auto-detects your stack — identifies the language/framework from your repo
  2. Indexes the codebase — builds the AST symbol index so the agent can navigate structurally from turn one
  3. Creates ZAP.md — project overview, build/test commands, architecture layout, key files, and a do-not-touch list
  4. Creates .zap/understanding.md — a deeper technical summary: module map, data flows, non-obvious patterns, constraints
  5. Writes .zap/project.json — persisted project config (language, index state)

Total time: ~30 seconds. From that point forward, every session starts informed — the agent knows your project.


7. Automated Session Continuity — Never Lose Your Thread

Claude Code and most other agents have no persistent memory of what you worked on last time. Every session you start over, re-explaining the goal, pasting in the error you were debugging, and reloading context you already established.

zap automates the handoff between sessions completely — you pick up exactly where you left off, without doing anything.

What happens at session end

When you close zap (/exit, Ctrl+C, or closing the terminal), it automatically:

  1. Summarizes what's next — makes a small LLM call over the last 10 messages, generating 1-3 bullet points with concrete next steps: file names, function names, features still in progress
  2. Writes .zap/context.md — a structured handoff file: goal, files touched, and the LLM-generated "What's next" summary
  3. Appends .zap/session_log.md — a dated history of every session: goal + files changed

What happens at session start

When you open zap again:

  • The "What was being worked on" line appears in the startup banner — you see it before you type anything
  • The full context.md is injected into the system prompt as ## Last Session Handoff — the agent already knows the context before your first message
  ◌ Last: Refactoring session/mod.rs into submodules
  ◌ Files: src/session/commands/code.rs, src/session/turn.rs

Agent memory across projects

/memory set key value persists facts (API patterns, team conventions, preferred approaches) that are injected into every session, across every project:

/memory set error-style  always use anyhow::Context for wrapping errors
/memory set test-db      never mock the database — always use a test container
/memory list             show all saved facts
/memory del error-style  remove a fact

8. SLM Support — Local Models as First-Class Executors

zap is the only coding agent that runs local small language models as first-class executors for the mechanical bulk of coding work — while frontier models handle the thinking.

  • Run local models via LM Studio, Ollama, or any OpenAI-compatible endpoint — no API key, no data leaving your machine
  • Structured plan execution — a frontier model designs the plan, your local SLM executes it step-by-step (100% success rate on pre-written plans in research)
  • Pre-indexingzap --index-only builds an AST index so SLMs navigate code with code_map/find_definition instead of slow manual reads
  • Streaming watchdog — tolerates long local-model prefills (minutes) with progress notices and idle detection
  • Core tool profileAGENT_TOOL_PROFILE=core sends only 6 tool schemas, making tool-calling tractable for small models

Full SLM support docs → · Research results →


Features at a glance

TUI Ratatui terminal UI — streaming output, sidebar with token counts, diff viewer (Ctrl+G), file browser (Ctrl+F), syntax highlighting
Providers LM Studio, Ollama, Anthropic, OpenAI, Gemini, DeepSeek, Groq, Mistral, xAI, Together AI, Perplexity, Cohere, OpenRouter + any OpenAI-compatible endpoint; per-provider settings persisted in ~/.agent.toml
Tools 15 built-in — read, edit, write, batch-edit, undo, shell, search, glob, code-map, find-def, find-refs, web-fetch, web-search, spawn-agent
Languages AST index: Rust, Python, TypeScript, JavaScript, Go, Java
Code quality /index quality — god objects, coupling, dead code candidates, quality score (0–100); reference counts computed in one O(source-size) pass
Index usage logging Every tool call answered by the AST index is logged to ~/.zap/zap.log and audit.jsonl — hit/miss per turn, auditable
Permission modes ask (grouped prompt per destructive op), auto (approve all), deny (fully read-only); "always" grant auto-approves a tool class for the session
Skills 23 built-in; always-on + keyword-triggered; user skills in ~/.zap/skills/ or .zap/skills/; SKILL.md standard compatible with Claude Code and Cursor
Skill trace /skill log — see which skills fired (or why they didn't) for every turn this session
Skill capture /skill capture <name> — extract session rules into a reusable skill file
Skill scope /skill scope — pin or restrict which domain skills are active for a session without editing files
Deploy /deploy — builds and installs zap with live streamed output; no shell timeout
Context mgmt Skill injection, casual-turn optimization (~20 tokens for greetings), sliding history window, tool-result pruning, /compact in-place summarisation, Anthropic prompt caching
Project init /init — auto-detects stack, indexes codebase, and generates ZAP.md + .zap/understanding.md filled with project-specific architecture, build commands, and constraints; ~30 seconds to full context awareness
Project intelligence .zap/context.md session handoff (last goal, files touched, what's next); .zap/understanding.md LLM-maintained project knowledge; .zap/session_log.md session history — read on demand, not pre-loaded
Sessions Every conversation persisted; /sessions fuzzy picker to resume any
Branching /branch forks a conversation like a git branch; /switch to move between them
Sub-agents spawn_agent runs parallel sub-agents with their own tool loop; multiple spawns in one turn run in parallel
Autonomous loop /goal <condition> runs turns automatically until the model signals done or a turn limit is reached
Extended thinking /think [on|off|N] — Anthropic extended thinking with configurable token budget
MCP (lazy-loaded) Standard .mcp.json format — works in Claude Code, Cursor, Kiro; servers connect on demand, zero cost until first use
Workflows Declarative YAML multi-step pipelines in .zap/workflows/ — versioned with your repo
Hooks PreToolUse / PostToolUse / SessionStart / SessionEnd / UserPromptSubmit — shell commands that run on agent events
Images /attach <path> or /paste clipboard — multimodal on supported models
Audit log Every tool call written to ~/.zap/audit.jsonl
Secret scanner 25+ patterns — Anthropic/OpenAI/Stripe keys, GitHub/GitLab tokens, AWS/GCP credentials, private keys, JWTs, generic password/api_key/secret fields — blocked before sending to any cloud LLM
Cost display Token breakdown per turn — skills, message, context, estimated $

Token efficiency — smart context detection

Pure greetings and social messages use a minimal 31-token prompt with no tools, even mid-conversation. Everything else gets the full context injection:

Message After model asks a question? Result
"hi", "hello", "hey" yes ~31 tokens (casual)
"thanks", "thank you", "ty" yes ~31 tokens (casual)
"good morning", "how are you" yes ~31 tokens (casual)
"yes" yes full context
"go ahead" yes full context
"ok", "cool", "sounds good" yes full context
any technical question full context

After the model asks a clarifying question, short replies like "yes", "ok", "go ahead" are treated as answers and receive the full context. Pure social messages always stay casual.


Install

macOS / Linux — one-liner

curl -fsSL https://raw.githubusercontent.com/zap-coding-agent/zap-coding-agent/main/install.sh | bash

The script detects your OS and architecture, downloads the latest release, installs to ~/.local/bin, and patches your shell config if needed. On macOS it also runs codesign --sign - automatically (required on macOS 26 Tahoe).

Platform Binary
macOS Apple Silicon (ARM64) zap-macos-arm64.tar.gz
macOS Intel (x86_64) zap-macos-x86_64.tar.gz
Linux x86_64 zap-linux-x86_64.tar.gz

Windows x86_64

Downlo