{"owner":"charmbracelet","repo":"crush","hasSkills":true,"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md"],"files":{"AGENTS.md":"# Crush Development Guide\n\n## Project Overview\n\nCrush is a terminal-based AI coding assistant built in Go by\n[Charm](https://charm.land). It connects to LLMs and gives them tools to read,\nwrite, and execute code. It supports multiple providers (Anthropic, OpenAI,\nGemini, Bedrock, Copilot, Hyper, MiniMax, Vercel, and more), integrates with\nLSPs for code intelligence, and supports extensibility via MCP servers and\nagent skills.\n\nThe module path is `github.com/charmbracelet/crush`.\n\n## Architecture\n\n```\nmain.go                            CLI entry point (cobra via internal/cmd)\ninternal/\n  app/app.go                       Top-level wiring: DB, config, agents, LSP, MCP, events\n  cmd/                             CLI commands (root, run, login, models, stats, sessions)\n  config/\n    config.go                      Config struct, context file paths, agent definitions\n    load.go                        crushrc and crush.json loading and validation\n    provider.go                    Provider configuration and model resolution\n  shellconfig/                      Bash-powered config format (crushrc builtins)\n  agent/\n    agent.go                       SessionAgent: runs LLM conversations per session\n    coordinator.go                 Coordinator: manages named agents (\"coder\", \"task\")\n    hooked_tool.go                 Decorator that runs PreToolUse hooks before tool execution\n    prompts.go                     Loads Go-template system prompts\n    templates/                     System prompt templates (coder.md.tpl, task.md.tpl, etc.)\n    tools/                         All built-in tools (bash, edit, view, grep, glob, etc.)\n      mcp/                         MCP client integration\n  hooks/                           Hook engine: runs user shell commands on hook events\n    hooks.go                       Decision types, aggregation logic, event constants\n    runner.go                      Parallel hook execution, timeout, dedup\n    input.go                       Stdin payload builder, env vars, stdout parsing (Crush + Claude Code compat)\n  session/session.go               Session CRUD backed by SQLite\n  message/                         Message model and content types\n  db/                              SQLite via sqlc, with migrations\n    sql/                           Raw SQL queries (consumed by sqlc)\n    migrations/                    Schema migrations\n  lsp/                             LSP client manager, auto-discovery, on-demand startup\n  ui/                              Bubble Tea v2 TUI (see internal/ui/AGENTS.md)\n  permission/                      Tool permission checking and allow-lists\n  skills/                          Skill file discovery and loading\n  shell/                           Bash command execution with background job support\n  event/                           Telemetry (PostHog)\n  pubsub/                          Internal pub/sub for cross-component messaging\n  filetracker/                     Tracks files touched per session\n  history/                         Prompt history\n```\n\n### Key Dependency Roles\n\n- **`charm.land/fantasy`**: LLM provider abstraction layer. Handles protocol\n  differences between Anthropic, OpenAI, Gemini, etc. Used in `internal/app`\n  and `internal/agent`.\n- **`charm.land/bubbletea/v2`**: TUI framework powering the interactive UI.\n- **`charm.land/lipgloss/v2`**: Terminal styling.\n- **`charm.land/glamour/v2`**: Markdown rendering in the terminal.\n- **`charm.land/catwalk`**: Snapshot/golden-file testing for TUI components.\n- **`sqlc`**: Generates Go code from SQL queries in `internal/db/sql/`.\n\n### Key Patterns\n\n- **Config is a Service**: accessed via `config.Service`, not global state.\n- **Tools are self-documenting**: each tool has a `.go` implementation and a\n  `.md` description file in `internal/agent/tools/`.\n- **System prompts are Go templates**: `internal/agent/templates/*.md.tpl`\n  with runtime data injected.\n- **Context files**: Crush reads AGENTS.md, CRUSH.md, CLAUDE.md, GEMINI.md\n  (and `.local` variants) from the working directory for project-specific\n  instructions.\n- **Bash config format**: Crush's primary config format is `crushrc` — a\n  Bash script using builtins (`provider`, `model`, `mcp`, `lsp`,\n  `permissions`, `hook`, `options`) to define config. `crush.json` is still\n  supported but is deprecated in favor of `crushrc` and may be removed in a\n  future release. Shell config files are discovered alongside JSON configs\n  and deep-merged through the same pipeline. Builtins are registered via\n  `shell.RegisterBuiltin` and gated by a `ConfigBuilder` on the context —\n  they are no-ops during normal bash tool execution. See\n  `internal/shellconfig/`.\n- **Persistence**: SQLite + sqlc. All queries live in `internal/db/sql/`,\n  generated code in `internal/db/`. Migrations in `internal/db/migrations/`.\n- **Pub/sub**: `internal/pubsub` for decoupled communication between agent,\n  UI, and services.\n- **Hooks**: User-defined shell commands in `crushrc` (or `crush.json`)\n  that fire before tool execution. The engine (`internal/hooks/`) is\n  independent of fantasy and agent — it takes inputs, runs commands,\n  returns decisions. The `hookedTool` decorator in\n  `internal/agent/hooked_tool.go` wraps tools at the coordinator level.\n  Hooks run before permission checks. See `HOOKS.md` for the user-facing\n  protocol.\n- **CGO disabled**: builds with `CGO_ENABLED=0` and\n  `GOEXPERIMENT=greenteagc`.\n\n## Build/Test/Lint Commands\n\n- **Build**: `go build .` or `go run .`\n- **Test**: `task test` or `go test ./...` (run single test:\n  `go test ./internal/llm/prompt -run TestGetContextFromPaths`)\n- **Update Golden Files**: `go test ./... -update` (regenerates `.golden`\n  files when test output changes)\n  - Update specific package:\n    `go test ./internal/tui/components/core -update` (in this case,\n    we're updating \"core\")\n- **Lint**: `task lint:fix`\n- **Format**: `task fmt` (`gofumpt -w .`)\n- **Modernize**: `task modernize` (runs `modernize` which makes code\n  simplifications)\n- **Dev**: `task dev` (runs with profiling enabled)\n\n## Code Style Guidelines\n\n- **Imports**: Use `goimports` formatting, group stdlib, external, internal\n  packages.\n- **Formatting**: Use gofumpt (stricter than gofmt), enabled in\n  golangci-lint.\n- **Naming**: Standard Go conventions — PascalCase for exported, camelCase\n  for unexported.\n- **Types**: Prefer explicit types, use type aliases for clarity (e.g.,\n  `type AgentName string`).\n- **Error handling**: Return errors explicitly, use `fmt.Errorf` for\n  wrapping.\n- **Context**: Always pass `context.Context` as first parameter for\n  operations.\n- **Interfaces**: Define interfaces in consuming packages, keep them small\n  and focused.\n- **Structs**: Use struct embedding for composition, group related fields.\n- **Constants**: Use typed constants with iota for enums, group in const\n  blocks.\n- **Testing**: Use testify's `require` package, parallel tests with\n  `t.Parallel()`, `t.SetEnv()` to set environment variables. Always use\n  `t.Tempdir()` when in need of a temporary directory. This directory does\n  not need to be removed.\n- **JSON tags**: Use snake_case for JSON field names.\n- **File permissions**: Use octal notation (0o755, 0o644) for file\n  permissions.\n- **Log messages**: Log messages must start with a capital letter (e.g.,\n  \"Failed to save session\" not \"failed to save session\").\n  - This is enforced by `task lint:log` which runs as part of `task lint`.\n- **Comments**: End comments in periods unless comments are at the end of the\n  line.\n\n## Testing with Mock Providers\n\nWhen writing tests that involve provider configurations, use the mock\nproviders to avoid API calls:\n\n```go\nfunc TestYourFunction(t *testing.T) {\n    // Enable mock providers for testing\n    originalUseMock := config.UseMockProviders\n    config.UseMockProviders = true\n    defer func() {\n        config.UseMockProviders = originalUseMock\n        config.ResetProviders()\n    }()\n\n    // Reset providers to ensure fresh mock data\n    config.ResetProviders()\n\n    // Your test code here - providers will now return mock data\n    providers := config.Providers()\n    // ... test logic\n}\n```\n\n## Formatting\n\n- ALWAYS format any Go code you write.\n  - First, try `gofumpt -w .`.\n  - If `gofumpt` is not available, use `goimports`.\n  - If `goimports` is not available, use `gofmt`.\n  - You can also use `task fmt` to run `gofumpt -w .` on the entire project,\n    as long as `gofumpt` is on the `PATH`.\n\n## Comments\n\n- Comments that live on their own lines should start with capital letters and\n  end with periods. Wrap comments at 78 columns.\n\n## Committing\n\n- ALWAYS use semantic commits (`fix:`, `feat:`, `chore:`, `refactor:`,\n  `docs:`, `sec:`, etc).\n- Try to keep commits to one line, not including your attribution. Only use\n  multi-line commits when additional context is truly necessary.\n\n## Working on the TUI (UI)\n\nAnytime you need to work on the TUI, read `internal/ui/AGENTS.md` before\nstarting work.\n\n## Styling System\n\nThe styling system lives in `internal/ui/styles/` and is organized into\nthree layers:\n\n- **`quickstyle.go`**: The stable base theme builder. `quickStyle(opts)`\n  constructs a `Styles` struct from `quickStyleOpts` — a palette of\n  design tokens (primary, secondary, fgBase, bgBase, success, error, etc.).\n  `quickStyle` must be fully token-driven: never hardcode specific\n  `charmtone.*` colors here (except Chroma syntax highlighting, which is\n  pending tokenization). This lets any theme reuse the base without\n  inheriting Charmtone-specific colors.\n- **`themes.go`**: Defines concrete themes. Each theme function (e.g.\n  `CharmtonePantera`) calls `quickStyle` with its palette, then applies\n  theme-specific overrides as needed.\n- **`styles.go`**: Defines the `Styles` struct and its documentation —\n  the shape of what `quickStyle` produces.\n\n**Adding theme-specific overrides**: When a style genuinely needs a\ncolor that doesn't fit the token model (e.g. the bang prompt uses\nSalt/Hazy/Larple), keep `quickStyle` on the closest semantic token and\noverride only the differing colors in the theme function:\n\n```go\nfunc CharmtonePantera() Styles {\n\ts := quickStyle(quickStyleOpts{ /* palette */ })\n\n\t// Override only the colors that differ from the token defaults.\n\ts.Editor.PromptBangIconFocused = s.Editor.PromptBangIconFocused.\n\t\tForeground(charmtone.Salt).\n\t\tBackground(charmtone.Hazy)\n\n\treturn s\n}\n```\n\n**Adding a new theme**: Add a function in `themes.go` that returns the\nresult of `quickStyle` with a `quickStyleOpts` palette (plus any needed\noverrides), then wire it into `ThemeForProvider`.\n"}}