sandbar

module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 23, 2026 License: MIT

README

Sandbar

Sandbar is a standalone terminal AI coding-agent harness in Go: a streaming REPL backed by any OpenAI-compatible model endpoint — cloud (OpenRouter, OpenAI, Gemini) or local (Ollama, llama.cpp, vLLM) — with 15 built-in tools behind tiered approvals, a SQLite thread store with full-text search, automatic context compression, subagents, plan mode, themes, and a --json event stream for scripting. It boots with zero configuration from OPENAI_API_KEY, ships as a single static binary, and has no server component and no telemetry: everything runs and stays on your machine.

Features

  • Streaming REPL — inline Bubble Tea interface (no alt-screen) with live reasoning display, context gauge, and session timer.
  • 15 tools, tiered approvals — file read/write/append/patch, shell (local or SSH), background jobs, git, web search, content search, web fetch, todo tracking, subagent delegation/resume, image generation, vision analysis. Every tool is classified read/write/exec; approve per tier, per tool, or per session. Approvals fail closed in headless runs.
  • SHA-256 write preconditions — mutating file tools require the digest observed at last read, so conflicting writes fail loudly instead of silently overwriting.
  • Cost rollups — usage events are priced against an embedded models.dev catalog snapshot (fully offline); the status bar and one-shot footer show cumulative spend, hidden for unknown or free models.
  • Read schemesfile_read resolves pr://<n>, issue://<n> (GitHub via your gh CLI) and agent://<task-id> (persisted subagent transcripts) before touching the filesystem.
  • SQLite thread store — every conversation persisted locally with WAL journaling, FTS5 full-text search (/search), session resume, forking, and undo.
  • Context auto-compression — real BPE token counting (offline-embedded vocabulary), summarization with group-aware boundaries that never split tool calls from results, and observable fallbacks. No silent history loss.
  • Subagents — delegate self-contained subtasks (delegate_task), resume interrupted ones (resume_task); independent delegations run concurrently.
  • Plan mode--plan / /plan runs a read-only turn that produces a plan you approve before anything changes.
  • Themes — light/dark/monochrome plus Catppuccin, Tokyo Night, Rosé Pine, Gruvbox, Dracula, and more; NO_COLOR respected.
  • --json scripting mode — newline-delimited StreamEvent stream for scripts and benchmark harnesses; pipe stdin in, events out.
  • Workspace jail — file operations resolve to the configured workspace root; path traversal and workspace-escaping shell commands are rejected.
  • Zero-config bootOPENAI_API_KEY alone is enough to start; a commented config template is written for you on first run.
  • Single static binary — pure-Go SQLite, CGO disabled, cross-compiles to linux/darwin/windows/freebsd on amd64/arm64.

Quick Start

The fastest path needs no config file at all:

export OPENAI_API_KEY=sk-...
sandbar

That synthesizes an OpenAI provider from the environment (OPENAI_BASE_URL and OPENAI_MODEL override the endpoint and model alias) and writes a commented config.yaml template to ~/.config/sandbar/config.yaml for you to edit later. See config.yaml.example for the full annotated schema:

workspace: "./workspace"          # file ops are jailed to this directory
database:  "sandbar.db"           # resolves under ~/.local/share/sandbar

providers:
  - name: openrouter-direct
    base_url: "https://openrouter.ai/api/v1"
    api_key: "${OPENROUTER_API_KEY}"
    models:
      google/gemini-3.1-flash-lite:
        context_length: 262144
    model_defaults:
      supports_tools: true

tools:
  approval:
    mode: "yolo"                  # yolo | write | always-ask

compression:
  enabled: true
  threshold: 0.80
Install

Prebuilt binary — via the install script:

curl -fsSL https://raw.githubusercontent.com/aetherbird/sandbar/main/install.sh | bash

The script detects your platform, downloads the release archive, verifies it against the published sha256 checksums, and installs to ~/.local/bin (override with BIN_DIR; pin a release with SANDBAR_VERSION=v0.3.0). Prebuilt binaries are not published yet — the install script and pinned versions will work once a goreleaser release ships (see docs/RELEASE.md). Build from source or go install in the meantime.

Build from source (Go 1.25+):

git clone https://github.com/aetherbird/sandbar.git
cd sandbar
make build        # produces ./sandbar (static, stripped, version-stamped)
make install      # installs to ~/.local/bin/sandbar

make build stamps the binary with git describe --tags --always --dirty; run sandbar version to see it.

go installgo install github.com/aetherbird/sandbar/cmd/sandbar@latest installs the binary as sandbar (module root is the repo root); the binary reports the module version of the tag it was installed from — see sandbar version.

Homebrew / Scoop — planned after the first release (tap and bucket generated by goreleaser).

Configuration

  • Config path — first of: --config <path>, $SANDBAR_CONFIG, $XDG_CONFIG_HOME/sandbar/config.yaml, ~/.config/sandbar/config.yaml, /etc/sandbar/config.yaml. The working directory is never searched.
  • Env varsSANDBAR_CONFIG (config path), SANDBAR_THEME (theme override); provider keys are interpolated into YAML as ${VAR} (e.g. ${OPENROUTER_API_KEY}, ${BRAVE_API_KEY}). OPENAI_API_KEY alone boots the zero-config default.
  • Client prefs~/.config/sandbar/client.yaml holds TUI-only preferences (default_model, theme, color_mode, font_size), auto-created with commented defaults on first run.
  • Data — the SQLite database lives under ~/.local/share/sandbar/ (absolute database: values are honored as-is).
  • models.json — a legacy-style provider registry layered on top of config.yaml providers (see models.json.example). Sandbar looks for models_json: in the config, then models.json next to the loaded config file. Schema: {"providers": {name: {baseUrl, api, apiKey, compat, models[]}}} with model entries {id, name, modelId, contextWindow, maxTokens} and compat quirks (supportsDeveloperRole, supportsReasoningEffort, maxTokensField, requiresToolResultName, requiresAssistantAfterToolResult, thinkingFormat, sendSessionId). Keys resolve as $ENV/${ENV} (unset → empty), !command (shell stdout, trimmed), or literal. On a provider-name clash models.json wins (the YAML provider is replaced, not an error); JSON providers are appended after the YAML ones. Importing a legacy file that lacks supports_tools defaults imported models to tool support. The zero-config env boot ignores models.json. api: "anthropic-messages" routes to the native Anthropic Messages wire client. Compat quirks currently honored: maxTokensField, requiresToolResultName, and thinkingFormat (mapped onto the reasoning dialect); supportsDeveloperRole, supportsReasoningEffort, requiresAssistantAfterToolResult, and sendSessionId are parsed but not yet applied.
Skills & Templates
  • Skills — on-demand instruction packs discovered from <workspace>/.sandbar/skills, .claude/skills, and .agents/skills (then ~/.config/sandbar/skills, ~/.claude/skills, ~/.agents/skills; earlier scopes shadow later ones by name). Each is a folder with a SKILL.md carrying a description: header; the system prompt advertises the list and the model reads the file only when relevant.
  • Prompt templates — markdown files in <workspace>/.sandbar/prompts or ~/.config/sandbar/prompts become slash commands: /name args expands the body ($1..$9, $@/$ARGUMENTS, ${@}, ${@:N}, ${@:N:L}) and submits it as your message. Registered commands win over same-named templates.
  • Prompt filesSYSTEM.md replaces the base persona instructions (everything else in the prompt still assembles around it), APPEND_SYSTEM.md appends at the end, and TITLE_SYSTEM.md templates the session title from the first message (all support {{cwd}}, {{date}}; the title file also {{message}}, {{firstLine}}). Looked up per file in <workspace>/.sandbar, .claude, .codex, .agents, then ~/.config/sandbar, ~/.claude, ~/.codex, ~/.agents — first existing wins; no ancestor walk.

Daily Use

Key What it does
/model Switch model (picker)
/sessions List and resume past sessions
/resume [id] Resume a session by id or unique prefix (picker without argument)
/new Start a fresh thread
/delete Delete the current thread (two-step: /delete confirm)
/title <text> Set the current session's title
/fork (/branch) Branch the current session
/compress (/compact) Compress context now
/undo Remove the last exchange
/search <query> Full-text search past conversations
/clear Clear the screen and start fresh
/noformat Re-print the last response as raw text
/redraw Repaint (recover from render drift)
/effort <level> Set reasoning effort: low | medium | high | default
/plan Toggle plan mode (read-only turn that produces a plan)
/theme Switch theme (picker or id)
/help (/?) Command reference
/quit (/q, /exit) Exit
! <command> Shell escape — run a command in the workspace
@path Mention a file; its content is expanded into the message

Editing. file_read stamps every line with an 8-hex content hash; paste those hash-prefixed lines into file_patch's old_str to anchor the edit — stale anchors are rejected with the current hashes instead of silently patching the wrong lines.

Pipe input for one-shot use:

cat main.go | sandbar "explain this file"
sandbar --json "list the failing tests" | jq -r 'select(.type=="token") | .content'

Privacy & Security

  • All local. Threads, messages, and summaries live in SQLite at ~/.local/share/sandbar. There is no telemetry, no crash reporting, and no server component. Outbound traffic goes only to the LLM/search providers you configure.
  • Fail-closed approvals. When approval policy requires a prompt but no interactive handler exists (headless/scripted runs), the tool call is denied — never silently allowed.
  • Workspace jail. File tools and dynamic shell commands are confined to the configured workspace; path traversal and workspace escapes are rejected. Note this is convenience hygiene, not a sandbox: the agent runs with your full OS privileges. Run it inside a container or OS sandbox if the context is untrusted. See SECURITY.md.

Development

make fmt          # go fmt ./... (CI also enforces gofmt -l is empty)
make test         # go test -race -count=1 -skip TestFullTuiPipeline ./...
make build        # CGO_ENABLED=0 static build
go vet ./...

Layout:

cmd/sandbar/           REPL entry point (the only binary)
internal/agent/        reasoning loop, subagents, steering
internal/backend/      Backend seam (LocalBackend)
internal/catalog/      embedded models.dev pricing snapshot
internal/cliadmin/     admin subcommands (doctor, config)
internal/cliui/        shared CLI rendering
internal/config/       YAML config, resolution, zero-config boot
internal/llm/          OpenAI-compatible client, stream events, tokens
internal/mcp/          Model Context Protocol clients
internal/memory/       SQLite store, FTS5 search, compression
internal/persona/      system prompt assembly, skills
internal/testutil/     test helpers
internal/tools/        tools, registry, approvals, jobs, ssh
internal/ui/theme/     palettes
migrations/            SQLite schema
tests/fixtures/        test fixtures
docs/DESIGN.md         full design document
go.mod / go.sum        module github.com/aetherbird/sandbar
Makefile               build/test targets
.goreleaser.yaml       release pipeline
install.sh             curl-pipeable installer
config.yaml.example    commented configuration template
models.json.example    legacy-style provider registry example
system-prompt.md       default persona prompt

License

MIT — see LICENSE.


Sandbar is forked from an in-house harness. Inspired by pi, opencode, and Claude Code.

Directories

Path Synopsis
cmd
sandbar command
internal
catalog
Package catalog resolves per-model pricing for cost rollups.
Package catalog resolves per-model pricing for cost rollups.
cliadmin
Package cliadmin contains command-line administration primitives that are independent of Sandbar's concrete CLI parser and renderer.
Package cliadmin contains command-line administration primitives that are independent of Sandbar's concrete CLI parser and renderer.
cliui
Package cliui owns Sandbar's terminal presentation primitives.
Package cliui owns Sandbar's terminal presentation primitives.
llm
mcp
Package mcp attaches configured MCP servers (internal/config) to sandbar using the official Go SDK (github.com/modelcontextprotocol/go-sdk).
Package mcp attaches configured MCP servers (internal/config) to sandbar using the official Go SDK (github.com/modelcontextprotocol/go-sdk).
testutil
Package testutil provides reusable test helpers for the Sandbar project.
Package testutil provides reusable test helpers for the Sandbar project.
ui/theme
Package theme provides Sandbar's shared semantic color-theme catalog.
Package theme provides Sandbar's shared semantic color-theme catalog.
Package migrations embeds the SQL schema migrations into the binary so the database can be created/upgraded without locating a migrations directory on disk (which previously had to sit next to the config file).
Package migrations embeds the SQL schema migrations into the binary so the database can be created/upgraded without locating a migrations directory on disk (which previously had to sit next to the config file).
tests

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL