Crush — Structure#
Layout pattern#
Custom “all-internal” monolith — a single main.go entry point at the repo root, with every package hidden under internal/. There is no pkg/ subtree and no cmd/ subdirectory; the multi-command CLI is implemented inside internal/cmd/ which is imported by main.go. This is an unusual but deliberate choice for an application binary that has no intention of being used as a library.
Directory map#
crush/
├── main.go Single entry point; registers swag annotations, delegates to internal/cmd
├── go.mod / go.sum Module: github.com/charmbracelet/crush
├── Taskfile.yaml Task runner (go-task): build, test, lint, sqlc, swag, release
├── crush.json Runtime configuration schema defaults
├── schema.json JSON Schema for config (generated by `task schema`)
├── sqlc.yaml sqlc configuration for query codegen
├── AGENTS.md In-repo agent skill definitions for AI tools
│
├── docs/ User-facing documentation
│ └── hooks/examples/ Hook examples for the extensibility system
│
├── scripts/ Two shell scripts (log-capitalisation lint, labeler helper)
│
├── .github/workflows/ GitHub Actions: build, lint, nightly, release, snapshot,
│ schema-update, security scan, CLA check
│
└── internal/ All application code (~359 .go files, ~50 packages)
│
├── cmd/ CLI layer — cobra commands + workspace wiring
│ ├── root.go Root command, Execute(), workspace bootstrap
│ ├── run.go `crush run` (non-interactive) command
│ ├── server.go `crush server` command (detached server mode)
│ ├── session.go `crush session` command
│ ├── login.go `crush login` command (OAuth flows)
│ ├── projects.go `crush projects` command
│ ├── models.go `crush models` command
│ ├── logs.go `crush logs` command
│ ├── schema.go `crush schema` command
│ ├── update_providers.go
│ ├── stats/ Embedded HTML/CSS/JS stats dashboard
│ └── gitignore/ Embedded .gitignore templates
│
├── app/ In-process application core (when not using client/server)
├── workspace/ Workspace interface + two implementations (AppWorkspace, ClientWorkspace)
├── server/ HTTP server over Unix socket / Windows named pipe
├── client/ HTTP client SDK for the server API
├── proto/ Shared wire types for client↔server communication
│
├── agent/ AI agent orchestration
│ ├── coordinator.go Agent loop + multi-agent coordination
│ ├── tools/ Tool implementations (file ops, shell, diff, MCP bridge)
│ ├── prompt/ System prompt templates
│ ├── hyper/ Hyper provider integration (embedded provider.json)
│ └── notify/ Desktop notification integration
│
├── backend/ LLM provider abstraction layer
├── client/ HTTP API client (server SDK)
│
├── config/ Configuration loading, schema, provider resolution
├── db/ SQLite connection, goose migrations, sqlc-generated queries
│ ├── migrations/ SQL migration files
│ └── sql/ Hand-written SQL queries for sqlc
│
├── session/ Session model and persistence
├── message/ Message model (user/assistant/tool messages)
├── history/ File-edit history tracking
├── projects/ Project registry
├── workspace/ Workspace interface (see above)
│
├── ui/ BubbleTea TUI — all view/model code
│ ├── model/ Root TUI model (main BubbleTea Model)
│ ├── chat/ Chat pane
│ ├── dialog/ Modal dialogs (permission, onboarding, etc.)
│ ├── diffview/ Diff viewer component
│ ├── completions/ Autocomplete component
│ ├── attachments/ File/image attachment UI
│ ├── list/ Reusable list component
│ ├── notification/ Desktop notification bridge
│ ├── anim/ Spinner/animation components
│ ├── logo/ Brand logo renderer
│ ├── styles/ Lipgloss styles and per-provider themes
│ ├── common/ Shared TUI state (Common struct)
│ ├── image/ Image rendering (sixel/kitty)
│ └── xchroma/ Chroma syntax highlighter integration
│
├── lsp/ Language Server Protocol client
├── hooks/ User-defined hook execution system
├── skills/ Built-in and user-defined agent skills
│ └── builtin/ Embedded skill files (crush-config, crush-hooks, jq)
├── commands/ Slash-command registry
├── permission/ Permission request/grant system
├── pubsub/ In-process publish-subscribe event bus
├── event/ Telemetry/metrics event emission
├── oauth/ OAuth flows (Copilot, Hyper providers)
│ ├── copilot/
│ └── hyper/
├── diff/ Diff computation utilities
├── diffdetect/ Diff detection heuristics
├── format/ Output formatting (spinner, markdown, etc.)
├── filetracker/ Agent file-read tracking
├── filepathext/ File-path utilities
├── fsext/ Filesystem utilities
├── stringext/ String utilities
├── ansiext/ ANSI escape utilities
├── csync/ Concurrent-safe synchronization helpers
├── env/ Environment variable helpers
├── home/ User home-directory resolution
├── log/ Structured logging setup (wraps log/slog)
├── update/ Self-update / version-check logic
├── version/ Version string (injected at build time via ldflags)
└── swagger/ Generated OpenAPI/Swagger spec (docs.go, swagger.json/yaml)Entry points#
| Binary | Source | Description |
|---|---|---|
crush | main.go → internal/cmd.Execute() | Single compiled binary; all subcommands are cobra sub-commands registered in internal/cmd/root.go |
There is no cmd/ top-level directory. All commands are registered under internal/cmd/:
| Subcommand | File | Purpose |
|---|---|---|
crush (default) | root.go | Launch interactive TUI |
crush run | run.go | Run a single non-interactive prompt |
crush server | server.go | Start a detached HTTP server |
crush session | session.go | Manage sessions (list, delete) |
crush login | login.go | OAuth login for providers |
crush projects | projects.go | List registered projects |
crush models | models.go | List available models |
crush logs | logs.go | Tail log output |
crush schema | schema.go | Print JSON Schema for config |
crush dirs | dirs.go | Print data/config directories |
crush stats | stats.go | Launch embedded stats dashboard |
crush update-providers | update_providers.go | Refresh provider metadata |
Package organization#
Internal packages#
All packages are under internal/ — the project exports nothing as a library.
Core application layer:
internal/app— In-process application object; owns the agent, config, db, and HTTP server lifecycleinternal/workspace—Workspaceinterface +AppWorkspace(in-process) /ClientWorkspace(HTTP client) implementations; the seam between CLI/TUI and the backendinternal/server— HTTP server over Unix socket/Windows named pipe; exposes REST API documented with Swaggointernal/client— Typed HTTP client for the server API (used byClientWorkspace)internal/proto— Shared DTO types used between server and client
Agent layer:
internal/agent— AI agent loop, tool dispatch, coordinator for sub-agentsinternal/agent/tools— Individual agent tools (file read/write, shell exec, diff, grep, etc.)internal/agent/tools/mcp— MCP tool bridge (forwards LLM tool calls to MCP servers)internal/agent/prompt— System prompt assembly from templatesinternal/agent/hyper— Hyper AI provider integration with embedded provider cataloginternal/backend— LLM provider abstraction (Anthropic, OpenAI, Gemini, Bedrock, etc.)
Persistence layer:
internal/db— SQLite connection viancruces/go-sqlite3; goose migrations; sqlc-generated type-safe query codeinternal/session— Session domain model and CRUDinternal/message— Message domain model (user/assistant/tool parts)internal/history— File-edit history per session
TUI layer (internal/ui/):
model— Root BubbleTea model: composes all sub-views, handles global key bindingschat— Chat pane (primary view)dialog— Modal dialogs (permission grants, onboarding wizard, provider setup)diffview— Inline diff viewercompletions— Slash-command autocompletestyles— Lipgloss styles and per-provider color themescommon— SharedCommonstruct passed to all sub-models
Extension/integration layer:
internal/lsp— LSP client (language server integration viasourcegraph/jsonrpc2)internal/hooks— User-defined hook scripts executed on agent eventsinternal/skills— Skill (.mdinstruction files) management; built-in skills embeddedinternal/commands— Slash-command registry (/clear,/compact, etc.)internal/permission— Permission request and grant trackinginternal/oauth— OAuth token flows for Copilot and Hyper providersinternal/event— Telemetry event emission (Catwalk analytics)
Utility packages:
internal/config— Config loading, schema validation, provider resolutioninternal/pubsub— In-process typed pub-sub businternal/format,internal/diff,internal/diffdetect— Output formatting, diff computationinternal/filetracker,internal/filepathext,internal/fsext— File operation helpersinternal/stringext,internal/ansiext,internal/csync— Misc utilitiesinternal/log,internal/env,internal/home,internal/version— Infrastructure stubs
Public packages (pkg/)#
None. Crush is a pure application binary with no exported library surface.
Layering#
CLI (internal/cmd)
│
▼
Workspace interface (internal/workspace)
│ │
▼ ▼
AppWorkspace ClientWorkspace
(in-process) (HTTP client)
│ │
▼ ▼
internal/app internal/server (Unix socket)
│
┌────┴────────────────────────────┐
│ │
Agent (internal/agent) TUI (internal/ui)
│
├── Backend (internal/backend) — LLM providers
├── Tools (internal/agent/tools) — file/shell/diff ops
├── LSP (internal/lsp)
├── MCP (internal/agent/tools/mcp)
└── DB (internal/db) — SQLite persistenceThe layering is clean: the CLI never calls the agent or TUI directly — everything goes through the Workspace interface. The TUI and the crush run non-interactive path use the same Workspace abstraction. The client/server split is optional (CRUSH_CLIENT_SERVER=1) and implemented transparently behind the same Workspace interface.
Build system#
- Build tool:
go-task(Taskfile.yaml) — replaces Makefile - Key targets:
task build—go build .with version ldflags; outputs./crush(orcrush.exe)task test—go test -race -failfast ./...task lint—golangci-lint+ log capitalisation scripttask sqlc— regenerate type-safe DB queries viasqlc generatetask swag— regenerate OpenAPI spec from Swaggo annotations inmain.go/internal/server/task hyper— regenerate embeddedprovider.jsonviago generatetask schema— regenerateschema.json(JSON Schema for config)task release— tag + push release (semver viasvu)task install—go installwith version ldflags
- Code generation: Three code generators in the build:
sqlc(DB queries),swag(OpenAPI),go generate(Hyper provider catalog) - CGO: Explicitly disabled (
CGO_ENABLED: 0) even though SQLite is used — achieved via the CGo-freencruces/go-sqlite3driver using WASM - Go experiment:
GOEXPERIMENT: greenteagcis set globally (enables the experimental green-tea GC for reduced pause times) - Docker: No Dockerfile present. Releases are built by GitHub Actions (
snapshot.yml/release.yml, likely using goreleaser)
Notable structural decisions#
No
pkg/orcmd/top-level directories. All code lives underinternal/. The multi-command CLI is insideinternal/cmd/rather than the idiomaticcmd/<binary>/main.golayout. This removes the false impression that any of these packages are importable.Workspaceinterface as the central seam. Rather than passing*app.Appto the TUI and CLI directly, both consume aWorkspaceinterface. This single design choice enables the optional client/server split (CRUSH_CLIENT_SERVER=1) — the TUI does not know or care whether the backend is in-process or across a Unix socket.Client/server architecture is opt-in but first-class. The server (
crush server) is a fully functional detached process exposing a REST API over a Unix socket. The client auto-starts, version-checks, and reconnects to this server. This architecture exists even though the default mode is in-process — it anticipates IDE integrations and headless automation.Code generation is a first-class citizen. Three separate generators (
sqlc,swag,go generate) are wired into the task runner. The generated files (internal/swagger/,internal/db/,internal/agent/hyper/provider.json) are committed to the repo.TUI package isolation. The
internal/ui/subtree is organised into fine-grained sub-packages per component (chat, dialog, diffview, completions, etc.) rather than a single flat package. Each sub-package returns a BubbleTeaModeland communicates with the root model only viatea.Msgmessages — a textbook BubbleTea architecture.WASM-based SQLite. By using
ncruces/go-sqlite3(a pure-Go WASM-embedded SQLite), the project achieves CGO-free static binaries while still having a full SQL database for persistence. This is an unusual and deliberate dependency choice that affects the entire build pipeline.