tapflow
팀 전체가 사용할 수 있는 자체 호스팅 iOS·Android 시뮬레이터 스트리밍
kimiflare: Cloudflare Workers AI 기반 자체 호스팅 터미널 AI 코딩 에이전트
A self-hosted AI coding agent for your terminal, powered by Kimi K2.7 on Cloudflare Workers AI. Runs on your own Cloudflare account — open source, with AI Gateway logs, caching, and authoritative cost
개발 도구TypeScript · HTMLGitHub @directionally-local온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
Moonshot kimi k3 terminal based coding agent & harness running on your own Cloudflare account.
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A terminal coding agent that runs any model on OpenRouter with your own key.
One key, 400+ models, per-turn billed cost.
autopilot sends every model call to OpenRouter using your own OpenRouter API key — bring-your-own-key, nothing proxied, nothing marked up. On first run you paste the key once (it's checked against OpenRouter before it's saved) and pick a model. That's the whole setup.README 전문 보기 ↗curl -fsSL https://dl.new | sh
That installs dl (Directionally Local's installer), which installs and runs Autopilot. Under dl, Autopilot shows dl's name and version (dl 0.15.0, "Ask dl anything") and dl --version names both. dl keeps Autopilot on a version it has tested and updates them together, so use dl's update command, not npm, to update.
Autopilot on its own, without dl: npm i -g autopilot-ai and run autopilot.
On the free tier, model calls go through Directionally Local's free tier service to its model host; it keeps usage counts, never prompts or code. With your own key, autopilot sends every model call straight to the provider you choose — nothing proxied, nothing marked up. On first run you pick a provider, connect it once (the key is checked before it's saved) and pick a model. That's the whole setup. OpenRouter is the recommended default; see Providers for the rest.
On OpenRouter you also get:
/model.≈$ only until the billed number arrives). /cost totals it by session, day, month and all time.auto until you turn them on with /settings modes on (/settings modes off turns them off again). With modes on, the session starts in edit and Shift+Tab or /mode switches between them. SDK sessions always have modes and start in edit. plan is a whitelist-only research mode: only read-only tools (read, glob, grep, web search, GitHub read-only, browser fetch) are allowed. Writes, edits, mutating bash, MCP tools, and LSP renames are all blocked. edit prompts per mutating call. auto approves everything for trusted tasks. In plan and edit, reading a credentials file (~/.ssh, ~/.aws, Autopilot's own config with your provider keys, .env, /proc/*/environ) also asks first, so a prompt injection cannot quietly read a key and send it out with web_fetch. Likewise web_fetch and browser_fetch ask before reaching a local or private network address (localhost, 127.0.0.1, 10.x/192.168.x, cloud metadata at 169.254.169.254, including names and redirects that resolve there), approved per origin, so a prompt injection cannot read metadata credentials or local services./undo — Irreversible actions always ask, even in auto, under --dangerously-allow-all, or under a broad allow rule: deleting untracked work, git reset --hard, force pushes, dropping a database. A production-looking target needs its name typed, not just Enter. Before a destructive file or git action Autopilot saves a recovery point outside the repo, and /undo restores the latest one. /audit reports what the agent could destroy from here and what would save you.CLAUDE.md, GEMINI.md, Cursor rules, Copilot instructions, Claude Code commands and MCP servers and uses them in place; /setup-import shows the checklist again. Nothing is copied into the repo or sent anywhere.cmd.exe, PowerShell, or bash based on platform. Override with AUTOPILOT_SHELL or /shell.↑/↓, j/k, Alt+1/2/3.createAgentSession API and JSONL-over-stdio RPC mode for building on top of autopilot.See the full changelog at github.com/directionally-local/autopilot/releases.
npm install -g autopilot-ai
autopilot
On first run, autopilot asks "How should it think?": choose Free — sign in with GitHub (50M tokens a week, no credit card) or Use my own API key to pick a provider such as OpenRouter, then pick a model. A provider key already in your environment skips the question and is used as-is. That's it.
Or install with Homebrew on macOS or Linux (Homebrew installs Node.js for you):
brew install directionally-local/tap/autopilot
autopilot
Or run without installing:
npx autopilot-ai
Coming from
kimiflare? The package was renamed. Runnpm uninstall -g kimiflare && npm install -g autopilot-aiand useautopilotinstead ofkimiflare. Your settings, keys, sessions, memory, traces and cost history are kept: on first start Autopilot moves~/.config/kimiflareto~/.config/autopilot,~/.local/share/kimiflareand~/.kimiflareto~/.local/share/autopilot, and~/.local/state/kimiflareto~/.local/state/autopilot(honouringXDG_*_HOME), and leaves a symlink at each old path so an olderkimiflarebinary still finds the same files. A file that already exists in the new folder is never overwritten; it stays in the old folder and Autopilot names it once. Project.kimiflare/folders are not moved: Autopilot reads.autopilot/first, then.kimiflare/, and creates only.autopilot/.KIMIFLARE_*environment variables still work.
Requires Node.js 22.23.4+, 24.22.1+ or 26.12.1+ (earlier releases have the Brotli bug CVE-2026-111497, and web_fetch refuses to run on them).
If npm warns that it blocked isolated-vm's install script, nothing is broken: isolated-vm is an optional dependency that ships prebuilt binaries for Node 22 and 24, and on a Node version without one Autopilot runs with Code Mode's sandbox off and tells you once per session how to build it.
To pick up an earlier conversation, run autopilot resume <id> (the exact command is printed when you exit), autopilot resume to choose one from this directory, or autopilot -c for the most recent one. Inside autopilot, /resume does the same.
To continue a Claude Code conversation (say it stopped on a usage limit), run autopilot --from-claude-code in the same folder. It finds that folder's most recent conversation in ~/.claude/projects, asks first, and starts a new session whose first message is a short handoff: your last request, the files changed, commands run and last errors, and Claude's last message (at most 12,000 characters, likely keys redacted). The transcript is read on this machine and goes only to your model, as the session's first message.
| Provider | id | Key | Notes |
|---|---|---|---|
| OpenRouter (default) | openrouter | OPENROUTER_API_KEY or browser sign-in | 400+ models, billed cost per turn, sticky routing, subagents, Jev checks |
| Anthropic | anthropic | ANTHROPIC_API_KEY | native Messages API: extended/adaptive thinking, explicit prompt caching (API keys only — Claude Pro/Max sign-in isn't permitted for third-party tools) |
| OpenAI | openai | OPENAI_API_KEY | Responses API: reasoning carries across tool loops (encrypted, stateless), codex models |
| Google Gemini | gemini | GEMINI_API_KEY / GOOGLE_API_KEY | OpenAI-compatible endpoint; Gemini 3 thought signatures are kept with each tool call |
| ChatGPT (Plus/Pro/Team plan) | chatgpt | autopilot auth chatgpt (browser sign-in with token sharing) | your plan's GPT models over the Responses API; tokens refresh automatically; usage counts against your plan. Uses OpenAI's official token-sharing sign-in, not the Codex backend |
| GitHub Copilot | copilot | autopilot auth copilot (GitHub device login; --enterprise <domain>) | your Copilot plan's models — Claude over Messages, GPT over Responses; usage counts as premium requests. Not a documented public API, so GitHub may change it |
| Google Vertex AI | vertex | Google credentials (gcloud auth application-default login, a service-account key in GOOGLE_APPLICATION_CREDENTIALS, or GOOGLE_VERTEX_ACCESS_TOKEN) + GOOGLE_VERTEX_PROJECT (or autopilot auth vertex --project …) | Gemini and partner models through Vertex's OpenAI-compatible endpoint, Claude through Anthropic's Messages API on Vertex; location GOOGLE_VERTEX_LOCATION (default global) |
| Amazon Bedrock | bedrock | AWS_BEARER_TOKEN_BEDROCK (Bedrock API key), or AWS credentials (env or ~/.aws/credentials / ; for SSO, ); region |
/provider <id> [key] (/provider lists them and where each key comes from), or from the CLI: autopilot auth <id> (saves a key and makes it active), autopilot auth use <id>, autopilot auth status. For one run, set AUTOPILOT_PROVIDER=<id>.autopilot connect runs the first-run screens on their own — the searchable provider list, key entry checked with the provider, then its live model list — and saves the choice, so a plain autopilot uses it. Installers and scripts can hand setup to it instead of asking for a key themselves: --state-file <path> writes the result (provider, name, baseUrl, apiKey, apiKeyEnv, keyRequired, model, authHeader, authFormat) as JSON readable by you only, and hides providers a plain key gateway cannot proxy (Copilot, ChatGPT, Vertex, Bedrock, Azure, Workers AI). --model-only keeps the active provider and only changes the model. Exits 0 when saved, 1 when cancelled, 2 on a usage error./cost shows estimates from the provider's published prices), Hotcell subagents, and the Jev completion/triage checks."embeddingProvider": "openai" (or AUTOPILOT_EMBEDDING_PROVIDER) with that provider's key, or memoryEmbeddingModel.Config file shape (keys can live here instead of the environment):
{
"provider": "deepseek",
"providers": {
"deepseek": { "apiKey": "sk-…", "model": "deepseek-v4-pro" },
"ollama": { "baseUrl": "http://gpu-box:11434/v1" }
}
}
Three ways to provide it — the first one found wins:
OPENROUTER_API_KEY (or AUTOPILOT_OPENROUTER_KEY). With this set, the setup screen never appears — the way to run autopilot headless (CI, a VM, a container).autopilot auth openrouter — approve in the browser and a key is created and saved (OpenRouter's OAuth PKCE flow). Over SSH or in a container it shows a link to open on any device and asks for the code OpenRouter displays (force this with --code). --paste prompts for an existing key instead, and autopilot auth openrouter <key> saves one directly. Inside the TUI, /key set <key> replaces it."openrouterApiKey": "sk-or-…" in ~/.config/autopilot/config.json (created with mode 600)./key shows which key is in use, what it has spent and how much credit is left. Model calls — including memory embeddings and small internal side-calls (summaries, memory extraction) — are all billed to this key.
A key with no credits can still use OpenRouter's free models (the "Free" section of /model, ids ending in :free), subject to OpenRouter's daily request cap.
Earlier kimiflare versions ran on Cloudflare Workers AI / AI Gateway. On the first launch after upgrading you'll be asked for an OpenRouter key once; after that:
@cf/moonshotai/kimi-k2.6 → moonshotai/kimi-k2.6, and so on), and the retired Cloudflare fields (OAuth login, gateway, Unified Billing, provider keys) are removed from config.json.bge-base-en-v1.5 model Workers AI served./gateway, autopilot auth cloudflare, --cloud and the Cloud-only commands are gone. /multi-agent (Commute) is retired; subagents now run locally through Hotcell.For substantial tasks, the agent can hand independent investigations to subagents: separate Autopilot instances that each explore the repository in their own local Hotcell sandbox and report back cited findings. Several subagents run in parallel behind a single approval while the main agent keeps the conversation and does the edits. You don't need to ask for them; say "don't use subagents" to opt out, or "use subagents" to insist.
Setup: install and start Hotcell, then add your OpenRouter key to its host key store (hotcell keys add openrouter). The real key never enters a sandbox. Subagents are read-only (no edits, shell, PRs, or MCP) and see your current working tree, including uncommitted changes — it is streamed into the sandbox, so private repositories work and nothing needs to be pushed (secrets like .env are never copied). The first subagent after an install builds a cached runtime (about two minutes, once per version); later ones start in seconds. See docs/subagents.md for limits and troubleshooting.
While subagents run they are listed in the activity panel (/agents). /subagents cancel <n> stops one without interrupting the turn, and /subagents stats shows how often subagents were offered, used, and succeeded over the last week (kept locally in ~/.local/share/autopilot/subagents.jsonl; no prompts or code are recorded).
You pick the model on first run, and can switch any time with /model. The picker opens on the best & latest models — recent, tool-capable models ranked by the agentic and coding benchmark scores OpenRouter publishes (Artificial Analysis), at most two per lab — so it stays current as new models ship, with no list maintained in code. Start typing to fuzzy-search all of them (sonet finds Claude Sonnet, gpt 6 the GPT-6 family). Or set one directly:
/model moonshotai/kimi-k3 # 1M context
/model anthropic/claude-sonnet-5
/model deepseek/deepseek-v4-flash
autopilot -m moonshotai/kimi-k2.7-code -p "..."
Any OpenRouter model id works, including variants like :free and :nitro. Models OpenRouter lists without tool calling are hidden from the picker — a coding agent needs tools.
Provider routing (optional). OpenRouter serves most models through several upstream providers. autopilot always requires providers that support every parameter it sends, and otherwise leaves routing to OpenRouter (price-weighted, uptime-aware, tool-calling quality first, sticky per session for caching). To steer it, add an openrouterProvider object to the config file — any of OpenRouter's provider preferences:
{ "openrouterProvider": { "ignore": ["SomeProvider"], "data_collection": "deny" } }
Avoid order / sort unless you need them: they turn off sticky routing, which is what keeps the prompt cache warm.
Point every model call at your own OpenAI-compatible endpoint instead of OpenRouter — useful when a host application (CI, an agents platform, a container) fronts model access with its own broker and doesn't want to hand autopilot a raw provider key:
export AUTOPILOT_BASE_URL="https://your-broker.example.com/v1" # /chat/completions is appended
export AUTOPILOT_API_KEY="<bearer for that endpoint>" # optional; header omitted if unset
autopilot -p "..." # or --mode rpc — no OpenRouter key needed
The same pair can be persisted in ~/.config/autopilot/config.json as baseUrl / apiKey
(env vars win over the file, field by field). When a base URL is configured:
<baseUrl>/chat/completions and memory embeddings to <baseUrl>/embeddings,
with Authorization: Bearer $AUTOPILOT_API_KEY (no Authorization header at all when the key is
unset — e.g. a local llama.cpp/Ollama server).Requesty is another OpenAI-compatible gateway you can use with your own key instead of OpenRouter. It is opt-in: it is only used when no OpenRouter key and no custom endpoint are configured, so an existing setup never changes.
export REQUESTY_API_KEY="<your key>" # from https://app.requesty.ai/api-keys
autopilot # or: autopilot auth requesty (checks and saves the key)
autopilot -p "..." -m openai/gpt-4o-mini
~/.config/autopilot/config.json as requestyApiKey (the env var wins).REQUESTY_BASE_URL picks a region, e.g. https://router.eu.requesty.ai/v1 for the EU. Only https URLs on Requesty's hosts (router.requesty.ai, router.eu.requesty.ai, router.us.requesty.ai, router.ap.requesty.ai) are accepted, so the key is never sent anywhere else./model lists Requesty's managed policies first (short ids such as kimi-k2.6 or
claude-sonnet-4-5), then its full vendor/model catalog. Both kinds of id work.kimi-k2.6 for the main model, deepseek-v4-flash for side calls, and
openai/text-embedding-3-small for memory embeddings./key shows whether the key is valid; OpenRouter
only features (browser sign in, /key set, the billed cost lookup) are not available.autopilot -p "summarize PLAN.md" # stream answer to stdout
autopilot -p "..." --dangerously-allow-all # auto-approve mutating tools (for scripts)
autopilot -p "..." --reasoning # include chain-of-thought in stderr
Send a task to an Autopilot server you run. It works in its own sandbox and opens a pull request, and you can close your laptop while it does.
autopilot remote add https://acme.example.com # approve the code in your browser; no keys copied
autopilot --on acme "fix the flaky checkout test" # runs on the server; uncommitted changes go along
autopilot runs # what's running or done, with PR links
autopilot runs watch <run> # follow a run (Ctrl-C detaches, the run keeps going)
autopilot pull <run> # check out the run's branch here
The run starts from the last commit your branch shares with origin. Unpushed commits, staged and unstaged edits, and untracked files that aren't ignored are sent along as a patch, up to 3 MiB. Add --detach to return right away and -m <model> to pick one of the server's models. Each task gets its own sandbox and branch, so several can run at once. The server keeps the model key and GitHub credentials; your laptop holds only a revocable, 90-day pairing credential in ~/.config/autopilot/remotes.json. Setting up the server side is covered in docs/dl-server-api.md.
Use autopilot programmatically from your own application — no TUI required.
import { createAgentSession } from "autopilot-ai/sdk";
const { session } = await createAgentSession({
cwd: "/path/to/project",
config: {
openrouterApiKey: process.env.OPENROUTER_API_KEY,
model: "moonshotai/kimi-k2.7-code",
},
});
// Stream every event: text deltas, tool calls, tasks, usage
session.subscribe((event) => {
console.log(event.type, event);
});
// Send a prompt
await session.prompt("Refactor auth to JWT + Redis");
// Mid-flight correction while the agent is still running
await session.steer("Use Redis instead of in-memory store");
// After the turn finishes
await session.followUp("Also add unit tests");
// Clean up
session.dispose();
Key features:
subscribe() — receive typed events (SessionEvent in src/sdk/types.ts): session.start / session.end, message.start / message.delta / message.reasoning / message.end, tool.start / tool.result, usage, permission.request / permission.resolved, tasks.update, warning, hook.failed, context.compacted, status and worker.finished. Errors arrive as session.end with reason: "error".prompt() / steer() / followUp() — full conversation lifecycleabort() — stop the running turnsetModel() / setMode() / setReasoningEffort() — change the model, permission mode (sessions start in edit) or reasoning effort (low|medium|high)resolvePermission() — answer a permission.request eventgetStatus() / getUsage() / save() — inspect and persist session statepermissionHandler — decide programmatically whether to allow mutating toolsmemoryEnabled, lspEnabled, costAttribution flagsCall session.dispose() when you're done with a session: it is the reliable way to end it
(SessionEnd hooks, the trace's session_end, background subagents, LSP and memory).
As a safety net, a live session also ends if your process exits without dispose(). On
beforeExit, SIGTERM, SIGINT or SIGHUP the SDK runs SessionEnd hooks and flushes OpenTelemetry
events within a 3 s budget. It never calls process.exit and doesn't change whether your
process exits:
An uncaught exception that would crash the process, or an explicit process.exit(), leaves no
time for asynchronous work. The SDK only starts the SessionEnd hook commands, and buffered
OpenTelemetry events may be lost. If your app handles uncaughtException itself, the SDK leaves
its sessions alone; call dispose() from your handler.
Apps that manage their own lifecycle can turn this off:
const { session } = await createAgentSession({ cwd, handleProcessExit: false });
The SDK uses the active provider — provider in config (or AUTOPILOT_PROVIDER),
else OpenRouter — and needs its key: providers.<id>.apiKey in config or the provider's env var
(OPENAI_API_KEY, DEEPSEEK_API_KEY, …). A custom gateway endpoint
(baseUrl / apiKey in config, or AUTOPILOT_BASE_URL / AUTOPILOT_API_KEY) needs no other key.
For OpenRouter, the key is resolved in this priority order:
config object (openrouterApiKey) — recommended for appsOPENROUTER_API_KEY / AUTOPILOT_OPENROUTER_KEY~/.config/autopilot/config.jsonA Requesty key (requestyApiKey in config, or REQUESTY_API_KEY) also works
when no OpenRouter key is set; pass a Requesty model id such as openai/gpt-4o-mini as model.
Pass provider in createAgentSession options to set OpenRouter provider-routing preferences for that session.
For Electron / desktop apps, we recommend storing the key in the OS keychain (e.g. Electron safeStorage or keytar) and passing it explicitly:
import { createAgentSession } from "autopilot-ai/sdk";
const openrouterApiKey = await keytar.getPassword("autopilot", "openrouter");
const { session } = await createAgentSession({
cwd: projectPath,
config: { openrouterApiKey },
});
If you need process isolation or a non-Node consumer, run autopilot in JSONL-over-stdio RPC mode:
node bin/autopilot.mjs --mode rpc
Give the subprocess OPENROUTER_API_KEY — or a custom gateway endpoint
(AUTOPILOT_BASE_URL + AUTOPILOT_API_KEY), ideal for host apps that broker model access themselves.
import { spawn } from "node:child_process";
const proc = spawn("npx", ["autopilot-ai", "--mode", "rpc"], {
cwd: projectPath,
stdio: ["pipe", "pipe", "pipe"],
});
// Read events
proc.stdout.on("data", (chunk) => {
for (const line of chunk.toString().split("\n")) {
if (!line.trim()) continue;
const event = JSON.parse(line);
console.log(event.type, event);
}
});
// Send commands
proc.stdin.write(JSON.stringify({ type: "new_session" }) + "\n");
proc.stdin.write(JSON.stringify({ type: "prompt", message: "Hello" }) + "\n");
// Resolve a permission request
proc.stdin.write(
JSON.stringify({ type: "resolve_permission", requestId: "req_0", decision: "allow" }) + "\n"
);
// Resume a previous session after a process restart (the `new_session`
// response echoes back the sessionId to store for later)
proc.stdin.write(JSON.stringify({ type: "new_session", sessionId: "sdk-session-…" }) + "\n");
autopilot
› fix the layout bug in this screenshot docs/bug.png
› convert this mockup design.png to Tailwind HTML
| Command | Effect |
|---|---|
/settings | List feature settings; /settings modes on|off turns plan/edit/auto modes on or off (off by default), /settings diagnostics on|off toggles post-edit diagnostics, /settings tour on|off the project tour, /settings refocus on|off bringing the terminal back after a browser sign-in (macOS), /settings pr-footer on|off the line "Opened with dl, a free coding agent." at the end of pull requests the agent opens (on by default, every provider; never added to commits; also prFooter: false in config.json or AUTOPILOT_PR_FOOTER=0), /settings invite-tip on|off the one-time invite line after your first opened PR or passing tests on the free tier |
/usage | Directionally Local: your plan (Free or Pro), tokens used and left this week, top-up tokens left, invite bonus, place in line when queued, and when the week resets |
/invite | Free tier: your invite link (copied to the clipboard where the terminal allows it), how many joined, how many were active this week, and the bonus earned; each friend who starts using dl gives you both +10M tokens a week. Also autopilot invite |
/upgrade | Directionally Local: buy Pro or a one-time top-up (opens the payment page, then waits up to 10 minutes for it to show); on Pro, opens the page to manage or cancel your plan. Also autopilot upgrade [pro|topup]. When the free tier runs out mid-session, the same choice (plus inviting a friend and using your own key) appears on its own and the blocked request continues once the purchase shows |
/tour | Summarize this project (name, kind, file count, test command, open GitHub issues when gh is signed in) and suggest three tasks; type 1–3 to start one. Shown automatically on the first session in each git repository (remembered in ~/.local/state/autopilot/project-tour.json, never in the repo); one small model call words the tasks (capped at 200 output tokens, local suggestions after 4 s) |
/mode edit|plan|auto | Switch permission mode (needs modes on) |
/shell auto|bash|cmd|powershell | Show or set the shell for the bash tool |
/resume | Pick a past conversation to restore |
There is no /thinking command. In the interactive UI the reasoning effort for each turn follows the request's intent tier (light → low, medium → medium, heavy → high), and a custom command (/command create) can pin it with effort. reasoningEffort in config (or KIMI_REASONING_EFFORT) sets the session default, and SDK sessions can change it with setReasoningEffort().
/subagents auto is the default: on substantial tasks the agent is asked to look for independent parts and delegate them, and it decides whether delegation is worth it. suggest only hints at delegation for likely parallel work, and off disables harness guidance (you can still ask for subagents explicitly). Explicit requests to use subagents or to work without them always win. Set the policy with /subagents, AUTOPILOT_SUBAGENT_POLICY, or subagentPolicy in config.
The harness never starts a subagent by itself: the agent calls the subagent tool, which always needs normal tool permission (one prompt covers a parallel batch) and obeys provider, read-only, spend, concurrency, and timeout limits. In Code Mode the subagent tool stays a direct tool next to execute_code. Jev (a fast yes/no decision model on OpenRouter) sharpens this: in auto mode it checks whether a substantial request has independent parts (strong delegation guidance at ≥80%, otherwise the softer nudge), and in suggest mode it decides whether to suggest at all. It receives at most 1,200 characters of redacted request text, runs alongside other pre-turn work, and any failure falls back to the default guidance.
When a substantial turn ends without the agent asking you anything, a Jev yes/no check asks whether it actually finished — or clearly explained a blocker — rather than stopping after a plan or "next I'll…". If it clearly didn't, the agent is told to continue (once per turn), so you don't have to type "go on". Typically under a second; OpenRouter only. Turn it off with "completionCheck": false in config or AUTOPILOT_COMPLETION_CHECK=0.
Type ! followed by a command (for example ! gh auth login) to run it in your own terminal, inside the session. Logins, password prompts and interactive programs work because the command gets the real TTY. The output is added to the conversation so the agent can see it. The agent's own bash tool has no terminal, so autopilot suggests ! <command> when something needs you: a login, a sudo password, an SSH passphrase, or a secret you'd rather not paste into chat. On macOS and Linux the output is captured with script(1); elsewhere the command still runs but its output isn't captured.
| Shortcut | Action |
|---|---|
Ctrl+C / Esc | Interrupt current turn when busy; exit when idle |
Ctrl+R | Toggle reasoning display |
Ctrl+O | Toggle verbose tool output |
Shift+Tab | Cycle mode (edit → plan → auto) when modes are on (/settings modes on) |
↑ / ↓ | Walk prompt history |
A message you send mid-turn is triaged instead of always waiting in line:
/now to promote it.Clear phrasing is decided instantly; ambiguous messages get a quick model check (OpenRouter only), and anything uncertain is queued. Use /subagents cancel <n> to stop a single subagent.
herdr is a terminal multiplexer for coding agents. Run autopilot (or autopilot -p "…") in a herdr pane and it reports its own state there, so herdr doesn't have to guess from the screen:
working while a turn runs, blocked while a permission prompt or a plan choice waits on you, idle when Autopilot is ready for your next message;summary token (use $summary in herdr's Agent sidebar rows);autopilot --session <id>, or autopilot --continue before the first message), so the pane comes back in the same session after a herdr server restart;There is nothing to install or configure: Autopilot reports when HERDR_PANE_ID and HERDR_SOCKET_PATH (or HERDR_BIN_PATH) are set, which herdr does for every pane, and does nothing otherwise. Reports go over herdr's socket API (falling back to the herdr CLI), run in the background with a 500 ms timeout, and never hold up the agent; a failure is logged once (herdr.report_failed). An Autopilot started from inside another one in the same pane leaves the reporting to the outer one. Check what herdr sees with herdr agent list. Remote sessions (autopilot --on <server>, the dl server) don't report.
autopilot writes structured JSON logs of agent-side activity (tool calls,
permission decisions, MCP/LSP lifecycle, session events, errors) to
~/.config/autopilot/logs/<date>.jsonl, one file per day, with 7-day
retention pruned automatically at startup.
The logs deliberately exclude prompts and completions. To capture full
request/response payloads locally for debugging, run with
AUTOPILOT_DUMP_LLM=1; each generation is also visible in your
OpenRouter activity log.
autopilot logs path # today's file
autopilot logs dir # log directory
autopilot logs prune # delete files older than 7 days
# Tail this session's activity, formatted:
tail -f $(autopilot logs path) | jq
# Find the slowest tool calls in the last day:
jq -r 'select(.event == "tool:end") | "\(.data.duration_ms)\t\(.data.tool)"' \
$(autopilot logs path) | sort -rn | head
Disable the file sink entirely with AUTOPILOT_LOG_SINK=off. The
separate AUTOPILOT_LOG_LEVEL env var (default off) controls stderr
output — independent of the file sink.
If you set AUTOPILOT_OTEL_ENDPOINT, autopilot also ships each log
entry to that endpoint over OTLP/HTTP
so it lands in Datadog, Honeycomb, Grafana Loki, an internal collector,
or any other backend that speaks OTel. Batched every 5 s (or every
100 entries, whichever first) and best-effort — never blocks the agent
loop.
# Full path:
export AUTOPILOT_OTEL_ENDPOINT="https://otel.example.com/v1/logs"
# Or just the base URL (we auto-append /v1/logs):
export AUTOPILOT_OTEL_ENDPOINT="https://otel.example.com"
# Optional headers (comma-separated key=value pairs) — e.g. for auth:
export AUTOPILOT_OTEL_HEADERS="Authorization=Bearer xyz,X-Tenant=acme"
Each log entry maps to one OTel LogRecord. Correlation IDs
(session_id, turn_id, request_id) become record attributes,
data.* fields are flattened to attributes with type-preserving
encoding, and a service.name=autopilot + service.version pair sits
on the resource.
Autopilot can record its own sessions so you can see how it performs on your work, and compare setups on your own tasks. Everything stays on your machine.
Tracing is off by default. Turn it on for one run with AUTOPILOT_TRACE=1, or always with "trace": { "enabled": true } in ~/.config/autopilot/config.json. Each session then writes a trace.jsonl under ~/.local/share/autopilot/traces/ (AUTOPILOT_TRACE_DIR or trace.root to change it), one event per line:
/good and /bad ratings (with an optional note), and whether your next message corrected the agent or told it to carry onLarge bodies are stored once, by hash, next to the trace. Likely secrets are redacted before anything is written, nothing is uploaded, and a single session stops recording at 200 MB.
autopilot insights aggregates your traces into one row per model: requests per user message, parallel-call share, bash share and bash-as-read share, tool failure rate by error code, guardrail hits, cache hit rate, redundant reads and cost. It is how model profiles are tuned — a profile is only changed when these numbers (and an eval comparison) say it helps.
autopilot insights # every traced session under the trace root
autopilot insights --since 2026-10-01 # recent sessions only
autopilot insights --model deepseek/deepseek-v4.1-flash --json
Autopilot never deletes your saved sessions or traces on its own; they are yours to analyze. Once they pass 1 GB, a new session mentions how much space they take. To see where they live, or to free space:
autopilot history # counts, sizes and paths
autopilot history prune --older-than 30 # delete sessions and traces untouched for 30 days
autopilot eval runs Autopilot headlessly on a set of tasks and compares two configurations: a model, a setting, or a harness change.
autopilot eval run --tasks evals/tasks/autopilot-bench --config evals/configs/baseline.json --model <id> --out /tmp/eval-a
autopilot eval run --tasks evals/tasks/autopilot-bench --config evals/configs/my-change.json --model <id> --out /tmp/eval-b
autopilot eval compare /tmp/eval-a/results.jsonl /tmp/eval-b/results.jsonl
Each task is a JSON file naming a repo, a base commit, a prompt and a test command. Every run gets its own git worktree with tracing forced on, and the task passes if the test exits 0. eval compare prints success rates with a paired significance test, cost, turns and the tasks that flipped. This repository ships autopilot-bench, twenty tasks mined from its own merged pull requests; see evals/README.md for the task format and how to build a set from your own repo.
autopilot can fire shell commands at these points, configured
per-project (.autopilot/settings.json) or globally
(~/.config/autopilot/settings.json); the older kimiflare paths are
still read (see Settings location):
| Event | Fires when | Veto? |
|---|---|---|
SessionStart | A session's first prompt, before UserPromptSubmit (source: startup/resume) | No |
PreToolUse | A tool call is about to run | Yes |
PostToolUse | A tool call just finished (success or failure) | No |
PostToolUseFailure | A tool call failed (also fires PostToolUse) | No |
PermissionRequest | You're about to be asked to approve a call | Allow / deny |
UserPromptSubmit | A prompt is submitted, in every mode (TUI, -p, --emit-events, serve, SDK) | Yes |
Stop | A turn ended cleanly | No |
PreCompact | Auto-compaction is about to run | No |
SessionEnd | autopilot exits, or /clear (reason) | No |
Hooks receive the event payload as JSON on stdin and as
AUTOPILOT_HOOK_* env vars (for shell-one-liner ergonomics; every one
is also set under its old KIMIFLARE_HOOK_* name, so existing scripts
keep working). The full
JSON copy in AUTOPILOT_HOOK_PAYLOAD is only set for payloads up to
32 KiB (OSes cap environment variable size); above that it is omitted,
AUTOPILOT_HOOK_PAYLOAD_OMITTED=1 is set, and stdin is the only source —
prefer reading stdin. Payloads
also carry Claude Code's field names (hook_event_name, tool_name,
tool_input, tool_response, tool_call_id, transcript_path), so
Claude Code hook scripts — and tools built for them, like
Agent Beacon — work unchanged. A successful
write/edit's tool_response carries Claude Code's structuredPatch
and originalFile (and type: "create" | "update" for Write); see
docs/beacon.md for the
size limits.
A blocked UserPromptSubmit never reaches the model: the TUI shows the
reason, -p and one-shot --emit-events exit with code 44,
serve answers 403 prompt_blocked, and the SDK's prompt() rejects
with PromptBlockedError (details in
docs/beacon.md).
Veto events (PreToolUse, UserPromptSubmit) follow Claude Code's rules:
| Hook result | Effect |
|---|---|
exit 0 | Allowed. Stdout may carry a JSON decision (below). |
exit 2 | Blocked. Stderr (or stdout if stderr is empty) is the reason shown to the model. |
| any other non-zero exit, timeout, command not found (127), spawn error | The hook failed: you get a warning and the action is allowed (fail-open), so a broken or missing hook never wedges every tool call — unless the hook sets "failClosed": true (see Hook failures). |
A PreToolUse hook that exits 0 can instead print a JSON decision:
{ "hookSpecificOutput": { "hookEventName": "PreToolUse",
"permissionDecision": "deny", "permissionDecisionReason": "no writes to prod config" } }
deny blocks, with permissionDecisionReason as the reason.ask sends the call through the normal permission prompt, even for
tools that don't usually ask. Hosts with no one to ask answer as they
already do (--print denies unless --dangerously-allow-all).allow means the hook doesn't object; it does not bypass your own
permission rules or mode.With several hooks, deny wins over ask, which wins over allow.
The legacy {"decision": "block", "reason": "..."} form also blocks
(use it for UserPromptSubmit). Stdout that isn't JSON is ignored, as
before. Hooks written for older versions that blocked with exit 1
must switch to exit 2.
failClosedWhen a hook times out, can't be started (missing binary, exit 127) or
exits non-zero (other than 2 on a veto event), Autopilot tells you
instead of only logging it:
--print writes
it to stderr (hook_failed event in --output-format stream-json, a
hookFailures array in json); --emit sends a RuntimeError
warning; the SDK emits a hook.failed session event; autopilot serve sends a hook.failed SSE event and lists them under
hookFailures in GET /session/:id./hooks list shows [failed N× this session]) but not shown again.autopilot.hook.failed
at WARN, with hook.id, hook.event, hook.failure
(timeout / spawn_failed / command_not_found / nonzero_exit),
hook.reason, hook.allowed, hook.fail_closed and
hook.failure_count, so a collector such as Agent Beacon records that
a policy hook did not run.For a policy hook that must never be skipped, set "failClosed": true
on it. On the veto events (PreToolUse, UserPromptSubmit,
PermissionRequest) a failure then blocks the action, with a reason
naming the hook and the failure. It has no effect on informational
events.
{ "hooks": { "PreToolUse": [
{ "id": "policy", "command": "my-policy-check", "timeoutMs": 10000, "failClosed": true }
] } }
PermissionRequest and PostToolUseFailurePermissionRequest fires when Autopilot is about to ask you to approve a
tool call — after plan mode, --dangerously-allow-all and config
permission rules have had their say, so it never fires for calls those
already settled. The payload has tool, args, tool_call_id,
mode (edit, plan, …; also $AUTOPILOT_HOOK_MODE), plus Claude Code's tool_name,
tool_input, tool_use_id and permission_mode; matcher matches the
tool name. The hook decides like a PreToolUse hook:
2, or {"hookSpecificOutput": {"permissionDecision": "deny", "permissionDecisionReason": "..."}}:
denied without asking you; the reason goes to the model.permissionDecision: "allow": approved without asking you. Unlike
PreToolUse, where allow only means "no objection", answering the
approval prompt is what this event is for. It never overrides plan mode
(an allow there still shows the prompt) or a config deny rule (those
calls never reach the hook)."ask", no JSON, or a hook that fails open: the normal prompt.
Hosts with no one to ask answer as before (--print denies).Claude Code's form, {"hookSpecificOutput": {"decision": {"behavior": "allow"|"deny", "message": "..."}}},
works too.
PostToolUseFailure fires when a tool call fails — an error, a denied
permission, or a bash command that exits non-zero or is killed — with
tool, args, tool_call_id, error (the failure as text) and
result, plus Claude Code's tool_name, tool_input, tool_use_id,
error and is_interrupt. PostToolUse keeps firing for failures
too, as before, so existing hooks are unaffected; register a hook on
only one of the two if you don't want to see a failure twice.
Both fire from every host: the TUI, --print, --emit, the SDK and
autopilot serve.
Hooks are read from four files and merged, in this order:
~/.config/kimiflare/settings.json (legacy global; after the one-time move it is a link to the file below and is read once)~/.config/autopilot/settings.json (global).kimiflare/settings.json (legacy project).autopilot/settings.json (project)~/.config is $XDG_CONFIG_HOME when that is set. For each event,
hooks run in that order; an entry with the same id as an earlier one
replaces it (ids are derived from event + command when not set, so a
hook copied verbatim from a legacy file runs once). The autopilot paths
are canonical: /hooks enable, the hook wizard and the dashboard write
new hooks there, while enabling or disabling an existing hook edits the
file it lives in. Project legacy files are never moved or dropped (the
global one moves with the rest of ~/.config/kimiflare); if your hooks
are only in a legacy file, autopilot shows a one-time notice naming the
new path. /hooks path prints all four.
A repository can ship hooks (.autopilot/settings.json,
.kimiflare/settings.json) and language servers (.autopilot/lsp.json,
or the older .kimiflare/lsp.json),
and both run commands on your machine. Autopilot does not load a
project's copies until you have trusted them, so cloning a repository and
starting Autopilot in it runs nothing the repository chose. When a project
has untrusted settings, Autopilot says so at startup.
/trust (or autopilot trust in the project directory) lists every
command the files would run. /trust yes (or answering y) trusts
exactly that content.~/.config/autopilot/trusted-project-files.json. Any later change,
such as a git pull or a branch switch, needs review again./hooks, the wizard or the dashboard, and LSP
settings saved by /lsp config, stay trusted: they are your own edits.AUTOPILOT_TRUST_PROJECT=1 to trust project files for that process.Global settings (~/.config/...) are always loaded.
matcher is a regex tested against the tool name for PreToolUse,
PostToolUse, PostToolUseFailure and PermissionRequest. It is tried against both autopilot's native name and the
Claude Code name (bash/Bash, edit/Edit, write/Write,
read/Read, glob/Glob, grep/Grep, web_fetch/WebFetch,
search_web/WebSearch; an edit with edits[] also answers to
MultiEdit), so a matcher copied from a Claude Code config works.
| Matcher | Matches |
|---|---|
bash, Bash, ^bash$ | bash calls |
Bash|Edit, ^(edit|write)$ | either tool |
^mcp_ | every MCP tool |
*, "", or no matcher | every tool |
Matching is case-sensitive (BASH matches nothing) and unanchored
(dit matches edit; use ^…$ for an exact name). An invalid regex
matches nothing.
/hooks # list configured hooks
/hooks recommended # list starter hooks shipped with autopilot
/hooks enable stop-bell # enable one (writes to .autopilot/settings.json)
/hooks enable stop-bell global # ...or the global file
/hooks disable stop-bell
/hooks path # print settings.json paths (incl. legacy ones)
/hooks reload # re-read settings.json after a manual edit
The recommended catalog includes terminal bells / macOS notifications
on Stop, secret-file guards on PreToolUse (e.g. block edits to
*.env), auto-format-with-prettier on PostToolUse, and a tool-call
audit log. All ship disabled — /hooks recommended lists them.
{
"hooks": {
"PreToolUse": [
{
"id": "no-secrets",
"matcher": "^(edit|write)$",
"command": "case \"$AUTOPILOT_HOOK_PATH\" in *.env|*.pem) echo 'blocked' >&2; exit 2;; esac"
}
],
"PostToolUse": [
{
"id": "format-ts",
"matcher": "^(edit|write)$",
"command": "npx --no-install prettier --write \"$AUTOPILOT_HOOK_PATH\" >/dev/null 2>&1 || true"
}
],
"Stop": [
{ "id": "bell", "command": "printf '\\a'" }
]
}
}
Per-hook fields:
command (required) — the shell command.matcher (optional) — regex matched against the native or Claude Code
tool name for PreToolUse / PostToolUse (see Matchers).
Ignored for other events.id (optional) — stable handle for /hooks enable|disable.
Auto-derived from event + command when omitted.enabled (default true) — set false to keep a hook in config
but skip it.timeoutMs (default 30000) — hard kill if the hook hangs (the
action then proceeds, with a warning).description (optional) — shown by /hooks list.failClosed (default false) — on veto events, block when the hook
itself fails instead of failing open.Hooks are always-on infrastructure: they fire whether the TUI is open
or autopilot is running in --print mode. They also fire for tool
calls generated from inside the Code Mode sandbox (heavy-tier turns),
because hook firing lives on the ToolExecutor itself — every call
path uses the same plumbing.
When intent classification has assigned a tier, hook payloads include
it as tier: "light" | "medium" | "heavy" (on UserPromptSubmit,
PreToolUse, PostToolUse) and as $AUTOPILOT_HOOK_TIER. Useful for
"skip auto-format on light turns" or "audit every heavy-turn write."
SDK consumers opt in to hooks with enableHooks: true on
createAgentSession. Default is off because the SDK is a primitive,
not the TUI. SDK sessions fire SessionEnd on dispose(), or when the
process exits without it (see Ending a session).
Add servers to ~/.config/autopilot/config.json (or $XDG_CONFIG_HOME/autopilot/config.json) under mcpServers. There is no interactive add wizard: /mcp list shows what is connected and /mcp reload reconnects after an edit.
{
"mcpServers": {
"filesystem": {
"type": "local",
"command": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
},
"linear": { "type": "remote", "url": "https://mcp.linear.app/sse" },
"meta-ads": {
"type": "http",
"url": "https://mcp.facebook.com/ads",
"headers": { "Authorization": "Bearer ${META_ADS_ACCESS_TOKEN}" }
}
}
}
local runs a command over stdio.remote uses the legacy SSE transport.http uses the MCP Streamable HTTP transport (POST plus an optional SSE stream), which most hosted servers use. ${VAR} in url, env and headers is expanded from the environment, so a token can stay out of the file.Servers configured in Claude Code, Cursor and OpenCode are also picked up automatically; they stay off until a task needs one. Hosted servers that require OAuth (rather than a bearer token) are not supported yet — use a user or system-user access token in headers instead.
git clone https://github.com/directionally-local/autopilot
cd autopilot
npm install
npm run build
npm link
Scripts:
npm run build — bundle with tsupnpm run dev — run via tsxnpm run typecheck — tsc --noEmitnpm test — run testsgit checkout -b feat/your-featurenpm run typecheck and npm run buildBuilt by Sina Meraji and contributors · MIT License
cmd.exe / PowerShell on Windows, bash on Unix. The bash tool works out of the box on all platforms.↑/↓, j/k, Alt+1/2/3).history.jsonl tracks daily usage forever, so /cost shows true all-time and monthly totals that survive across sessions and version updates.remember, recall, and forget tools.AWS_PROFILEeval "$(aws configure export-credentials --format env)"AWS_REGIONConverse API with SigV4 signing; Claude thinking and prompt caching; inference-profile ids like global.anthropic.claude-sonnet-5-5 |
| DeepSeek | deepseek | DEEPSEEK_API_KEY |
| MiniMax | minimax | MINIMAX_API_KEY | OpenAI-compatible endpoint with split reasoning (<think> tags are separated out if they appear) |
| Moonshot AI (Kimi) | moonshot | MOONSHOT_API_KEY |
| Kimi Code (subscription) | kimi-code | KIMI_API_KEY |
| Z.ai (GLM) | zai | ZHIPU_API_KEY |
| Z.ai GLM Coding Plan | zai-coding | ZHIPU_API_KEY |
| xAI | xai | XAI_API_KEY | Grok over the Responses API |
| Mistral | mistral | MISTRAL_API_KEY | tool-call ids adapted to Mistral's format; mistral-embed |
| Groq · Cerebras · Together AI · Fireworks AI · DeepInfra | groq · cerebras · together · fireworks · deepinfra | GROQ_API_KEY · CEREBRAS_API_KEY · TOGETHER_API_KEY · FIREWORKS_API_KEY · DEEPINFRA_API_KEY | fast / open-model hosts |
| Vercel AI Gateway | vercel | AI_GATEWAY_API_KEY | many vendors with one key |
| Cloudflare Workers AI | cloudflare | CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID (or autopilot auth cloudflare --account-id …) | Cloudflare-era kimiflare configs reuse their credentials, and memory stays on bge-base-en-v1.5. For AI Gateway, use its OpenAI-compatible URL as a custom endpoint |
| Azure OpenAI | azure | AZURE_API_KEY + AZURE_RESOURCE_NAME (or autopilot auth azure --resource <name>) | v1 API over Responses; model ids are your deployment names, discovered from the resource. Entra ID sign-in isn't supported yet — use a key |
| Ollama (local) | ollama | none | installed models are discovered; providers.ollama.baseUrl for another host |
| LM Studio (local) | lmstudio | none |
| vLLM (local) | vllm | none | your own GPUs; vllm serve with --enable-auto-tool-choice --tool-call-parser <name> for tool calls |
| Requesty | requesty | REQUESTY_API_KEY |
| Any OpenAI-compatible endpoint | custom | AUTOPILOT_API_KEY (optional) | AUTOPILOT_BASE_URL — LiteLLM, vLLM, llama.cpp, a company gateway |
/checkpoint [label] / /checkpoint list | Save a checkpoint in the current session, or list this session's checkpoints |
/undo | Restore the latest recovery point Autopilot saved before a destructive file or git action in this project |
/audit | Read-only report of what the agent could destroy from here: allowed destructive actions, reachable databases and cloud accounts (names and hosts only), recovery that exists, and suggested rules |
/compact | Summarize older turns to free context |
/init | Scan repo and write AGENTS.md project context (refreshes an existing AGENTS.md, or a legacy KIMI.md, KIMIFLARE.md or AGENT.md in place) |
/setup-import | Show what Autopilot uses from your other coding agents (Claude Code, Cursor, Gemini CLI, GitHub Copilot, OpenCode) |
/memory | Show memory stats and search |
/mcp list / /mcp reload | Manage MCP servers |
/reasoning | Toggle chain-of-thought display |
/model | Pick a model from the active provider's catalog (or /model <id>, /model list [filter]) |
/provider | List providers, or switch with /provider <id> [key] (see Providers) |
/jev | Ask Jev for a typed, scoped one-shot decision |
/subagents | List running subagents; /subagents cancel <n>|all stops them; /subagents off|suggest|auto sets the delegation policy (default: auto) |
/now | Run your latest queued message now instead of after the current turn |
/key | Show the active provider's key — on OpenRouter with spend and credit (/key set <key>, /key clear) |
/cost | Show cost by session, day, month and all time (OpenRouter-confirmed, or estimated from the provider's prices) |
/good / /bad [note] | Rate the last turn; recorded in the session trace when tracing is on |
/update | Check for updates |
/help | List all commands |