Skip to content
jonas-johanssonPublic

About

Coding agent for the terminal.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Pace

A really nice terminal-based coding agent.

Screenshot of Pace in action

Screenshot of Pace in action

Quick start

npm install
npm run build
node apps/pace/dist/app.js

In your .bashrc set:

alias pace='node ~/dev/pace/apps/pace/dist/app.js'

Set at least one API key:

export ANTHROPIC_API_KEY=sk-ant-...    # anthropic/* models
export OPENAI_API_KEY=sk-...           # openai/* models
export OPENCODE_ZEN_API_KEY=...        # opencode/* models via OpenCode Zen
export FIREWORKS_API_KEY=...           # fireworks/* models
export FRIENDLI_API_KEY=...            # friendli/* models
export COMPACTIFAI_API_KEY=...         # compactifai/* models (Multiverse Computing, e.g. quasar-438b)
export OPENROUTER_API_KEY=sk-or-...    # openrouter/* models (e.g. openrouter/xiaomi/mimo-v2.6-pro)
export XIAOMI_API_KEY=sk-...           # xiaomi/* models (e.g. xiaomi/mimo-v2.6-pro); XIAOMI_BASE_URL overrides the endpoint (Token Plan)
export EXA_API_KEY=...                 # optional; higher web search limits. Backend-only, sent as an x-api-key header

Without EXA_API_KEY, web search uses Exa's rate-limited anonymous MCP endpoint and retries with backoff before failing the tool call.

Features

  • Interactive TUI
  • Sessions
  • Undo
  • Bash, web search, web fetch, read, write, edit
  • Compaction
  • Paste image for vision models
  • MCP
  • Skills
  • Subagents
  • AGENTS.md
  • Mouse support (scroll, select to copy)
  • Slash commands
  • Tables
  • Token usage and cost in your currency
  • Fast boot
  • Table rendering

Models

Switch models at any time with Tab or /model <model-id>. Model IDs use the full provider/model string. Models default to an unset variant, so Pace sends no explicit reasoning effort, thinking level, or similar provider-native options unless you select a variant. For models with variants, use Ctrl+T or /variant <variant> to cycle provider-native options such as OpenAI reasoning effort; the cycle includes the unset variant.

Pace ships with a curated built-in model catalog and also loads new models from models.dev into the model picker. Built-in metadata and variants win for known models; fetched metadata adds models that Pace does not already know about.

Built-in Model ID
anthropic/claude-haiku-4-5
anthropic/claude-sonnet-4-6
anthropic/claude-opus-4-6
anthropic/claude-opus-4-7
anthropic/claude-opus-4-8
opencode/claude-haiku-4-5
opencode/claude-sonnet-4-6
opencode/claude-opus-4-6
opencode/claude-opus-4-7
opencode/claude-opus-4-8
opencode/claude-fable-5
opencode/kimi-k2.6
opencode/kimi-k2.7-code
opencode/kimi-k3
opencode/deepseek-v4-pro
opencode/deepseek-v4-flash
opencode/deepseek-v4-flash-free
opencode/deepseek-v4.1-flash
opencode/glm-5.2
opencode/glm-5.3
opencode/glm-5.3-flash
fireworks/glm-5.3
fireworks/glm-5.3-flash
friendli/glm-5.3-flash
opencode/gpt-5.5
openai/gpt-5.5
lmstudio/google/gemma-4-12b

Model catalog environment variables:

Variable Description
PACE_MODELS_URL Override the models.dev source URL. Defaults to https://models.dev.
PACE_MODELS_PATH Read/write a specific catalog JSON file instead of the default cache path.
PACE_DISABLE_MODELS_FETCH Set to 1, true, or yes to disable network refreshes.

Keyboard shortcuts

Key Action
Tab / Shift+Tab Cycle models forward / backward
Ctrl+T Cycle the current model's variant, including unset
Ctrl+G Toggle focused and detailed transcript views
Escape Cancel the running prompt
Ctrl+V Paste image from clipboard
Ctrl+Z Insert /undo into an empty composer
Ctrl+C Clear input, or press twice to exit
Shift+Enter Insert a newline
!command Run a shell command directly (e.g. !ls -la)

Slash commands

Command What it does
/new Start a fresh conversation
/model <model-id>[:variant] Switch model (or list models without args)
`/variant [variant unset]`
/sessions List saved sessions for this project
/resume <id> Resume a saved session
/undo Rewind to before the last user message
/compact [focus] Summarize older context to free up the window
/view <focused|detailed> Switch between compact activity and the complete transcript
/skills List available skills
/skill:<name> Run a skill
/agents List available subagents
/mcp List connected MCP servers and tools
/mcps Enable or disable MCP servers (Ctrl+E)

Pace starts in focused view, which keeps every user message plus the turn's agent messages — while Pace is working, the last three agent messages stay visible so recent progress is in view, and once a turn finishes it collapses to its final message. Reasoning, tool output, and meta noise are always hidden, and a Working... line shows what Pace is doing while it works. The complete transcript is still recorded; press Ctrl+G or use /view detailed to reveal it at any time, including while Pace is working.

File and image references

  • @filename — mention a project file (autocomplete with Tab)
  • @image(./path.png) — attach an image inline
  • Bare image paths like ./screenshot.png are also auto-detected

Headless mode

pace run executes the agent non-interactively with the same wiring as the TUI (AGENTS.md, skills, MCP, subagents, sessions). The prompt comes from an argument, --prompt, --prompt-file, or piped stdin.

pace run "Summarize this repo"                     # plain text to stdout
pace run -p "fix the failing test" --model fireworks/glm-5.3:max
cat prompt.txt | pace run --output-format json
pace run --prompt-file task.md --output-format stream-json
pace run --session <id> "continue where we left off"
pace run --continue "what did we decide?"

Options:

Flag Description
-p, --prompt <text> Prompt text
--prompt-file <path> Read the prompt from a file
--session <id> Continue an existing session
--continue Continue the most recent session for this project
--model <id[:variant]> Model selection (falls back to config defaultModel)
--output-format <f> text (default), json (final result object), or stream-json (NDJSON events)
--max-turns <n> Cap assistant turns (exit code 2 when hit)
--append-system <text|@file> Append extra system text
--steering-stdin Read NDJSON steering messages from stdin while running: {"type":"steer","text":"..."}

stream-json emits one JSON object per line: system (session id, model), text_delta, reasoning_delta, tool_start / tool_input / tool_output / tool_content / tool_end, usage (per assistant turn), compaction (when the context is compacted), and a final result (text, turns, usage, cost, stop reason). Exit codes: 0 finished, 1 error, 2 turn cap, 130 cancelled.

Sessions created by pace run are normal Pace sessions: resume them in the TUI with pace --session-id <id>, or continue them headlessly with --session <id>.

Context compaction

Long conversations are compacted automatically so the model never runs out of window. When the context reaches the threshold, Pace summarizes the older conversation with a one-shot LLM call (same model, system prompt, and tools, so cache-capable providers serve it mostly from cache), appends a compaction entry to the session tree, and continues from the summary plus the most recent ~20k tokens verbatim. Nothing is deleted: /tree still shows the full history, and expanding the compacted block in the TUI shows the summary.

Compaction also runs manually with /compact [focus instructions] — useful to shed context mid-task. Focus instructions are passed to the summarizer ("Pay particular attention to: …").

Configure it under the compaction key (all fields optional):

{
  "compaction": {
    "auto": true,
    "thresholdTokens": 250000,
    "keepRecentTokens": 20000,
    "model": "opencode/deepseek-v4-flash:nothink"
  }
}
  • auto — enable auto-compaction (default true)
  • thresholdTokens — context size that triggers compaction, clamped to the model's window (default 250000)
  • keepRecentTokens — approximate size of the recent context kept verbatim (default 20000)
  • model — summarizer model override; defaults to the current model for cache sharing

Configuration

Pace reads global configuration from ~/.config/pace/config.json.

Pace also reads a global instructions file from ~/.config/pace/AGENTS.md and merges it into the system prompt alongside the project AGENTS.md in the current working directory. Global instructions apply in every project; project-specific instructions take precedence. Create the file to set preferences that should always apply, such as:

Always talk in ASD-STE100 Simplified Technical English.
Always talk to me like I have ADHD.

Choose the startup model and the models that Tab / Shift+Tab cycle through with full provider/model IDs. Omit :variant for the unset/default selection, or use provider/model:variant to select a provider-native variant explicitly:

{
  "defaultModel": "opencode/gpt-5.5:medium",
  "cycleModels": [
    "opencode/gpt-5.5:medium",
    "opencode/gpt-5.5:high",
    "opencode/kimi-k2.6",
    "openai/gpt-5.5:xhigh"
  ],
  "sessionTitleModel": "opencode/deepseek-v4-flash:nothink"
}

Pace remembers the last explicit variant used for each model in memory, so cycling away from a model and back restores that model's previous variant during the current run. Use Ctrl+T until the model label has no :variant, or run /variant unset, to return to the provider default without sending explicit variant options.

Pace uses sessionTitleModel to auto-name new sessions after the first user message. Choose a cheap, fast text model; for models with thinking variants, prefer a no-thinking variant such as opencode/deepseek-v4-flash:nothink. If title generation fails, Pace falls back to showing a preview of the first user message.

To display estimated costs in a specific currency, configure a USD conversion rate, display format, and how many fraction digits:

{
  "cost": {
    "conversionRate": 10,
    "format": "{amount} kr",
    "fractionDigits": 1
  }
}

If fractionDigits is omitted, Pace uses dynamic precision similar to the default USD display.

While a tool call's arguments stream in from the model (for example the content of a large write), Pace appends the number of bytes received so far to the live tool title, e.g. write: src/app.ts · 12600 B. To turn it off:

{
  "toolProgress": {
    "streamedBytes": false
  }
}

Omarchy theme synchronization

Pace detects the active Omarchy theme background, uses its built-in dark or light theme for the interface, and paints the canvas with the theme's exact background color. To update a running Pace session when Omarchy changes theme, install the included hook once from the Pace repository:

install -Dm755 scripts/omarchy-theme-set-pace-hook \
  "$HOME/.config/omarchy/hooks/theme-set.d/pace-theme"

The hook sends Pace SIGUSR2 after Omarchy changes its theme. Pace then reads the active theme from ~/.local/state/omarchy/current/theme/colors.toml (falling back to ~/.config/omarchy/current/theme/colors.toml on older Omarchy versions) and updates without a restart. The hook does not modify Pace configuration.

Pace follows the Omarchy dark or light mode and copies the theme's background color onto the canvas, including the inline text areas that sit on it (agent messages, reasoning, meta, and inline tool lines). The background is painted as 24-bit color when the terminal supports it and as the nearest 256-color index otherwise; set PACE_TRUECOLOR=0 or PACE_TRUECOLOR=1 to force either mode. All other colors keep using the built-in dark or light palette, and individual Omarchy accent colors are not copied. A manual /theme dark or /theme light selection remains active until the next Omarchy theme change.

Subagents

Subagents are specialized helpers that run in their own isolated context window. The main agent delegates tasks to them with the agent tool. Each subagent returns only its final result, so exploration and tool noise stay out of the main conversation.

Pace ships with one built-in agent:

  • explore — fast, read-only codebase exploration

Define your own agents as Markdown files with YAML frontmatter:

  • Project: .agents/agents/<name>.md
  • Global: ~/.agents/agents/<name>.md
---
name: reviewer
description: Reviews code changes for bugs, security issues, and missing tests
tools: read, bash
---
You are a code reviewer. Analyze the given changes and report:
- Bugs and edge cases
- Security concerns
- Missing tests

Do not modify any files.

Frontmatter fields:

Field Description
name Must match the file name
description Tells the main agent when to delegate. Be specific about trigger conditions
tools Comma-separated allowlist. Omit for all tools
model Optional model id, e.g. opencode/claude-haiku-4-5. Defaults to the current model
variant Optional variant id for the model, e.g. low (reasoning effort). Ignored if the model has no such variant

Subagents cannot spawn subagents. Use /agents to list available agents.

MCP servers

Configure external tool servers in ~/.config/pace/mcp.json:

{
  "filesystem": {
    "type": "local",
    "command": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "~"],
    "enabled": true
  },
  "remote-api": {
    "type": "remote",
    "url": "https://example.com/mcp",
    "headers": { "Authorization": "Bearer <token>" },
    "enabled": true
  }
}

MCP tools show up as mcp__<server>__<tool> and the agent uses them automatically when relevant.

mcp.json is a stable, manually-authored config file — Pace never writes to it. To enable or disable servers at runtime, press Ctrl+E or run /mcps to open a picker with checkboxes (like the model picker): space toggles a server, and the change takes effect immediately (the server connects or disconnects and its tools appear or disappear). The enabled/disabled state is saved to ~/.config/pace/prefs.json and wins over the enabled field in mcp.json.

Project structure

Pace is an npm workspaces monorepo with three packages, layered so each package only depends on the one below it:

apps/pace       →  @pace/agent  →  @pace/llm
(TUI app)          (runtime)        (LLM SDK)
Workspace Package Purpose
packages/llm @pace/llm Provider-agnostic LLM SDK: streaming requests, tool calls, model catalog
packages/agent @pace/agent Agent runtime: agent loop, sessions, tools, MCP, skills, subagents
apps/pace pace The terminal experience: input, rendering, slash commands

packages/llm — LLM SDK

Module Purpose
src/provider.ts Neutral message, stream-event, tool-result, and Provider interface types
src/providers/anthropic.ts Anthropic Messages API provider
src/providers/openai.ts OpenAI Responses API provider
src/providers/openai-compatible.ts Shared Chat Completions/responses implementation for OpenAI-compatible endpoints
src/providers/{opencode-zen,fireworks,friendli,lmstudio,openrouter,compactifai,xiaomi}.ts Thin configurations of the shared OpenAI-compatible provider
src/registry.ts resolveProvider() model-to-provider routing with lazy instantiation
src/models.ts, src/model-catalog.ts Built-in model catalog plus remote refresh from models.dev
src/events.ts, src/fetch-retry.ts Typed event bus and fetch retry/backoff

packages/agent — agent runtime

Module Purpose
src/loop.ts runAgentLoop(): the event-driven stream → tool-call → stream cycle, shared by the main prompt and subagents
src/session.ts Tree-shaped session entries, turn drafts, undo, JSON persistence
src/tools/ Tool registry and built-in tools (bash, read, write, edit, web-fetch, web-search, skill, agent)
src/mcp-config.ts, src/mcp-transport.ts, src/mcp-client.ts, src/mcp-tools.ts Hand-rolled MCP client and tool bridging
src/skill.ts, src/agent.ts, src/subagent.ts Skill discovery, subagent definitions, headless subagent execution
src/preferences.ts, src/frontmatter.ts Runtime preference storage and YAML frontmatter parsing

apps/pace — terminal app

Module Purpose
src/app.ts Entry point: startup, slash commands, system prompt assembly, loop-event → UI wiring
src/tui.ts ANSI renderer: blocks, input editor, overlays, mouse support
src/view-model.ts Render-block types and formatters shared by live view and session replay
src/themes.ts, src/syntax.ts, src/terminal-utils.ts, src/clipboard.ts, src/git.ts, src/fuzzy.ts, src/reasoning.ts, src/session-view.ts Supporting UI modules

Development

Install and build all workspaces:

npm install
npm run build

Type check:

npm run lint

Run the test suite (mock-provider tests for the agent loop):

npm test

Start the app from source:

npm start

About

Coding agent for the terminal.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Contributors

Languages