# Gnoma — Project Instructions ## What is this? Provider-agnostic agentic coding assistant in Go 1.26. Named after the northern pygmy-owl (Glaucidium gnoma). Agents are called "elfs" (elf owl). ## Module & repo layout - Module: `somegit.dev/Owlibou/gnoma` - Upstream (primary, accepts PRs): - GitHub mirror (read-only): PRs go to the upstream Gitea instance, not GitHub. The GitHub side is a push mirror — direct pushes to `main`/`dev` there will be rejected by the ruleset. ## Big picture (read this before diving in) Single static Go binary. Request flow: 1. `cmd/gnoma` parses flags, picks TUI vs pipe mode, builds the session. 2. `internal/session` owns one chat lifecycle; `internal/engine` runs the agentic loop (stream → tool calls → re-query → until done). 3. `internal/router` picks the arm per prompt: multi-armed bandit over provider adapters in `internal/provider/{anthropic,openai,google,mistral,openaicompat}`, tiered SLM (`internal/slm`) → CLI-agent subprocess → local → cloud, with `Strengths` + `MaxComplexity` + `CostWeight` shaping selection. 4. `internal/security` is the safety boundary: SafeProvider wrapping, firewall (network egress), secret scanner, redaction, incognito mode. `internal/safety` is separate — it's the pre-launch CWD classifier. 5. `internal/tool` is the local-action boundary; `internal/permission` gates every tool call. 6. Extensibility surfaces: `internal/hook`, `internal/skill`, `internal/mcp` (JSON-RPC over stdio), `internal/plugin` (TOFU-pinned). Discriminated unions (struct + type discriminant) are the project's chosen way to model variants — see `internal/message` and `internal/stream`. Don't reach for interfaces when a discriminant fits. Full essentials (vision, domain model, ADRs, process flows): `docs/essentials/INDEX.md`. **Read INDEX.md before changing architectural boundaries or adding new packages.** Note: INDEX predates `internal/safety` and `internal/slm` — cross-check the actual tree. ## Build & Test ```sh make build # ./bin/gnoma make test # unit tests make test-integration # //go:build integration — needs real API keys make lint # golangci-lint run ./... make check # fmt + vet + lint + test — canonical pre-commit gate make cover # coverage.html # Run a single test / package go test -run TestRouterSelect ./internal/router/ go test -v ./internal/router/ # Benchmarks go test -bench=. ./internal/router/ ``` ## Conventions ### Go Style - Go 1.26 idioms: `new(expr)`, `errors.AsType[E]`, `sync.WaitGroup.Go` - Structured logging with `log/slog` - Discriminated unions via struct + type discriminant (not interfaces) - Pull-based stream iterators: `Next() / Current() / Err() / Close()` - `json.RawMessage` for tool schemas and arguments (zero-cost passthrough) - Functional options for complex configuration - `errgroup` for parallel work ### Testing - TDD: write tests first - Table-driven tests - Build tag `//go:build integration` for tests hitting real APIs - `testing/synctest` for concurrent tests - `t.TempDir()` for file system tests ### Naming - Packages: short, lowercase, no underscores - Interfaces: describe behavior, not implementation - Errors: `Err` prefix for sentinel errors, `*Error` suffix for error types ### Commits - Conventional commits (feat:, fix:, refactor:, test:, docs:, chore:) - No co-signing ### Providers - Mistral: `github.com/VikingOwl91/mistral-go-sdk` (user's own SDK) - Anthropic: `github.com/anthropics/anthropic-sdk-go` - OpenAI: `github.com/openai/openai-go` - Google: `google.golang.org/genai` - Ollama/llama.cpp: via OpenAI SDK with custom base URL