This file describes the hawk project for AI agents working in this codebase.
The TUI /memory command references this file.
hawk is an AI-powered coding agent for the terminal. It reads codebases, writes and edits files, runs tests, and manages git — all through natural language. Built in Go with zero CGO dependencies, it ships as a single static binary for linux/darwin/windows on amd64/arm64.
Tagline: AI coding agent for your terminal — built for developers, not teams or enterprises.
hawk is part of the hawk-eco mono-ecosystem:
| Component | Purpose |
|---|---|
| hawk | AI coding agent (this repo) |
| eyrie | LLM provider runtime — routing, streaming, retries, caching |
| yaad | Graph-based persistent memory for coding agents |
| tok | Tokenizer, compression, secrets scanning, rate limiting |
| sight | Diff-based code review and static analysis |
| inspect | Security audit library (CVE, API security, CI output) |
| trace | Session capture and replay CLI |
Modules are pinned in go.mod. External checkouts live under external/ with a
go.work file for local development.
hawk/
├── cmd/ # CLI entry point (Cobra + Bubble Tea TUI)
├── internal/
│ ├── engine/ # Agent loop, compaction, context management
│ │ ├── ctxmgr/ # Context providers, packing, visualization
│ │ ├── token/ # Budget allocation, prediction
│ │ ├── streaming/ # Response cache, stream optimizer, thinking
│ │ ├── session/ # Compression, cross-session learning
│ │ ├── memory/ # Knowledge distillation
│ │ ├── planning/ # Goals, task decomposition
│ │ ├── workflow/ # JSON-defined automation pipelines
│ │ ├── review/ # Code review bot, quality scorer
│ │ ├── observability/ # Profiler, debug recorder
│ │ ├── validation/ # Lint loop, test loop
│ │ └── ...
│ ├── tool/ # 40+ built-in tools (file edit, git, codegen, etc.)
│ ├── config/ # Settings, env manager, migration
│ ├── session/ # SQLite persistence, search, export, replay
│ ├── permissions/ # Guardian, rules DSL, boundary checker
│ ├── sandbox/ # Seatbelt, landlock, net proxy
│ ├── intelligence/ # Repo map, AST analysis, dependency graphs
│ ├── multiagent/ # Personas, inter-agent messaging, sub-agents
│ ├── hooks/ # Event-driven plugin system
│ ├── mcp/ # Model Context Protocol client/server
│ ├── daemon/ # Background HTTP/SSE server
│ ├── resilience/ # Circuit breaker, rate limiting, health checks
│ └── feature/ # Eval, fingerprint, scaffolding
├── shared/types/ # Cross-repo exported types (severity, etc.)
├── docs/ # Architecture docs, research notes
└── testdata/ # Test fixtures
- Zero CGO: Pure Go, cross-compilable. Tree-sitter is optional.
internal/is private: Other repos importshared/types/only.- Tool safety layer: Every tool call goes through permissions (guardian, rules DSL, boundary checker) before execution.
- Engine-first: The agent loop in
internal/engine/orchestrates context packing, tool dispatch, streaming, and session persistence. - Ecosystem integration: eyrie handles all LLM API communication. hawk never talks to LLM APIs directly.
go build ./cmd/hawk # Build binary
go test -race ./... # Run all tests with race detector
make ci # Full CI suite (lint, test, security)
make cover # Coverage report
make path # Developer path verification
make smoke # Build + quick verification- Standard Go project layout:
cmd/for entry points,internal/for private - Tests live alongside source files (
foo.go→foo_test.go) - Use table-driven tests where practical
- Errors are values — wrap with
fmt.Errorf("context: %w", err) - No global mutable state; prefer dependency injection
Use Conventional Commits:
feat: add new tool
fix: handle edge case in file edit
docs: update AGENTS.md
refactor: extract context packing logic
test: add coverage for guardian
- Signed commits are required in this repo.
- Git is configured for SSH signing with
commit.gpgsign=trueand the user's SSH signing key. - In sandboxed agent sessions,
git commitmay fail even when the key is unlocked because the sandbox cannot accessSSH_AUTH_SOCK. - When that happens, run
git commitoutside the sandbox or with an unsandboxed/escalated execution path so git can talk to the host SSH agent.
gofmtandgo vetare mandatory (enforced by CI)- Keep functions focused; extract helpers for clarity
- Prefer explicit error handling over panics
- Comments on exported types/functions only (per Go convention)
- Create
internal/tool/mytool.go - Implement the tool interface (name, description, parameters, execute)
- Register in the tool registry
- Add tests in
mytool_test.go - The tool automatically gets permission checking via the safety layer
- Place code in the appropriate
internal/package - Follow existing patterns (e.g., context providers are pluggable)
- Add tests and update documentation
CONTRIBUTING.md— PR process, commit conventionsdocs/— Architecture details, security model, ecosystem message flowexternal/— Ecosystem repo checkouts forgo.workdevelopmentshared/types/— Types exported for sight/inspect/tok (they must not importinternal/)
- Unit tests for all new code
- Integration tests for tool execution and engine loop
- Race detector enabled in CI (
-race) - No test files committed with
t.Skip()without a tracking issue
- Do not import
internal/from other ecosystem repos — useshared/types/ - Do not put API keys in
.envor shell env for hawk — use/config(OS keychain) - The
external/directory is for local dev only; CI clones repos separately go.workandgo.work.sumare committed — CI'smodule hygienejob runsgo work syncand asserts the result is in sync with the repo. Both files point at./external/*checkouts; the.github/actions/checkout-eyrieaction populates./external/on CI runners before the build runs.
- Tool types:
FooToolstruct implementing theToolinterface (Name(),Description(),Parameters(),Execute()) - Config types:
Settings,MCPServerConfig,CustomProviderConfig— no prefix, inconfigpackage - Engine types:
Session,CoreLoop,SafetyLayer,Intelligence,Optimizer— inenginepackage - Health checks:
Checkerfunc type,Checkstruct withName,Status,Message - Resilience:
Breaker(circuit breaker),Config+Do(retry),Limiter(rate limit) - Error types:
ValidationErrorwithField,Message,Value;ValidationResultwithErrors,Valid - Bridges:
Ready() boolmethod,NewBridge()constructor, graceful degradation when unavailable
- Tool registration:
tool.NewRegistry(tools...)→registry.Get("ToolName")→tool.Execute(ctx, input) - Settings loading:
config.LoadSettings()merges global + project;config.LoadGlobalSettings()for global-only - Session construction:
engine.NewSessionWithClient(client, provider, model, systemPrompt, registry, deploymentRouting) - Service composition:
engine.NewSessionServices(opts...)withWithProvider(),WithTools(),WithMemory(), etc. - Health checks:
health.NewRegistry()→registry.Register("name", checker)→registry.Run(ctx)→registry.Status() - Circuit breaker:
circuit.New(cfg)→breaker.Call(fn)orbreaker.Allow()→breaker.State() - Retry:
retry.Do(ctx, cfg, fn)with exponential backoff + jitter;retry.DoWithResult[T]for typed returns - Config validation:
config.ValidateSettings(s)returnsValidationResult{Errors, Valid} - Ecosystem panel:
config.FormatEcosystemPanel(ctx, provider, model)for diagnostics
- Table-driven tests with
t.Run(name, func(t *testing.T){...})for all multi-case tests t.Parallel()on all tests that don't share mutable statet.TempDir()for filesystem isolation (auto-cleanup)credentials.MapStore{}for credential isolation in tests:store := &credentials.MapStore{} credentials.SetDefaultStore(store) t.Cleanup(func() { credentials.SetDefaultStore(nil) })
bytes.Bufferasio.Writerfor logger output capture- Fuzz tests for input parsing robustness:
func FuzzFoo(f *testing.F) { ... } - No mocks framework — use concrete types and test doubles
- Meta-audit tests in
internal/testaudit/enforce architectural invariants via go/ast
- Safe to refactor:
internal/resilience/(retry, circuit, ratelimit, health) — no public API - Safe to refactor:
internal/observability/logger/— internal only, no external consumers - Safe to refactor:
internal/system/(bus, shutdown, retention, cron, staleness) - Caution:
internal/engine/session.goSession struct — widely referenced across 30+ sub-packages - Caution:
internal/config/settings.goSettings struct — serialized to JSON, dual-format (snake_case + camelCase) - Caution:
internal/tool/Tool interface — implemented by 40+ tools - Blocked:
shared/types/— exported to eyrie, sight, inspect, tok; changes break ecosystem
| What | Where |
|---|---|
| CLI entry point | cmd/root.go |
| Agent loop | internal/engine/session.go (Stream(), agentLoop()) |
| Session services | internal/engine/session_services.go |
| Tool interface | internal/tool/tool.go (Tool, Registry) |
| Tool context | internal/tool/tool.go (ToolContext, WithToolContext) |
| Settings | internal/config/settings.go (Settings, LoadSettings()) |
| Config validation | internal/config/validator.go (ValidateSettings()) |
| Config migration | internal/config/migrate.go (MigrationRegistry) |
| Env manager | internal/config/envmanager.go (EnvManager) |
| Health checks | internal/resilience/health/health.go (Registry, Checker) |
| Circuit breaker | internal/resilience/circuit.go (Breaker, Manager) |
| Retry | internal/resilience/retry/retry.go (Do(), DoWithResult()) |
| Rate limiter | internal/resilience/ratelimit/ratelimit.go (Limiter) |
| Logger | internal/observability/logger/logger.go (Logger) |
| Metrics | internal/observability/metrics/metrics.go (Counter, Gauge, Timer) |
| OTEL tracing | internal/observability/oteltrace/trace.go |
| Multi-agent missions | internal/multiagent/mission.go (Mission) |
| Message bus | internal/multiagent/messaging.go (MessageBus) |
| Shared memory | internal/multiagent/shared_memory.go (SharedMemory) |
| Session persistence | internal/session/persist.go |
| MCP client | internal/mcp/mcp.go |
| MCP server | internal/mcp/server.go |
| Provider routing | internal/provider/routing/router.go |
| Bridges | internal/bridge/{inspect,sight,sessioncapture}/bridge.go |
| Doctor diagnostics | cmd/diagnostics.go |
| Meta-audit tests | internal/testaudit/audit_test.go |
- No
os.Getenvininternal/— useenv.Getenv(ininternal/env/) for simple reads, orconfig.Getenvif the package can importinternal/configwithout cycles.config.EnvManageris for profile/secret management. Exceptions:internal/observability/oteltrace/for telemetry env vars; runtime environment probes (e.g.TMUX,STY,TERM_PROGRAM,SHELL,GOPATH) which are set by the OS/terminal and not by config. - No
panic()for error handling — returnerrorvalues. Exception:init()functions for package-level assertions. - No
fmt.Printfor logging — uselogger.Loggerwith structured fields. Exception:internal/onboarding/andinternal/engine/scaffold/for user-facing CLI output. - No API keys in settings.json — use OS secret store via
credentialspackage and/configcommand. - No importing
internal/from other ecosystem repos — useshared/types/for cross-repo types. - No global mutable state — prefer dependency injection via
depsstructs orcontext.WithValue. - No
t.Skip()without a tracking issue — every skipped test needs a GitHub issue number.