A true gift of freedom and privacy. Zero-telemetry Β· provider-neutral Β· phone home to no one. Current build:
v10.0.4β subscription accounts, security harness add-ons, and hardened self-hosting in the full-width retro terminal.
Shadow is a zero-telemetry, provider-neutral security engineering agent that runs on your terms. Point it at any model β Anthropic, any OpenAI-compatible endpoint, Gemini, or a local model on your own box. Its built-in Security foundation covers investigation, hardening, incident response, defensive automation, secure coding, and supporting systems work. No Shadow account, no signup, no phone-home: network access follows your provider, tool and update choices. Your config stays local and readable (~/.shadow/config.json), credentials are sent to their configured services, and you can switch models mid-session without losing context.
We're not competing for "coding-tool" mindshare β we're handing you back control: local-first autonomy, real guardrails, and full ownership of your workspace and your data.
Under the hood it's a tool-calling agentic runtime: the model reasons, emits tool calls, Shadow executes them against the OS through a bounded, observable loop with a configurable permission model and enforced guardrails (workspace jail + OS sandbox), and loops until the task is done or a stop condition fires.
This is not a chat app β it is a tool-calling runtime.
- Subscription accounts: sign in with ChatGPT directly or connect the official Claude Code CLI. Account identity survives model/profile changes and never silently falls back to API billing.
- Reliable onboarding: choose a subscription, cloud provider, local model server, or local file through one keyboard-driven flow. Endpoint checks are bounded and retryable, and failed entries remain editable.
- Security foundation and harness add-ons: every session starts from Shadow's provider-neutral Security foundation. Trusted packages under
~/.shadow/harnessescan add scoped instructions, skills, and tools for new sessions without choosing your model, endpoint, credentials, permissions, or sandbox. - Local skill learning:
/learncreates evidence-linked skill candidates;/skillssupports explicit review, activation, revision history, and rollback. Shadow adds no telemetry or callback service. - Hardened self-hosting: trusted profiles can pair a model preset with a harness and runtime limits. Self-hosted SGLang/Qwen presets can explicitly control chat-template thinking without leaking that extension to cloud providers.
- Cleaner terminal output: completed reasoning collapses into one expandable row, models that emit no reasoning create no empty panel, and repeated grep activity is grouped. Escape interruption remains covered during text, first-token, and thinking streams.
Harness selection is fixed when a session starts; mid-session harness switching remains future work. See the user guide, terminal contract, and release notes.
- Recoverable work: browse active workers through
/work, inspect persistent attempts and blockers in/jobs, and exchange local project messages through/room. Restarting a session never automatically repeats commands or edits. - Review and retain changes:
/reviewbrowses working changes, branch comparisons and commits. Worker worktrees and patches remain available to inspect, apply, keep or discard explicitly. - Independent consultations:
/consultuses a separate read-only conversation with a selected model profile. Follow-ups survive session resume without changing the lead session's model. - Bounded collaboration:
/teamoffers second opinion, implement-and-review and parallel-team presets, with pipeline, debate and plan/solve options. Child workers inherit permissions, cancellation and budgets; recorded checks remain separate from model opinions. - Context and connectors: ranked repository excerpts, semantic navigation with labeled lexical fallback, scoped instructions, skill origins and memory provenance. Snowfall's
/mcpadds live reconnect/disable controls, progress, deadlines and large-result retrieval. - Terminal and response fixes: more footer spacing, elapsed minutes, visible
/effortchoices, separate thinking blocks, preserved structured tool calls and partial results, and clearer interrupted/cancelled states. - Privacy by default: jobs, rooms and progress records are stored locally. No analytics or crash uploads; update discovery is off unless enabled in trusted user configuration. Project files cannot enable it.
Start with the collaboration guide, user guide and terminal contract. Workers stop when Shadow exits; remote MCP cancellation is best effort, and missing provider usage remains unknown. See release notes.
- Guided setup in the Snowfall style: choose Local file, Model server or Cloud, with compact menus, arrow keys, search and a review before saving.
- Reliable endpoint setup: cancellable checks, clear time limits, masked keys, and retry/edit controls that keep your entries when a server fails. Each endpoint keeps its own credentials.
- More providers: Cerebras, Fireworks, DeepInfra, Hugging Face, NVIDIA NIM, vLLM, SGLang and llama.cpp join the catalog without crowding the first screen.
- Named conversations: sessions take a name from the opening prompt. Use
/renameto choose one, find it in/resume, and see it in the terminal title./newpreserves the previous conversation.
See the 10.0.1 verification record and release notes.
- Snowfall is now the default terminal: a full-width conversation, pinned composer and compact footer keep your work centered. The original thick SHADOW wordmark returns in cyan with responsive fallbacks for narrow terminals.
- A navigable transcript: page and wheel scrolling, prompt jumps, text selection, search, folded tool details and scrollback restoration on normal exit.
- Everyday controls: queued follow-ups, permission dialogs, custom slash commands, external-editor access and accessible themes share the existing agent runtime.
- Work Center stays available: v9 agent, shell and task tracking, persisted history, and explicit controls continue across terminal, web and ACP.
- Compatibility:
SHADOW_TUI=ink shadowretains Vim mode, round-table mode, custom status lines and configurable key mappings. Headless, REPL, web and editor integrations keep their existing entrypoints.
See the terminal contract, release verification, and release notes.
- Work Center:
/worklists agents, background shells and plan items with status, ownership, elapsed time and bounded activity history. Inspect details, filter by type/status/tool/file, and view persisted cross-session history without restarting old work. - Explicit controls: pause/resume background agents at safe boundaries, adjust queued priority, cancel active background agents, stop owned shells, and request a linked retry with explicit confirmation. Historical work remains read-only.
- Shared state: terminal, web companion and the versioned ACP Work Center extension use the same work state and control boundaries.
- Full SHADOW wordmark: the two-tone 51-column mark is restored; medium-width layouts stack the full mark before falling back to a compact header.
- Renderer contract: In v9, Ink was the supported default and
SHADOW_TUI=pi shadowselected the experimental preview. Its daily commands and session transitions are covered by regression tests;/vim,/table,/statusline, custom commands and custom key mappings remain Ink-only. See Terminal renderers. - Approval previews make terminal and invisible controls visible without altering the original tool inputs. Session resume, rewind, fork and exports preserve the documented context and ownership boundaries.
See the release notes and Work Center guide.
- Installers and standalone updates now use Blackfrost's signed release bucket.
- Existing binary users can run
shadow updateonce through the legacy host to receive this transition, then future updates use Blackfrost. - Windows PowerShell 5.1 remains supported. Signature checks are mandatory by default; no extra PowerShell installation is required. Standalone self-update verifies and replaces the binary directly without spawning PowerShell.
shadow doctor --privacyidentifies manual release downloads separately from optional version checks.shadow update --offlinerefuses before making a request.- Updated compatible dependency versions to resolve the reported Undici and js-yaml advisories.
- Includes the previously published fix for preserving partial sub-agent findings at the iteration limit.
The product page and installer entrypoints are at blackfrostai.com/shadow. Source releases and binary releases have separate publication steps.
- π Subscription auth that carries its own endpoint. An imported Codex/ChatGPT credential is now used whole: the bearer travels with the base URL, the account/product identity headers and the wire it is bound to, instead of being recombined by hand. If a configured
baseUrlis not the backend that token belongs to, Shadow refuses it and says so rather than quietly sending a ChatGPT credential somewhere else. Tokens refresh themselves before they expire (they live about an hour), andshadow login statusreports the stored credential, its endpoint, its expiry and the opt-in gate β a stored-but-inactive credential used to be indistinguishable from no credential at all.shadow login import codeximports;shadow login codexexplains. - π Inference boxes that describe themselves. Mark a preset
autoModel(or runshadow local add --endpoint http://<host>:<port>/v1) and Shadow asks the box what it is serving on every start, using that model id and the context window the endpoint reports β so restarting a self-hosted box with a different model needs no config edit at all. A base URL written with or without/v1self-corrects, and the three failure causes are told apart: unreachable, reachable-with-nothing-loaded, and an unrecognized catalogue. All of them are non-fatal: Shadow names the box, keeps the model id the preset declares, and boots. - π‘ A 26-fix deep-review hardening pass. An adversarial review of the runtime found and fixed real defects across the stack: a filesystem-jail escape where
..was resolved before the symlink walk (<ws>/link/../outside/xwas judged inside while the OS opened a file outside); shell commands that rode input redirection or a session grant out of the jail; a read-before-edit guard that forgot every file the moment you sent a second message; redaction that returned a shared object reference raw and missed PEM private keys;run_shellleaving stdin an open pipe, socat/read/pythonblocked for the full timeout; a text-recovery scanner that was cubic in the reply length (a degenerate repetition cost 41s at 30 KB and 149s at 45 KB β now about a millisecond); tool-call arguments that several OpenAI-compatible servers send as a JSON object being silently dropped; and terminals that batch keypresses into one read typing a key's bytes into the draft. Plus LSP diagnostics that stopped for a file after a server restart, and session retention that ranked the newest log by filename instead of by time.
Earlier highlights:
-
v8.6 β the endpoint harness: the web console's Endpoints pane (pinned permanent hosts with a port-resolver doctor, quick-pick probes, an opt-in bounded LAN scan, one-click promotion), editable + testable saved connections, source-faithful
Ctrl+X Cdraft copying with display-column caret navigation, and a first-class self-hosting guide with a no-network provider doctor. -
v8.5 β discovery + hardening: onboarding model discovery (the wizard probes your endpoint and saves your allowlist as
/modelpresets), the deep-review hardening line (25 adversarially-verified findings fixed), and the correctness sweep. -
v8.4 β the UX parity line: first-run auto-onboard, Shift+Tab plan mode, Ctrl+X M model picker,
/export html, stableweb.token, auto-format after writes, Claude-session import, spend guardrails, LSP diagnostics after writes, and/goalmission mode. -
v8.0β8.3 β the web console era:
shadow web(sessions sidebar, streaming transcript, approval dock, inspector), toasts + context gauge, stream resilience, self-documenting config + instruction-file autopilot. -
π Collaboration Mode β a live model round-table.
/table gpt glmseats 2β4 models in one shared conversation; you hold the baton and route each turn with@handle. Compare reasoning side by side, or let a strong model check a fast one. -
π Charts, tables & a genuinely beautiful TUI. Fenced
chartblocks render as real unicode bar / line / sparkline graphs; GFM tables get rounded grids with ledger-aligned numbers; markdown, code, and diffs are calm and readable. -
βΏ Accessibility, first-class. A
colorblind(OkabeβIto) palette and ahigh-contrasttheme; user prompts have a filled background band and failed tools aβmarker. -
π Copy & paste that just works. Multi-line paste is atomic (embedded newlines never fire a stray send),
Ctrl-Vpastes from the system clipboard,Alt-Ccopies the last answer,/copy codegrabs the last code block. -
β¨ A slash menu that anticipates you. Fuzzy matching (
/thmβ/theme), inline argument completion (/theme β£lists every theme with aβ currentmarker), and did-you-mean on typos. -
π₯ Three local backends, auto-served.
.gguf(llama.cpp), MLX (Apple Silicon β text and multimodal via mlx-vlm), and vLLM (Linux + CUDA) β point an entry at a model and Shadow launches the server for you. -
π§ Never loses the plot. Context compaction now pins your task verbatim, so a long session (or a weak local summarizer) can't make the model forget what it's doing.
| Milestone | What | State |
|---|---|---|
| M0 | Skeleton, config, provider-neutral block model, mock provider, headless loop (termination + budget), approval gate, REPL | β |
| M1 | Tools: read_file write_file edit_file grep glob run_shell (zod-validated, structured results) |
β |
| M2 | Safety: symlink-aware workspace jail, catastrophic-command denylist, autonomy levels, approval flow, dry-run, SSRF netguard | β |
| M3 | Ink TUI HUD (streaming output / status bar / inline approval dialog / two-stage Ctrl-C) | β |
| M4 | Append-only redacted session logs + project-facts memory (memory tool) |
β |
| M5 | Real providers (Anthropic + OpenAI-compatible, streaming, prompt caching, retry), web tools | β |
| M6 | Anthropic-compatible harness parity: plan/ask/export, approval taxonomy, fallback, permission rules, hooks, MCP/skills/agent | β |
| M7 | Format-adaptive universality β dual transport + auto-detect, text-tool-call recovery, control-token scrub, three tool-call signature regimes (Anthropic signed / Gemini thought_signature / plain OpenAI); validated against a 9-model test program |
π§ |
Per-version detail ships with each release.
Shadow ships as a single self-contained binary (no Node needed to run it). The installers are served from blackfrostai.com/shadow, with reviewed copies in this repository. They verify the signed manifest using a pinned release key.
curl -fsSL https://blackfrostai.com/shadow/install.sh | shirm https://blackfrostai.com/shadow/install.ps1 | iexThe installer detects your platform, downloads the matching binary, verifies it (see below), and drops it on your PATH. Update: shadow update. Uninstall: delete the binary (rm "$(command -v shadow)").
Shadow is a security tool, so the installer fails closed. It downloads SHASUMS256.txt plus an ECDSAβP256 signature (SHASUMS256.txt.sig) made with an offline release key, verifies that signature against the public key pinned in the installer, and only then checks the binary's SHAβ256 against the signed manifest. A compromised download host can't forge the signature, so a tampered binary is rejected β the install aborts. (Verification uses openssl on macOS/Linux; on Windows, PowerShell 7.1+ verifies via ImportFromPem and Windows PowerShell 5.1 β stock Windows β verifies via .NET Framework's native ECDSA; runtimes with neither abort.) The release public key is public/bin/SHASUMS256.pub and is embedded in install.sh/install.ps1 β read them before piping to a shell.
Requires Node.js β₯ 22.19 and git:
git clone https://github.com/Blackfrost-AI/Shadow_CLI.git && cd Shadow_CLI && npm install && npm run build && npm linkThen shadow --help from anywhere.
A fresh install ships with no provider configured β Shadow won't run until you connect one. Just launch it:
shadowOn first run it opens with the mode chooser β the real first question:
1. Local file β a .gguf or MLX model on this machine (auto-served)
2. Local server β Ollama / LM Studio / llama.cpp already running
3. Cloud β Anthropic, OpenAI, Alibaba Qwen (DashScope), Z.ai (GLM), OpenRouter, β¦
Local file asks for a .gguf path, registers + activates it, and offers a live test β no key, no cloud, done. Local server and Cloud open the provider menu filtered to what fits (Anthropic, OpenAI, Alibaba Qwen/DashScope, OpenRouter, Groq, DeepSeek, Mistral, xAI, Gemini, Together, Z.ai (GLM), Ollama, LM Studio, or a custom endpoint), where you paste your API key (masked as you type), choose a model, and Shadow runs a live connection test before saving. Type back or b at any onboarding prompt to return to the previous step without restarting. Re-run it anytime to switch:
shadow onboard # change provider / model / key (terminal flow)
shadow onboard --web # secure setup in a local browser form β encrypted vault + master passwordFor encrypted-at-rest keys, use shadow onboard --web: it opens a self-contained form on 127.0.0.1 and seals your key into a password-protected vault (~/.shadow/vault.enc) rather than a plaintext file β see Security model. Re-running it merges another provider's key into the same vault. The plain shadow onboard terminal flow remains available.
Your choice is saved per-machine to ~/.shadow/ β config.json (provider + model) and either the encrypted vault.enc (with --web) or credentials.json (key/token, chmod 600). Nothing is committed to the repo; every machine sets up its own. Environment variables (ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL, OPENAI_API_KEY, β¦) and CLI flags still override the saved config, so CI and scripted runs can stay key-in-env. In a non-interactive context (--task, piped, no TTY) with nothing configured, Shadow exits with Run \shadow onboard` to set one up` instead of hanging.
shadow # interactive HUD in the current directory
shadow --task "fix the failing tests" # one-shot, scriptable
shadow --yolo --task "build the app" # fully autonomous β no prompts (see Autonomy)
shadow --provider mock --task hi # no API key needed (deterministic mock)
shadow web # browser console on 127.0.0.1 (see below)
shadow export # export latest session to exports/*.mdA browser workspace for the same agent: sessions sidebar, a chat transcript with tool cards and approvals, an inspector with local usage records, and a settings sheet covering models, agents, MCP servers and the project allowlist. The access token travels only in the URL fragment (never sent to the server), the listener binds loopback only, sessions run inside the same allowlist jail as the CLI, and every asset is served from the binary β no CDN, no external requests.
Export the current (or a past) conversation as readable markdown:
/export # in the TUI β writes <workspace>/exports/shadow-<timestamp>.md
/export reports/run.md # optional output path (resolved within the workspace)
shadow export # CLI β latest session in cwd
shadow export --session .shadow/sessions/<file>.jsonl --out reports/run.mdSource of truth is the append-only session log (.shadow/sessions/*.jsonl), not the TUI render cache.
npm install
npm run typecheck && npm run lint && npm test # all green
# Interactive (TTY) β launches the Snowfall terminal:
ANTHROPIC_API_KEY=sk-ant-... npm run dev -- --system ./prompts/SHADOW.md --autonomy auto-edit
# One-shot, non-interactive (plain renderer, scriptable):
ANTHROPIC_API_KEY=sk-ant-... npm run dev -- --task "add a test for parseConfig and run it"
# No API key needed β deterministic mock provider:
npm run dev -- --provider mock --task "scan repo"
# Build a runnable binary:
npm run build && node dist/index.js --help # or link `shadow` via package.json binnpm run dev runs the TypeScript entry directly via tsx; npm run build compiles to dist/.
--system <path> external system-prompt markdown
--autonomy <level> manual | auto-read | auto-edit | full (default auto-edit)
--provider <name> anthropic | openai | mock
--model <id> model id (default claude-opus-4-8)
--base-url <url> override provider base URL (also ANTHROPIC_BASE_URL / OPENAI_BASE_URL)
--max-output-tokens <n> per-call output cap (raise for verbose "thinking" models)
--max-iterations <n> loop iteration cap (default 200; raise for big multi-file tasks)
--context-budget <n> token budget before summarization (default 128000)
--max-wall-sec <n> wall-clock ceiling in seconds (safety stop for long autonomous runs)
--workspace <path> workspace root (default cwd) β all file paths resolve under this
--style <name> proactive | explanatory | learning | procedural
--plan-mode start in explore/plan mode before implementation
--dry-run write/exec tools become no-ops that report what they WOULD do
--task "<text>" run a single task non-interactively and exit (plain renderer)
--repl force the plain REPL even in a TTY (skip the Ink HUD)
--yolo bypass ALL permission checks (autonomy=full, auto-approve
everything incl. denylisted, never ask). Aliases: --nuke,
--dangerously-skip-permissions
--log-level <l> silent | error | info | debug
-v, --version print version
-h, --help show this help
Set with --autonomy (or SHADOW_AUTONOMY), toggle mid-session in the HUD with Shift+Tab.
| Level | Behavior |
|---|---|
manual |
confirm every tool call |
auto-read |
auto-approve read/search/glob; confirm write/exec/network |
auto-edit (default) |
auto-approve reads + writes inside the workspace; confirm exec/network |
full |
auto-approve everything except denylisted/destructive ops, which are always confirmed |
A catastrophic shell command (rm -rf /, mkfs, dd of=/dev/β¦, fork bombs, chmod -R 777 /, β¦) triggers an explicit confirmation regardless of level β even at full. The list is extendable via denylistExtra in config.
--yolo (aliases --nuke, --dangerously-skip-permissions) is the explicit sandbox-off + guardrails-off flag: forces full autonomy, auto-approves everything (incl. denylist), grants root to bypass workspace jail, and disables OS sandbox for run_shell (writes anywhere OS allows). Prints warning. Use only on trusted/throwaway envs. --no-sandbox is lower-level for just disabling the shell sandbox while keeping other guards.
Non-interactive runs (--task) have no human to ask, so anything that would reach the approval gate is denied (and fed back to the model as a recoverable error) rather than run blindly. Capability is therefore set entirely by --autonomy: use --autonomy full to let the agent run shell/network unattended; a denylisted command is still refused, never executed. This makes --task safe to script and to run in CI.
Shadow's system prompt (prompts/SHADOW.md, or the built-in default) instructs the driven model to keep the workspace organized β files go in logical subdirectories (src/, tests/, docs/, plans/, research/), not dumped in the root, and scratch is cleaned up β and to persist its thinking as markdown: a short plans/<name>.md before multi-step work (checked off as it goes), and research/<topic>.md for findings with concrete references. Substantial deliverables are written as files, not buried in chat. Point --system at your own prompt to override.
| Key | Action |
|---|---|
Ctrl-C (1st, while running) |
graceful stop β abort the in-flight tool/turn |
Ctrl-C (2nd, or when idle) |
quit |
Shift+Tab / Tab |
cycle autonomy level (applies live to a running loop) |
β / β |
move the caret within the composer (edit mid-line) |
β / β |
input history |
Esc |
abort the running task, or clear the composer when idle |
\ + Enter |
insert a newline in the composer (multiline prompt) |
Ctrl-O |
toggle collapse of the latest reasoning block (when idle) |
Enter while running |
interrupt the active turn and queue the typed text as the next prompt |
y / n / a |
in a permission dialog: approve / deny / always (approve + raise autonomy) |
s / f |
in a permission dialog: approve for session / approve shell command prefix |
1β9 / Enter |
in an ask_user_question dialog: pick an option / confirm (Esc to skip) |
Type / in the composer to open a command menu (with descriptions). Filter by
typing (/cl β /clear), β/β to select, Tab to autocomplete, Enter to run,
Esc to dismiss.
| Command | Action |
|---|---|
/help |
show keybindings and the command list |
/clear |
clear the screen and reset the conversation |
/model |
switch between configured models (picker when multiple) |
/style |
cycle output style |
/autonomy |
cycle the autonomy level |
/fast |
toggle Anthropic fast mode (lower latency; next turn) |
/compact |
summarize earlier turns to free context |
/cost / /usage |
show session token usage and cost |
/context |
show context-window usage |
/export |
export the session to markdown (optional path) |
/resume |
resume a prior session (optional session id/path) |
/rewind |
rewind to a turn index (e.g. /rewind 2) |
/init |
scaffold SHADOW.md in the workspace |
/agents |
list agent definitions |
/work |
inspect work, filter current/history items, and use explicit background controls |
/memory |
show project memory facts |
/permissions |
list or edit permission rules |
/doctor |
diagnose environment, credentials, and guardrails |
/quit |
exit Shadow |
While the agent is running, informational commands (/help, /cost, /usage, /context, /fast, /work) work without interrupting the turn. Renderer-specific commands and keys are documented in Terminal renderers.
π New here? The User Guide is the task-oriented walkthrough β connecting a model, tuning output length, reasoning effort, autonomy, and troubleshooting.
Layered precedence: CLI flags > env > shadow.config.json > defaults, validated with zod (fails fast with a readable message). maxOutputTokens (the per-call output cap) defaults to 65536 so reasoning models don't hit the cap before answering β change it per-run with --max-output-tokens <n>, live with /config set maxOutputTokens <n>, or permanently in the config file (details). Example shadow.config.json:
{
"provider": "anthropic",
"model": "claude-opus-4-8",
"autonomy": "auto-edit",
"maxIterations": 25,
"maxOutputTokens": 65536,
"contextBudget": 100000,
"maxToolResultChars": 16384,
"shellEnvAllowlist": ["PATH", "HOME", "USER", "LANG", "TERM", "TMPDIR", "SHELL"],
"denylistExtra": [],
"fallbackModel": "claude-sonnet-4-6",
"parallelTools": true,
"permissionRules": [
{ "tool": "run_shell", "pattern": "rm -rf", "action": "ask" }
],
"hooks": {
"pre_tool_use": ["scripts/pre-hook.sh"],
"post_tool_use": []
},
"mcpServers": {
"example": { "command": "npx", "args": ["-y", "some-mcp-server"] }
},
"models": [
{ "label": "opus", "provider": "anthropic", "model": "claude-opus-4-8", "fallback": "claude-sonnet-4-6" },
{ "label": "sonnet", "provider": "anthropic", "model": "claude-sonnet-4-6", "disabled": false }
],
"budget": { "maxTotalTokens": 2000000, "maxCostUSD": 5, "maxWallClockSec": 1800 },
"priceTable": {
"claude-opus-4-8": { "input": 5, "output": 25, "cacheReadMult": 0.1, "cacheWriteMult": 1.25 },
"claude-sonnet-4-6": { "input": 3, "output": 15 },
"claude-haiku-4-5": { "input": 1, "output": 5 }
}
}The loop always terminates β on natural completion, the iteration cap (maxIterations), a budget ceiling (maxTotalTokens / maxCostUSD / maxWallClockSec), or Ctrl-C β and never hangs silently on a provider error (retryable errors back off and surface; 400/401/403 surface immediately).
Shadow implements the same tool/mode contracts as the reference Anthropic client, so Anthropic models (and compatible endpoints) run without feature surprises.
| Capability | Shadow tool / surface |
|---|---|
| Enter plan mode | enter_plan_mode (user must approve) + Shift+Tab / --plan-mode |
| Write plan | plan_write |
| Exit plan mode | exit_plan_mode (separate approval kind from permissions) |
| Structured questions | ask_user_question (numbered options in TUI/REPL) |
| Session export | /export, shadow export |
| Model fallback | fallbackModel + per-model fallback / disabled in models[]; one auto-retry per session on 529/overloaded |
| Permission rules | permissionRules: per-tool deny / ask / allow with optional regex on preview |
| Hooks | hooks.pre_tool_use / post_tool_use β shell scripts, JSON on stdin; non-zero pre-hook denies |
| Live shell output | shell_output bus events (TUI + headless renderer) |
| Sub-agents | agent tool (isolated context sub-loop) |
| Scheduled wakeups | schedule_wakeup (in-session timer β queued prompt) |
| MCP | mcpServers in config (stdio or HTTP) β tools registered as mcp_<server>_<name> |
| Fast mode | --fast / /fast / SHADOW_FAST=1 β Anthropic low-latency path |
| Extended cache | cacheTtl: "1h" / SHADOW_CACHE_TTL=1h β prompt cache breakpoints |
| Multi-root | --add-dir / additionalDirectories β widen jail + shell sandbox |
| Background shells | run_in_background, bash_output, kill_shell tools |
| Multi-edit | multi_edit β atomic multi-hunk file edits |
| Skills tool | skill β invoke bundled skill scripts |
| Skills | skills/<name>/SKILL.md or .shadow/skills/ β indexed in system prompt |
| Parallel tools | parallelTools (default on; sequential during plan mode / gate tools) |
Shadow's limits are tunable, not hard-coded β on a self-hosted model where tokens are free and the context window is large, dial them up to match the box. For an ambitious multi-file build:
shadow --task "<big task>" --autonomy full \
--max-iterations 40 --max-output-tokens 24000 --context-budget 200000 --max-wall-sec 1800--max-iterations raises the turn cap (default 200 suits most tasks; a multi-file build with a testβfix loop wants more); --context-budget is how many tokens accumulate before summarization kicks in (a self-hosted model with a 256K window rarely needs to summarize at all); --max-wall-sec is a wall-clock safety stop so a long autonomous run still terminates. The termination guarantee is preserved at every setting β you can always raise the ceilings, but there is always a ceiling (and Ctrl-C).
API keys are read from the environment only: ANTHROPIC_API_KEY, OPENAI_API_KEY.
Both providers accept a base-URL override (--base-url, or ANTHROPIC_BASE_URL / OPENAI_BASE_URL / baseUrl in config). The Anthropic provider also accepts a bearer token via ANTHROPIC_AUTH_TOKEN (which takes precedence over x-api-key) β so Shadow is a drop-in against any Anthropic-Messages-compatible endpoint, including a local Ollama server:
ANTHROPIC_BASE_URL=http://your-host:11434 ANTHROPIC_AUTH_TOKEN=ollama ANTHROPIC_API_KEY="" \
npm run dev -- --provider anthropic --model <your-model> --autonomy auto-editThis has been verified end to end (multi-step read_fileβwrite_file tool calling, plus larger multi-file builds) against a self-hosted Ollama model. Note: smaller "thinking" models emit verbose reasoning that Shadow discards β give them generous maxOutputTokens so the budget isn't consumed before the tool call. OpenAI-compatible endpoints (e.g. Ollama's /v1) work the same way via --provider openai --base-url http://your-host:11434/v1.
MLX on Apple Silicon:
shadow local addalso takes an MLX model folder or anmlx-community/<model>repo id β a repo id downloads from HuggingFace once, then serves fully local viamlx_lm.server(uv tool install mlx-lm):shadow local add mlx-community/Qwen2.5-0.5B-Instruct-4bit # repo id (one-time download) shadow local add ~/models/My-MLX-Model # or a local MLX folder shadow local test <name> && shadow local use <name>
Requires llama.cpp β Shadow launches and manages the server for you, but the
llama-serverbinary must be installed. Install it withbrew install llama.cpp(macOS/Linux) or build from source, or point Shadow at an existing binary viaggufServer/$SHADOW_LLAMA_SERVER. When it's missing,shadow local add/testoffers to install it for you.
Point a model entry at a local .gguf and Shadow starts a llama.cpp server for it on activation (and shuts it down on exit), then talks to it over the OpenAI endpoint β no Ollama/LM Studio process to run:
Pick it from /model (or set it as the default) and Shadow serves it locally β first load shows a "loading the model into memory" note. An already-running server on the same port is reused. The context budget is capped under the server's -c automatically.
Point Shadow at a vision model you run, and every model you drive can see β even a text-only one. Vision in Shadow is a capability you plug in, not a property of your model: run any VLM behind an OpenAI-compatible endpoint (Ollama, vLLM, llama.cpp), add a vision block to ~/.shadow/config.json, and the driving model gains eyes by delegating. Ask "what's in this screenshot?" and it calls the describe_media tool β Shadow sends the image to your endpoint, gets a text description back, and the model reasons over it. A small local coder with no native vision can now read a UI mockup, a diagram, or a chart. The image goes only to the vision model you chose and host β nothing to a coding-model provider. vision is a project-untrusted key (a cloned repo can't redirect your images), and the tool is off under --offline. See VISION.md for setup.
/table <model> <model> β¦ seats 2β4 models in one shared conversation. You hold the baton and route each turn with @handle <question>; /pass @handle forwards it, /table done restores your single model. Each seat's turn is drawn under a colored βΊ handle provider/model header, so a multi-model thread reads as a legible group chat. Compare a fast local model against a frontier cloud one, or let a strong reasoner check a cheap coder β all in one context. (@mention routing is whitelist-only, so tool output can't hijack the baton.)
Shadow is format-adaptive, not capability-guaranteed. It speaks both the Anthropic Messages and OpenAI-Chat wire formats, recovers tool calls a model emits as plain text, scrubs leaked control tokens, and round-trips all three tool-call signature regimes (Anthropic signed thinking, Gemini thought_signature, plain OpenAI). What it can't do is make a weak model capable or turn a chat model into an agent. The rule of thumb: Shadow is for agentic, tool-calling models, run over the wire format they were trained for.
Shadow works with any agentic, tool-calling model β cloud frontier models (Claude, GPT, Gemini) or capable local models served over an OpenAI- or Anthropic-compatible endpoint (Ollama, LM Studio, llama.cpp, vLLM). The harness is model-neutral: it recovers tool calls emitted as plain text, scrubs stray control tokens, and round-trips all three tool-call signature regimes (Anthropic signed thinking, Gemini thought_signature, plain OpenAI), so a broad range of models drive the loop well.
Picking a local model:
- Use an instruct / tool-calling build, not a base or chat-only model β a chat model narrates but never calls a tool.
- Match the wire format to the model. Most local models speak OpenAI-Chat; a few are trained to emit Anthropic-format calls and should be routed over the Anthropic transport. First-run onboarding detects common cases and offers to switch.
- A dedicated GPU and more parameters mean more reliable multi-step work, but capable ~12Bβ35B instruct models handle everyday tasks well.
Rules of thumb: if a model calls tools cleanly but produces the wrong work, that's a capability limit no harness can fix β reach for a stronger model. If it never emits a tool call, it's a chat model, not an agent.
Per workspace, under <workspace>/.shadow/:
sessions/<timestamp>.jsonlβ append-only, redacted event log (replayable; source for/export).memory.jsonβ durable "known facts" (build/test commands, key files, conventions). The agent reads these at startup and writes new ones via thememorytool; they survive restarts.exports/β markdown session exports (created by/exportorshadow export).skills/β optionalSKILL.mdfiles for progressive-disclosure skill injection.
The full picture β including what Shadow does not protect against β lives in THREAT_MODEL.md. Limits are stated with the same prominence as guarantees.
- Workspace jail β every file path resolves to an absolute path contained within the workspace root;
..traversal, absolute-outside paths, and symlinks pointing outside the root are rejected (including for not-yet-created files). - Shell β
spawn(neverexec) withcwd= workspace root and an env allowlist; provider API keys are never passed into subprocesses. - OS sandbox for
run_shellβ on macOS (seatbelt /sandbox-exec) and Linux (bubblewrap /bwrap), shell commands run confined: filesystem writes are restricted to the workspace +/tmp, and reads of~/.shadow(the credentials store) are denied β so a command can't trash files outside the workspace or read your API key. Network is allowed by default (installs/fetches); setsandboxNetwork: falseto deny it, orsandbox: "off"/--no-sandboxto disable confinement entirely. This is the real boundary; the env-allowlist + denylist are defense-in-depth. (No OS sandbox on Windows βrun_shellis unconfined there.) - Web tools β gated as
networkrisk; an SSRF guard blocksfile:///non-http schemes and any host resolving to loopback / private / link-local / cloud-metadata (169.254.169.254) addresses, pins the connection to the validated IP (defeats DNS-rebinding), and re-validates + re-pins each redirect hop. Fetched content is treated as untrusted data β instructions inside it are never followed. - Secret hygiene β keys from env/credentials store only; the project
shadow.config.jsoncannot set security-critical fields (baseUrl/autonomy/etc.); session logs and surfaced errors are redacted (resolved keys masked by value; best-effort, not a guarantee). - Encrypted credential vault (optional) β instead of a plaintext
credentials.json, keys can be sealed in~/.shadow/vault.encwith a master password: scrypt (N=2ΒΉβΆ) β AES-256-GCM (authenticated β a wrong password or a tampered file simply fails to open). Set it up withshadow onboard --web, which opens a local, self-contained browser form (bound to127.0.0.1, one-time token, a strict CSP that blocks any outbound request β a key typed there physically cannot leave the machine) and encrypts on submit; re-running it merges more providers into the same vault. On the next run Shadow unlocks the vault via the OS keychain (macOS Keychain / libsecret / Windows DPAPI) so you type the password once; on a box with no keychain it falls back to prompting each session, or readsSHADOW_VAULT_PASSWORDfor headless/CI. If you already have a plaintextcredentials.json, the first interactive run offers to encrypt it into the vault and then shreds the plaintext (overwrite-then-remove). Env vars still override everything, so nothing about existing key-in-env workflows changes.
Shadow includes no analytics service, crash uploads or hosted coordination service. Sessions, jobs, rooms and progress records are stored locally. Prompts and selected context/tool output go to your configured model provider. Enabled MCP servers, invoked web tools, plugin operations, model downloads and updates have their own network behavior. Programs you configure can also access the network under their own settings.
Verify it yourself: shadow doctor --privacy prints exactly what the active config can send β every outbound egress path (model provider, web tools, MCP servers, the opt-in update check) with each marked live or inactive, plus where your keys live (encrypted vault vs plaintext) and whether offline mode is usable. The report makes no network calls β it inspects your config and local state only. Add --offline to see the offline posture (everything but a local model blocked).
Update discovery is off by default. To enable notices, set "updateCheck": true in your
trusted global ~/.shadow/config.json; a project-local config cannot change this choice.
When enabled, Shadow checks the public repository's version on launch, with a local daily cache,
and displays a notice. The request contains no analytics payload, persistent identifier or
credentials, but the destination receives normal connection metadata such as your IP address.
It does not install a release automatically. shadow update is a separate explicit download;
offline mode blocks both network paths. With discovery disabled, no discovery request is made.
Shadow runs on macOS, Linux, and Windows β the runtime is Node, and the platform-specific surface is handled:
run_shellruns in your shell ($SHELL//bin/sh) on macOS/Linux and in PowerShell on Windows, with a per-platform env allowlist (Windows getsSYSTEMROOT/PATHEXT/etc.; provider API keys are never passed on any platform).grepusesrg(ripgrep) when a real binary is onPATH, otherwise a built-in cross-platform Node scanner.- The workspace jail, file tools, and provider streaming are platform-neutral.
macOS and Linux are the most battle-tested (incl. a live multi-model stress suite). Windows support is newer β the install path and PowerShell run_shell are in place; the catastrophic-command denylist patterns are unix-oriented, so on Windows add PowerShell equivalents via denylistExtra if you rely on it.
npm run typecheck # tsc --noEmit, strict
npm run lint # eslint
npm test # node:test β unit + headless integration of the full loop (mock provider, no network/API key)
npm run format # prettierThe agent loop runs headless (no Ink) under test, driven by a deterministic mock provider and a scripted approval gate β see test/. Layers are decoupled: the loop never imports Ink; tools never import the provider.
See DECISIONS.md for every default and deviation.
