Module: github.com/PizenLabs/ohgo · Status: working product (see
CHANGELOG.md)
# From source (static binary, no cgo)
make build
./ohgo --help
# Install into ~/.local/bin (first on PATH, no sudo needed)
make install
# System-wide install instead
sudo make install PREFIX=/usr/local
# Release tarball, once a version is published on GitHub
OHGO_VERSION=v0.1.0 sh install.sh
# Docker
make docker
docker run -it -v "$PWD:/work" ohgo:devRequires Go 1.27+ for source builds. No C toolchain: CGO_ENABLED=0 go build ./...
succeeds and the dependency graph contains zero C packages.
First run:
ohgo init # writes ~/.ohgo/settings.json (never overwrites without --force)
ohgo doctor # every failure names its fix
ohgo # interactive session| Provider | Auth | Protocol | Models |
|---|---|---|---|
| Anthropic | ANTHROPIC_API_KEY |
Messages | 28 bundled |
| OpenAI | OPENAI_API_KEY |
Chat Completions | 47 bundled (Responses-only lines excluded) |
| Groq, Together, Fireworks, DeepSeek, Cerebras, Mistral | per-provider key | Chat Completions | bundled |
| OpenRouter | OPENROUTER_API_KEY |
Chat Completions | 7 seeds + live discovery |
| OpenCode Zen / OpenCode Go | OPENCODE_API_KEY (shared) |
Chat Completions, Messages | 71 / 34 seeds + live discovery |
| Ollama (local) | none | native chat | live discovery only |
| Ollama Cloud | OLLAMA_API_KEY |
native chat | 52 seeds + live discovery |
Not yet supported: xAI and Azure/OpenAI Responses-only models (no Responses
transport), Google Gemini (no google-generative-ai transport), AWS Bedrock
(SigV4 signing), pi-vfs/pi-walker internals, MCP/LSP/DAP.
Credentials resolve in order: explicit request key, ~/.ohgo/credentials.json
(0600, written only when you save), environment, then parsed shell startup
files (read as text, never executed). ohgo doctor and /usage show
which source won — values are never printed.
Shell completion and reference docs ship with the binary:
ohgo completion bash > /etc/bash_completion.d/ohgo # bash|zsh|fish|powershell
ohgo docs --dir ./ohgo-docs # man pages + markdownexport ANTHROPIC_API_KEY=sk-... # or OPENAI_API_KEY, GROQ_API_KEY, ...
ohgo doctor # check the setup first; every failure names its fix
ohgo "summarise this repository" # one-shot via `ohgo run "…"`
ohgo # interactive session (full-screen UI on a TTY)
ohgo --no-tui # plain REPL instead
ohgo --resume 12 # continue session 12
ohgo models groq # list one provider's models
ohgo sessions show 12 # inspect a saved transcriptFirst run? ohgo doctor tells you what is missing — usually one environment
variable — and nothing else fails silently because of it.
Reads, edits, and runs code in your repository through eleven tools
(read write edit bash wait glob grep ls webfetch todo task), with session
persistence, context compaction, subagent delegation (--max-depth),
a wall-clock run budget (--deadline), and per-tool approval prompts.
In-session commands: /help /commands /clear /model [provider/id] /models /sessions /jobs /theme [dark|light] /connect /disconnect [provider] /status /usage /doctor /diff [target] /retry /compact /quit. The REPL and the full-screen UI share the same backend, so every command means the same thing in both. /status reports the workspace and session; /usage opens the usage and provider inspector.
Mid-session /model resolves through the same catalog as ohgo models,
so a listed model is always switchable. /commands (or Tab on a /-line)
opens a searchable command palette; every row runs the command it names.
/diff opens an interactive diff inspector: a hierarchical changed-file tree
with A/M/D badges, a +X -Y stats bar, and per-file hunks with
word-level highlighting. It fetches only the file list and counts on open and
loads each file's hunks on demand (cached), so a large changeset opens
instantly. /diff compares the working tree against HEAD, /diff <target>
compares it against a revision, and /diff <base>..<target> compares two
revisions. Keys: tab focus · j/k move · enter open · / filter ·
esc close.
The full-screen UI is one adaptive workbench: header (identity, runtime
phase, model, connection), conversation, composer, status line
(cwd, session, context %, measured usage). Layout follows the terminal size
(WindowSizeMsg is a first-class input; a resize re-renders but never
starts work or drops history):
- Wide (≥100 cols): full metadata plus keyboard hints.
- Medium (70–99): reduced metadata, full conversation.
- Narrow (<70): conversation first; secondary metadata collapses before readability does. Footers stay one row.
- Mini (<40 cols or <8 rows): a size notice instead of overlapping regions. Input and history are preserved; enlarge to resume.
On an empty conversation the workspace shows a short welcome — identity, one
invitation to describe a task, and at most three examples — as chrome, never
as a transcript entry: it cannot enter the session log, it disappears the
moment real content arrives, it never reappears over a resumed transcript, and
/clear brings it back. Narrow terminals drop the examples and keep the
introduction plus a /help pointer. The header owns identity, model, and
runtime phase; the status line owns workspace context (cwd, session, context
%, measured usage) and never repeats them. Short content is padded below the
transcript so the composer stays pinned to the bottom of the screen.
Shortcuts that are shown always work: enter send (queues while busy),
alt+enter/ctrl+j insert a break, ↑ recalls history on a single line,
pgup/pgdn scroll, esc cancels a turn (never quits), ctrl+c cancels
when busy, clears a non-empty draft, or points at the exit chords when idle
(never quits), ctrl+d on an empty composer quits. Approval
dialogs default to the conservative choice; closing, cancelling, or resizing
never approves. Widths are measured in terminal cells (CJK/emoji/ANSI
aware), long URLs and tool output wrap and bound rather than overflow.
The /model picker lists the merged catalog (bundled plus live-cached
rows) with the active model marked, provider scope, result count, and
freshness (live, cached, bundled, refreshing…). Search is
multiword and case-insensitive across name, id, provider, and metadata
(openrouter, free, deepseek v4 free, provider:openrouter);
free matches verified zero-price rows, falling back to name text only
when pricing is unknown — unknown prices are never rendered as free.
Picker keys: printable input including space searches, ↑↓ move,
PgUp/PgDn page, Home/End jump, ←→ move the query cursor,
enter selects the highlighted canonical id, esc clears the query
first and closes second, ctrl+r refreshes the live catalog in the
background. A stale snapshot refreshes automatically on open; failures keep
cached rows with a reason, and a slower older refresh never overwrites a
newer one.
~/.ohgo/settings.json (project .ohgo/settings.json merges over it):
{
"model": "groq/llama-3.1-8b-instant",
"maxTokens": 32000,
"theme": "dark",
"approvals": { "bash": "deny", "write": "allow" }
}"allow" never asks, "deny" never runs; anything else (or absent) prompts
each time. ohgo init writes model and theme; /theme updates theme
in place.
Precedence for the model: --model flag, then settings, then OHGO_MODEL,
then the catalog default (anthropic/claude-sonnet-4-5). An unknown name is
an error everywhere — a silent fallback would send your conversation to a
provider you never chose.
OHGO_HISTORY_FILE overrides the plain-REPL history location
(~/.ohgo/history by default; empty disables it).
See ARCHITECTURE.md for the layer map, turn lifecycle,
concurrency rules, error taxonomy, storage, transports, and performance
notes. Port invariants live in MEMORY.md; the incremental plan
in PORT.md; status in PROGRESS.md.
Key properties:
- The loop never touches HTTP. All transport goes through
ai.StreamFn, the seam every test drives. - Model facts are data.
catalog.Resolve(provider/id)is the only source; no code branches on model id strings. - Containment is structural. Tool paths resolve inside the working root in one place, so no call site can forget the check.
- Isolation is structural. A subagent at the depth cap is built without a
tasktool, so it cannot delegate however prompted. - Budgets are explicit. Completion budget defaults to the model's ceiling; the run is bounded by wall-clock deadline, and expiry returns completed turns rather than an error.
make check # build + vet + gofmt + full test suite
make race # same, with the race detector
make bench # glob/grep/agent/ai benchmarkscmd/ohgo/integration_test.go drives a full tool call through the real
transport and filesystem against an httptest provider; TestWireRequestCarries…
tests assert budgets and endpoints on the actual request body, because unit
tests once missed a duplicated budget block.