AGENTS.md

  1# Crush Development Guide
  2
  3## Project Overview
  4
  5Crush is a terminal-based AI coding assistant built in Go by
  6[Charm](https://charm.land). It connects to LLMs and gives them tools to read,
  7write, and execute code. It supports multiple providers (Anthropic, OpenAI,
  8Gemini, Bedrock, Copilot, Hyper, MiniMax, Vercel, and more), integrates with
  9LSPs for code intelligence, and supports extensibility via MCP servers and
 10agent skills.
 11
 12The module path is `github.com/charmbracelet/crush`.
 13
 14## Architecture
 15
 16```
 17main.go                            CLI entry point (cobra via internal/cmd)
 18internal/
 19  app/app.go                       Top-level wiring: DB, config, agents, LSP, MCP, events
 20  cmd/                             CLI commands (root, run, login, models, stats, sessions)
 21  config/
 22    config.go                      Config struct, context file paths, agent definitions
 23    load.go                        crush.json loading and validation
 24    provider.go                    Provider configuration and model resolution
 25  agent/
 26    agent.go                       SessionAgent: runs LLM conversations per session
 27    coordinator.go                 Coordinator: manages named agents ("coder", "task")
 28    hooked_tool.go                 Decorator that runs PreToolUse hooks before tool execution
 29    prompts.go                     Loads Go-template system prompts
 30    templates/                     System prompt templates (coder.md.tpl, task.md.tpl, etc.)
 31    tools/                         All built-in tools (bash, edit, view, grep, glob, etc.)
 32      mcp/                         MCP client integration
 33  hooks/                           Hook engine: runs user shell commands on hook events
 34    hooks.go                       Decision types, aggregation logic, event constants
 35    runner.go                      Parallel hook execution, timeout, dedup
 36    input.go                       Stdin payload builder, env vars, stdout parsing (Crush + Claude Code compat)
 37  session/session.go               Session CRUD backed by SQLite
 38  message/                         Message model and content types
 39  db/                              SQLite via sqlc, with migrations
 40    sql/                           Raw SQL queries (consumed by sqlc)
 41    migrations/                    Schema migrations
 42  lsp/                             LSP client manager, auto-discovery, on-demand startup
 43  ui/                              Bubble Tea v2 TUI (see internal/ui/AGENTS.md)
 44  permission/                      Tool permission checking and allow-lists
 45  skills/                          Skill file discovery and loading
 46  shell/                           Bash command execution with background job support
 47  event/                           Telemetry (PostHog)
 48  pubsub/                          Internal pub/sub for cross-component messaging
 49  filetracker/                     Tracks files touched per session
 50  history/                         Prompt history
 51```
 52
 53### Key Dependency Roles
 54
 55- **`charm.land/fantasy`**: LLM provider abstraction layer. Handles protocol
 56  differences between Anthropic, OpenAI, Gemini, etc. Used in `internal/app`
 57  and `internal/agent`.
 58- **`charm.land/bubbletea/v2`**: TUI framework powering the interactive UI.
 59- **`charm.land/lipgloss/v2`**: Terminal styling.
 60- **`charm.land/glamour/v2`**: Markdown rendering in the terminal.
 61- **`charm.land/catwalk`**: Snapshot/golden-file testing for TUI components.
 62- **`sqlc`**: Generates Go code from SQL queries in `internal/db/sql/`.
 63
 64### Key Patterns
 65
 66- **Config is a Service**: accessed via `config.Service`, not global state.
 67- **Tools are self-documenting**: each tool has a `.go` implementation and a
 68  `.md` description file in `internal/agent/tools/`.
 69- **System prompts are Go templates**: `internal/agent/templates/*.md.tpl`
 70  with runtime data injected.
 71- **Context files**: Crush reads AGENTS.md, CRUSH.md, CLAUDE.md, GEMINI.md
 72  (and `.local` variants) from the working directory for project-specific
 73  instructions.
 74- **Persistence**: SQLite + sqlc. All queries live in `internal/db/sql/`,
 75  generated code in `internal/db/`. Migrations in `internal/db/migrations/`.
 76- **Pub/sub**: `internal/pubsub` for decoupled communication between agent,
 77  UI, and services.
 78- **Hooks**: User-defined shell commands in `crush.json` that fire before
 79  tool execution. The engine (`internal/hooks/`) is independent of fantasy
 80  and agent — it takes inputs, runs commands, returns decisions. The
 81  `hookedTool` decorator in `internal/agent/hooked_tool.go` wraps tools at
 82  the coordinator level. Hooks run before permission checks. See
 83  `HOOKS.md` for the user-facing protocol.
 84- **API key validation**: `ProviderConfig.TestConnection` in
 85  `internal/config/config.go` proves authentication per-provider rather
 86  than hitting `/models` everywhere (many gateways expose a public
 87  `/models`, which proves nothing). Three outcomes: validated, invalid,
 88  or `ErrValidationUnsupported` — the last is rendered by the UI as
 89  "saved (not verified)" and is a first-class result, not a failure.
 90  Any PR that adds or changes a provider probe, classifier, or the
 91  openai-compat allowlist must update `internal/config/VALIDATION.md`
 92  and the audit table in `config_validate_test.go` in the same commit.
 93- **CGO disabled**: builds with `CGO_ENABLED=0` and
 94  `GOEXPERIMENT=greenteagc`.
 95
 96## Build/Test/Lint Commands
 97
 98- **Build**: `go build .` or `go run .`
 99- **Test**: `task test` or `go test ./...` (run single test:
100  `go test ./internal/llm/prompt -run TestGetContextFromPaths`)
101- **Update Golden Files**: `go test ./... -update` (regenerates `.golden`
102  files when test output changes)
103  - Update specific package:
104    `go test ./internal/tui/components/core -update` (in this case,
105    we're updating "core")
106- **Lint**: `task lint:fix`
107- **Format**: `task fmt` (`gofumpt -w .`)
108- **Modernize**: `task modernize` (runs `modernize` which makes code
109  simplifications)
110- **Dev**: `task dev` (runs with profiling enabled)
111
112## Code Style Guidelines
113
114- **Imports**: Use `goimports` formatting, group stdlib, external, internal
115  packages.
116- **Formatting**: Use gofumpt (stricter than gofmt), enabled in
117  golangci-lint.
118- **Naming**: Standard Go conventions — PascalCase for exported, camelCase
119  for unexported.
120- **Types**: Prefer explicit types, use type aliases for clarity (e.g.,
121  `type AgentName string`).
122- **Error handling**: Return errors explicitly, use `fmt.Errorf` for
123  wrapping.
124- **Context**: Always pass `context.Context` as first parameter for
125  operations.
126- **Interfaces**: Define interfaces in consuming packages, keep them small
127  and focused.
128- **Structs**: Use struct embedding for composition, group related fields.
129- **Constants**: Use typed constants with iota for enums, group in const
130  blocks.
131- **Testing**: Use testify's `require` package, parallel tests with
132  `t.Parallel()`, `t.SetEnv()` to set environment variables. Always use
133  `t.Tempdir()` when in need of a temporary directory. This directory does
134  not need to be removed.
135- **JSON tags**: Use snake_case for JSON field names.
136- **File permissions**: Use octal notation (0o755, 0o644) for file
137  permissions.
138- **Log messages**: Log messages must start with a capital letter (e.g.,
139  "Failed to save session" not "failed to save session").
140  - This is enforced by `task lint:log` which runs as part of `task lint`.
141- **Comments**: End comments in periods unless comments are at the end of the
142  line.
143
144## Testing with Mock Providers
145
146When writing tests that involve provider configurations, use the mock
147providers to avoid API calls:
148
149```go
150func TestYourFunction(t *testing.T) {
151    // Enable mock providers for testing
152    originalUseMock := config.UseMockProviders
153    config.UseMockProviders = true
154    defer func() {
155        config.UseMockProviders = originalUseMock
156        config.ResetProviders()
157    }()
158
159    // Reset providers to ensure fresh mock data
160    config.ResetProviders()
161
162    // Your test code here - providers will now return mock data
163    providers := config.Providers()
164    // ... test logic
165}
166```
167
168## Formatting
169
170- ALWAYS format any Go code you write.
171  - First, try `gofumpt -w .`.
172  - If `gofumpt` is not available, use `goimports`.
173  - If `goimports` is not available, use `gofmt`.
174  - You can also use `task fmt` to run `gofumpt -w .` on the entire project,
175    as long as `gofumpt` is on the `PATH`.
176
177## Comments
178
179- Comments that live on their own lines should start with capital letters and
180  end with periods. Wrap comments at 78 columns.
181
182## Committing
183
184- ALWAYS use semantic commits (`fix:`, `feat:`, `chore:`, `refactor:`,
185  `docs:`, `sec:`, etc).
186- Try to keep commits to one line, not including your attribution. Only use
187  multi-line commits when additional context is truly necessary.
188
189## Working on the TUI (UI)
190
191Anytime you need to work on the TUI, read `internal/ui/AGENTS.md` before
192starting work.