|
| 1 | +# AGENTS.md — Hawk Coding Agent |
| 2 | + |
| 3 | +This file describes the hawk project for AI agents working in this codebase. |
| 4 | +The TUI `/memory` command references this file. |
| 5 | + |
| 6 | +--- |
| 7 | + |
| 8 | +## Project Overview |
| 9 | + |
| 10 | +hawk is an AI-powered coding agent for the terminal. It reads codebases, writes |
| 11 | +and edits files, runs tests, and manages git — all through natural language. |
| 12 | +Built in Go with zero CGO dependencies, it ships as a single static binary for |
| 13 | +linux/darwin/windows on amd64/arm64. |
| 14 | + |
| 15 | +**Tagline:** AI coding agent for your terminal — built for developers, not teams |
| 16 | +or enterprises. |
| 17 | + |
| 18 | +## Ecosystem |
| 19 | + |
| 20 | +hawk is part of the hawk-eco mono-ecosystem: |
| 21 | + |
| 22 | +| Component | Purpose | |
| 23 | +|-----------|---------| |
| 24 | +| **hawk** | AI coding agent (this repo) | |
| 25 | +| **eyrie** | LLM provider runtime — routing, streaming, retries, caching | |
| 26 | +| **yaad** | Graph-based persistent memory for coding agents | |
| 27 | +| **tok** | Tokenizer, compression, secrets scanning, rate limiting | |
| 28 | +| **sight** | Diff-based code review and static analysis | |
| 29 | +| **inspect** | Security audit library (CVE, API security, CI output) | |
| 30 | +| **trace** | Session capture and replay CLI | |
| 31 | + |
| 32 | +Modules are pinned in `go.mod`. External checkouts live under `external/` with a |
| 33 | +`go.work` file for local development. |
| 34 | + |
| 35 | +## Architecture |
| 36 | + |
| 37 | +``` |
| 38 | +hawk/ |
| 39 | +├── cmd/ # CLI entry point (Cobra + Bubble Tea TUI) |
| 40 | +├── internal/ |
| 41 | +│ ├── engine/ # Agent loop, compaction, context management |
| 42 | +│ │ ├── ctxmgr/ # Context providers, packing, visualization |
| 43 | +│ │ ├── token/ # Budget allocation, prediction |
| 44 | +│ │ ├── streaming/ # Response cache, stream optimizer, thinking |
| 45 | +│ │ ├── session/ # Compression, cross-session learning |
| 46 | +│ │ ├── memory/ # Knowledge distillation |
| 47 | +│ │ ├── planning/ # Goals, task decomposition |
| 48 | +│ │ ├── workflow/ # JSON-defined automation pipelines |
| 49 | +│ │ ├── review/ # Code review bot, quality scorer |
| 50 | +│ │ ├── observability/ # Profiler, debug recorder |
| 51 | +│ │ ├── validation/ # Lint loop, test loop |
| 52 | +│ │ └── ... |
| 53 | +│ ├── tool/ # 40+ built-in tools (file edit, git, codegen, etc.) |
| 54 | +│ ├── config/ # Settings, env manager, migration |
| 55 | +│ ├── session/ # SQLite persistence, search, export, replay |
| 56 | +│ ├── permissions/ # Guardian, rules DSL, boundary checker |
| 57 | +│ ├── sandbox/ # Seatbelt, landlock, net proxy |
| 58 | +│ ├── intelligence/ # Repo map, AST analysis, dependency graphs |
| 59 | +│ ├── multiagent/ # Personas, inter-agent messaging, sub-agents |
| 60 | +│ ├── hooks/ # Event-driven plugin system |
| 61 | +│ ├── mcp/ # Model Context Protocol client/server |
| 62 | +│ ├── daemon/ # Background HTTP/SSE server |
| 63 | +│ ├── resilience/ # Circuit breaker, rate limiting, health checks |
| 64 | +│ └── feature/ # Eval, fingerprint, scaffolding |
| 65 | +├── shared/types/ # Cross-repo exported types (severity, etc.) |
| 66 | +├── docs/ # Architecture docs, research notes |
| 67 | +└── testdata/ # Test fixtures |
| 68 | +``` |
| 69 | + |
| 70 | +## Key Design Decisions |
| 71 | + |
| 72 | +- **Zero CGO:** Pure Go, cross-compilable. Tree-sitter is optional. |
| 73 | +- **`internal/` is private:** Other repos import `shared/types/` only. |
| 74 | +- **Tool safety layer:** Every tool call goes through permissions (guardian, |
| 75 | + rules DSL, boundary checker) before execution. |
| 76 | +- **Engine-first:** The agent loop in `internal/engine/` orchestrates context |
| 77 | + packing, tool dispatch, streaming, and session persistence. |
| 78 | +- **Ecosystem integration:** eyrie handles all LLM API communication. hawk |
| 79 | + never talks to LLM APIs directly. |
| 80 | + |
| 81 | +## Development Guidelines |
| 82 | + |
| 83 | +### Build & Test |
| 84 | + |
| 85 | +```bash |
| 86 | +go build . # Build binary |
| 87 | +go test -race ./... # Run all tests with race detector |
| 88 | +make ci # Full CI suite (lint, test, security) |
| 89 | +make cover # Coverage report |
| 90 | +make path # Developer path verification |
| 91 | +make smoke # Build + quick verification |
| 92 | +``` |
| 93 | + |
| 94 | +### Go Conventions |
| 95 | + |
| 96 | +- Standard Go project layout: `cmd/` for entry points, `internal/` for private |
| 97 | +- Tests live alongside source files (`foo.go` → `foo_test.go`) |
| 98 | +- Use table-driven tests where practical |
| 99 | +- Errors are values — wrap with `fmt.Errorf("context: %w", err)` |
| 100 | +- No global mutable state; prefer dependency injection |
| 101 | + |
| 102 | +### Commit Conventions |
| 103 | + |
| 104 | +Use [Conventional Commits](https://www.conventionalcommits.org/): |
| 105 | +``` |
| 106 | +feat: add new tool |
| 107 | +fix: handle edge case in file edit |
| 108 | +docs: update AGENTS.md |
| 109 | +refactor: extract context packing logic |
| 110 | +test: add coverage for guardian |
| 111 | +``` |
| 112 | + |
| 113 | +### Code Style |
| 114 | + |
| 115 | +- `gofmt` and `go vet` are mandatory (enforced by CI) |
| 116 | +- Keep functions focused; extract helpers for clarity |
| 117 | +- Prefer explicit error handling over panics |
| 118 | +- Comments on exported types/functions only (per Go convention) |
| 119 | + |
| 120 | +### Adding a New Tool |
| 121 | + |
| 122 | +1. Create `internal/tool/mytool.go` |
| 123 | +2. Implement the tool interface (name, description, parameters, execute) |
| 124 | +3. Register in the tool registry |
| 125 | +4. Add tests in `mytool_test.go` |
| 126 | +5. The tool automatically gets permission checking via the safety layer |
| 127 | + |
| 128 | +### Adding a New Feature |
| 129 | + |
| 130 | +1. Check FEATURES.md for the feature list and conventions |
| 131 | +2. Place code in the appropriate `internal/` package |
| 132 | +3. Follow existing patterns (e.g., context providers are pluggable) |
| 133 | +4. Add tests and update documentation |
| 134 | + |
| 135 | +## File Organization Notes |
| 136 | + |
| 137 | +- `FEATURES.md` — Complete feature reference with 100 features across 12 categories |
| 138 | +- `CONTRIBUTING.md` — PR process, commit conventions |
| 139 | +- `docs/` — Architecture details, security model, ecosystem message flow |
| 140 | +- `external/` — Ecosystem repo checkouts for `go.work` development |
| 141 | +- `shared/types/` — Types exported for sight/inspect/tok (they must not import `internal/`) |
| 142 | + |
| 143 | +## Testing Philosophy |
| 144 | + |
| 145 | +- Unit tests for all new code |
| 146 | +- Integration tests for tool execution and engine loop |
| 147 | +- Race detector enabled in CI (`-race`) |
| 148 | +- No test files committed with `t.Skip()` without a tracking issue |
| 149 | + |
| 150 | +## Common Pitfalls |
| 151 | + |
| 152 | +- Do not import `internal/` from other ecosystem repos — use `shared/types/` |
| 153 | +- Do not put API keys in `.env` or shell env for hawk — use `/config` (OS keychain) |
| 154 | +- The `external/` directory is for local dev only; CI clones repos separately |
| 155 | +- `go.work` is for local multi-repo development; it is not committed |
| 156 | + |
| 157 | +## Naming Conventions |
| 158 | + |
| 159 | +- **Tool types**: `FooTool` struct implementing the `Tool` interface (`Name()`, `Description()`, `Parameters()`, `Execute()`) |
| 160 | +- **Config types**: `Settings`, `MCPServerConfig`, `CustomProviderConfig` — no prefix, in `config` package |
| 161 | +- **Engine types**: `Session`, `CoreLoop`, `SafetyLayer`, `Intelligence`, `Optimizer` — in `engine` package |
| 162 | +- **Health checks**: `Checker` func type, `Check` struct with `Name`, `Status`, `Message` |
| 163 | +- **Resilience**: `Breaker` (circuit breaker), `Config` + `Do` (retry), `Limiter` (rate limit) |
| 164 | +- **Error types**: `ValidationError` with `Field`, `Message`, `Value`; `ValidationResult` with `Errors`, `Valid` |
| 165 | +- **Bridges**: `Ready() bool` method, `NewBridge()` constructor, graceful degradation when unavailable |
| 166 | + |
| 167 | +## API Patterns |
| 168 | + |
| 169 | +- **Tool registration**: `tool.NewRegistry(tools...)` → `registry.Get("ToolName")` → `tool.Execute(ctx, input)` |
| 170 | +- **Settings loading**: `config.LoadSettings()` merges global + project; `config.LoadGlobalSettings()` for global-only |
| 171 | +- **Session construction**: `engine.NewSessionWithClient(client, provider, model, systemPrompt, registry, deploymentRouting)` |
| 172 | +- **Service composition**: `engine.NewSessionServices(opts...)` with `WithProvider()`, `WithTools()`, `WithMemory()`, etc. |
| 173 | +- **Health checks**: `health.NewRegistry()` → `registry.Register("name", checker)` → `registry.Run(ctx)` → `registry.Status()` |
| 174 | +- **Circuit breaker**: `circuit.New(cfg)` → `breaker.Call(fn)` or `breaker.Allow()` → `breaker.State()` |
| 175 | +- **Retry**: `retry.Do(ctx, cfg, fn)` with exponential backoff + jitter; `retry.DoWithResult[T]` for typed returns |
| 176 | +- **Config validation**: `config.ValidateSettings(s)` returns `ValidationResult{Errors, Valid}` |
| 177 | +- **Ecosystem panel**: `config.FormatEcosystemPanel(ctx, provider, model)` for diagnostics |
| 178 | + |
| 179 | +## Testing Patterns |
| 180 | + |
| 181 | +- **Table-driven tests** with `t.Run(name, func(t *testing.T){...})` for all multi-case tests |
| 182 | +- **`t.Parallel()`** on all tests that don't share mutable state |
| 183 | +- **`t.TempDir()`** for filesystem isolation (auto-cleanup) |
| 184 | +- **`credentials.MapStore{}`** for credential isolation in tests: |
| 185 | + ```go |
| 186 | + store := &credentials.MapStore{} |
| 187 | + credentials.SetDefaultStore(store) |
| 188 | + t.Cleanup(func() { credentials.SetDefaultStore(nil) }) |
| 189 | + ``` |
| 190 | +- **`bytes.Buffer`** as `io.Writer` for logger output capture |
| 191 | +- **Fuzz tests** for input parsing robustness: `func FuzzFoo(f *testing.F) { ... }` |
| 192 | +- **No mocks framework** — use concrete types and test doubles |
| 193 | +- **Meta-audit tests** in `internal/testaudit/` enforce architectural invariants via go/ast |
| 194 | + |
| 195 | +## Refactoring Guidelines |
| 196 | + |
| 197 | +- **Safe to refactor**: `internal/resilience/` (retry, circuit, ratelimit, health) — no public API |
| 198 | +- **Safe to refactor**: `internal/observability/logger/` — internal only, no external consumers |
| 199 | +- **Safe to refactor**: `internal/system/` (bus, shutdown, retention, cron, staleness) |
| 200 | +- **Caution**: `internal/engine/session.go` Session struct — widely referenced across 30+ sub-packages |
| 201 | +- **Caution**: `internal/config/settings.go` Settings struct — serialized to JSON, dual-format (snake_case + camelCase) |
| 202 | +- **Caution**: `internal/tool/` Tool interface — implemented by 40+ tools |
| 203 | +- **Blocked**: `shared/types/` — exported to eyrie, sight, inspect, tok; changes break ecosystem |
| 204 | + |
| 205 | +## Key File Locations |
| 206 | + |
| 207 | +| What | Where | |
| 208 | +|---|---| |
| 209 | +| CLI entry point | `cmd/root.go` | |
| 210 | +| Agent loop | `internal/engine/session.go` (`Stream()`, `agentLoop()`) | |
| 211 | +| Session services | `internal/engine/session_services.go` | |
| 212 | +| Tool interface | `internal/tool/tool.go` (`Tool`, `Registry`) | |
| 213 | +| Tool context | `internal/tool/tool.go` (`ToolContext`, `WithToolContext`) | |
| 214 | +| Settings | `internal/config/settings.go` (`Settings`, `LoadSettings()`) | |
| 215 | +| Config validation | `internal/config/validator.go` (`ValidateSettings()`) | |
| 216 | +| Config migration | `internal/config/migrate.go` (`MigrationRegistry`) | |
| 217 | +| Env manager | `internal/config/envmanager.go` (`EnvManager`) | |
| 218 | +| Health checks | `internal/resilience/health/health.go` (`Registry`, `Checker`) | |
| 219 | +| Circuit breaker | `internal/resilience/circuit.go` (`Breaker`, `Manager`) | |
| 220 | +| Retry | `internal/resilience/retry/retry.go` (`Do()`, `DoWithResult()`) | |
| 221 | +| Rate limiter | `internal/resilience/ratelimit/ratelimit.go` (`Limiter`) | |
| 222 | +| Logger | `internal/observability/logger/logger.go` (`Logger`) | |
| 223 | +| Metrics | `internal/observability/metrics/metrics.go` (`Counter`, `Gauge`, `Timer`) | |
| 224 | +| OTEL tracing | `internal/observability/oteltrace/trace.go` | |
| 225 | +| Multi-agent missions | `internal/multiagent/mission.go` (`Mission`) | |
| 226 | +| Message bus | `internal/multiagent/messaging.go` (`MessageBus`) | |
| 227 | +| Shared memory | `internal/multiagent/shared_memory.go` (`SharedMemory`) | |
| 228 | +| Session persistence | `internal/session/persist.go` | |
| 229 | +| MCP client | `internal/mcp/mcp.go` | |
| 230 | +| MCP server | `internal/mcp/server.go` | |
| 231 | +| Provider routing | `internal/provider/routing/router.go` | |
| 232 | +| Bridges | `internal/bridge/{inspect,sight,sessioncapture}/bridge.go` | |
| 233 | +| Doctor diagnostics | `cmd/diagnostics.go` | |
| 234 | +| Meta-audit tests | `internal/testaudit/audit_test.go` | |
| 235 | + |
| 236 | +## Anti-Patterns |
| 237 | + |
| 238 | +- **No `os.Getenv` in `internal/`** — use `config.EnvManager` to centralize env access. Exception: `internal/observability/oteltrace/` for telemetry env vars. |
| 239 | +- **No `panic()` for error handling** — return `error` values. Exception: `init()` functions for package-level assertions. |
| 240 | +- **No `fmt.Print` for logging** — use `logger.Logger` with structured fields. Exception: `internal/onboarding/` and `internal/engine/scaffold/` for user-facing CLI output. |
| 241 | +- **No API keys in settings.json** — use OS secret store via `credentials` package and `/config` command. |
| 242 | +- **No importing `internal/` from other ecosystem repos** — use `shared/types/` for cross-repo types. |
| 243 | +- **No global mutable state** — prefer dependency injection via `deps` structs or `context.WithValue`. |
| 244 | +- **No `t.Skip()` without a tracking issue** — every skipped test needs a GitHub issue number. |
0 commit comments