tack is a terminal-native autonomous coding harness for OpenRouter.
It runs in your terminal, keeps a live transcript, and can plan, edit, run, and verify changes inside the current workspace.
What it gives you:
- an autonomous agent loop that can inspect the repo, make edits, and execute commands
- streaming chat responses when you want to steer manually
- model, provider, and reasoning selection
- transcript search and history recall
- markdown-rendered output with syntax highlighting and rich formatting
- approval gates for risky commands and external paths
It is a single Rust binary crate built on ratatui, crossterm, and OpenRouter's chat and model endpoints.
The core operating model is:
- Take a goal in agent mode.
- Generate a concise plan from workspace context.
- Execute one tool action at a time.
- Record each step, reflection, and changed file set.
- Verify with the project-aware test command when possible.
- Repair, retry, or finish when the goal is complete.
That loop keeps the app closer to a terminal-native coding agent than a traditional chat client.
The app works like a live control panel for autonomous work:
- the top bar shows the active model, provider route, reasoning level, and chat or agent mode
- the center pane shows the conversation and the agent's progress
- the composer at the bottom is where you type goals, prompts, and slash commands
- the footer shows status, context usage, latency, throughput, pricing, and the last response usage
- when an agent run is active and the terminal is wide enough, a side panel appears with the agent's phase, step budget, workspace, changed files, verification state, and latest reflection
- Install a recent stable Rust toolchain.
- Set your OpenRouter API key.
- Run the app.
export OPENROUTER_API_KEY=your_key_here
cargo runIf you prefer to keep the key in your shell startup file, the app also reads OPENROUTER_API_KEY from ~/.zshrc.
The app reads a small set of environment variables on startup:
OPENROUTER_API_KEY- required unless it can be found in~/.zshrcOPENROUTER_MODEL- optional starting model, defaults toopenai/gpt-4o-miniAGENT_MAX_STEPS- optional agent step cap, defaults to24AGENT_MAX_RUNTIME_SECS- optional agent runtime cap in seconds, defaults to1200
The app also persists preferences in a hidden JSON file in your home directory:
modelprovider_slugreasoning_effort
That file is how the app remembers the last model, provider route, and reasoning choice between launches.
Chat mode is the manual steering lane.
- type a prompt and press
Enterto send it Shift+Enterinserts a newline without submitting- OpenRouter responses stream token-by-token into the transcript
- if the response exposes reasoning text, it is rendered as a separate reasoning message before the assistant answer
- the footer updates with first-token latency, average latency, tokens per second, and usage cost when the response completes
Agent mode is the autonomous lane. It treats the current directory as a workspace and drives the plan/act/verify loop for you.
- press
Shift+Tabto toggle between chat and agent mode - or run
/agentwith no arguments to toggle - run
/agent <goal>to start an agent run immediately
The agent flow is:
- create an
AgentRunfor the current directory - ask OpenRouter for a concise plan
- execute one structured action at a time
- record each step, reflection, and changed file set
- verify the result with a project-aware test command when possible
- stop when the goal is complete, the budget is exhausted, or verification fails too many times
The default verification command is chosen from the workspace:
cargo testwhenCargo.tomlexistsbun testwhenbun.lockbexistsnpm testwhenpackage.jsonexistsgo test ./...whengo.modexistspython -m pytestwhenpyproject.tomlorpytest.iniexistsgit diff --checkwhen the repo only looks like a git checkout
The agent can ask the model to emit structured JSON actions. Supported actions are:
searchglob_pathslist_direxists_pathstat_pathread_filecreate_dirwrite_fileappend_fileprepend_filepatch_editdiff_filecopy_pathmove_pathdelete_pathread_linkcreate_symlinkcreate_archiveextract_archiverun_commandfinish
Read-only and write-capable actions are separated in the implementation, and the app refuses obviously dangerous mutations such as writing directly to the workspace root.
The agent has a few safety rails built in:
- commands are run through
zsh -lc - shell commands have a timeout
- action results are summarized so huge command output does not flood the transcript
- commands that look like they mutate the workspace are treated as higher risk
- external paths outside the workspace can trigger approval
- approvals can be remembered for the current session
If the agent asks for approval, the keybinds are:
Enter,1,y, orYapprove2,a, orAapprove and remember the current command or external root for the sessionEsc,3,n, orNdenysorSstop the agent run
Use /help inside the app to print the full list. The main commands are:
/help- show the command list/agent [goal]- toggle agent mode or start an agent run/agent-status- print a detailed status summary for the active agent run/agent-stop- cancel the active agent run/find <text>- search the transcript/find-next- move to the next transcript match/find-prev- move to the previous transcript match/model [query]- open the model picker, optionally filtered/use-model <id-or-query>- set a model directly, then choose a provider/provider <slug|auto>- choose a provider route/reasoning [auto|level]- open or set the reasoning level/refresh- reload OpenRouter model metadata/clear- clear chat history/quitor/q- exit the app
Enter- submit the current inputShift+Enter- insert a newlineShift+Tab- toggle chat and agent modeCtrl+S- submit the current inputCtrl+F- prefill/findAlt+UpandAlt+Down- recall previous inputsCtrl+UpandCtrl+Down- scroll the transcriptCtrl+PageUpandCtrl+PageDown- page through the transcriptCtrl+HomeandCtrl+End- jump to the top or bottom of the chatTab- autocomplete slash commandsEsc- contextual cancel, clear, or stop action, usually with a second press to confirmCtrl+C- quit
While in multiline composer mode, the editor also supports common line navigation and deletion shortcuts such as Ctrl+A, Ctrl+E, Ctrl+U, Ctrl+K, Ctrl+W, Alt+Backspace, and Alt+Delete.
The app fetches OpenRouter model metadata on launch.
/modelopens a filtered model search- choosing a model loads its provider routes
/provider autorestores OpenRouter's automatic route selection/provider <slug>pins a specific provider route when one is available/reasoningshows the reasoning levels advertised by the current model- the current model's context window, pricing, supported parameters, architecture details, and reasoning metadata are shown in the model picker
- the selected model, provider route, and reasoning effort are persisted across launches
If the model changes and OpenRouter returns a resolved model ID that is in the loaded catalog, the app updates the active model preference to match it.
The transcript renderer is intentionally rich. It supports:
- fenced code blocks
- syntax highlighting for several common languages
- diff blocks with added and removed line coloring
- task lists
- tables
- definition lists
- footnotes
- inline code styling that treats path-like spans differently from shell commands
That makes model responses, code reviews, and agent output easier to scan inside the terminal.
The screen is split into four main regions:
- top bar
- transcript area
- input composer
- status footer
The top bar shows the active model, provider route, reasoning level, and whether the app is busy.
The footer shows:
- current status
- chat or agent mode
- context usage
- current pricing for the selected model
- last response token usage and cost
- speed and reliability metrics
When an agent run is active and the terminal is at least 96 columns wide, the transcript area splits into chat plus an agent detail panel.
src/main.rs binary entrypoint
src/lib.rs crate wiring and shared constants
src/runtime.rs terminal setup, event loop, and input handling
src/app.rs application state, commands, search, metrics, and model selection
src/services.rs OpenRouter streaming, model/provider fetch, and shared helpers
src/agent.rs agent orchestration and run lifecycle
src/agent_services.rs filesystem actions, command execution, verification, and approval logic
src/ui.rs ratatui layout and widgets
src/markdown.rs markdown rendering for chat messages
src/styles.rs theme, colors, icons, and text styling
src/models.rs shared data models and agent state types
src/config.rs environment loading and persisted preferences
src/tests.rs behavior-focused tests
Common local checks:
cargo fmt
cargo test
cargo clippyThe tests cover:
- markdown rendering behavior
- agent JSON repair and plan parsing
- command summarization
- verification command selection
- transcript search and history recall
- agent budgeting and verification flow
- model/provider/reasoning selection
- If you see
OPENROUTER_API_KEY was not found in the environment or ~/.zshrc, export the key or add it to~/.zshrc. - If the model list will not load, check network access and whether the key is valid for OpenRouter.
- If the wrong model keeps coming back, delete the persisted preferences file in your home directory to clear saved settings.
- If agent commands keep asking for approval, use
approve and rememberonce for the current command or external root. - If the agent behaves like it is stalled, check the footer and the agent side panel for the current phase, step budget, verification state, and last reflection.
- The agent runs from the current directory, so it is best used inside a git checkout you are comfortable modifying.
- The app uses Unix-specific shell and filesystem features for agent commands and symlinks, so it is best suited to Unix-like systems.
- The OpenRouter model catalog and provider metadata are fetched live, so available routes can change over time.