- Build:
go build -o oc ./cmd/oc/ - Hot reload: uses
air(.air.toml),airrebuilds to./tmp/main - Go version: 1.26.3+ (go.mod)
- No tests in repo — no
_test.gofiles anywhere - No lint/typecheck scripts — standard Go tooling only
- Spelling:
IntialModel(notInitialModel) ininternal/tui/model.go:106— intentional name, do not "fix"
- Module:
oc(go.mod) - Entrypoint:
cmd/oc/main.go— creates Bubbletea v2 program withtui.IntialModel() - TUI framework:
charm.land/bubbletea/v2,charm.land/bubbles/v2,charm.land/lipgloss/v2,charm.land/glamour/v2 - External server: TUI is a client that spawns/manages
opencode serveas a subprocess. Server URL:http://127.0.0.1:4096
| Package | Responsibility |
|---|---|
cmd/oc/ |
Entrypoint |
internal/tui/ |
Bubbletea Model, Update, View, keybinding modes |
internal/tui/commands/ |
Cmds (side effects) and message types |
internal/api/ |
HTTP client to opencode server |
internal/server/ |
Manages opencode serve lifecycle (start/kill) |
internal/history/ |
Session persistence to ~/.oc/sessions/ |
internal/domain/ |
Shared types (Message, Session, Config) |
internal/sysprompt/ |
Multi-agent judge system prompt |
modeInsert, modeNormal, modeVisual, modeQus (questions), modeSession (browse), modeCmd, modePerm (permissions). Dispatched via onKeyPress → typed handler per mode. Handlers are on Model value receivers returning (Model, tea.Cmd).
All messages defined in internal/tui/commands/messages.go. Update dispatches via type switch in internal/tui/update.go:377. SSE listener (internal/tui/commands/sse.go) subscribes to /global/event and uses program.Send() for interactive messages (questions, permissions, streaming deltas).
question.asked→ControlRequestMsgpermission.asked→PermissionRequestMsgmessage.part.delta→ChatStreamMsg(text/reasoning chunks)message.part.updated→ resolves buffered deltas by part typemessage.updated→ChatStreamMsg{Done: true}when finish reason present
GET /global/health— health checkGET /config/providers— provider listGET /path— working directoryPOST /session— create sessionGET /session/:id— session details (tokens, limits)POST /session/:id/message— send messageGET /global/event— SSE event streamPOST /permission/:id/reply— reply to permission requestPOST /question/:id/reply— reply to question
- Events bubble as messages defined in
messages.go. Useprogram.Send()for messages that need to be dispatched while SSE listener blocks.
| Mode | Key | Action |
|---|---|---|
| — | / |
Show command menu |
| — | Esc |
Normal mode |
| Normal | i |
Insert mode |
| Normal | j/k |
Scroll down/up |
| Normal | gg/G |
Scroll to top/bottom |
| Normal | V |
Visual mode (select messages) |
| Visual | y |
Yank selected messages |
| Insert | Enter |
Send message |
| Insert | Ctrl+C |
Clear input / Quit |
/help, /sessions, /session new, /clear, /multiagent, /retry, /load <n>, /tokens, /exit
- GitHub Actions:
.github/workflows/release.yml— cross-compiles linux/darwin amd64/arm64, tarballs + checksums, triggered onv*tag - Install script:
scripts/install.sh - Build output ignored:
/dist/,/tmp/,/os,/oc(.gitignore)