Shared project rules for coding agents. Keep this file in sync with AGENTS.md, except the Agent Delegation section at the end.
# Build
make build # builds dist/tinycode
# Run
./dist/tinycode # TUI mode
./dist/tinycode <directory> # TUI against a different directory
./dist/tinycode serve # headless API proxy
./dist/tinycode acp # Agent Client Protocol (IDE integration, stdio)
# Tests
go test ./... -count=1 # all tests (or: make test)
go test ./internal/tui/... -count=1 # single package
# Lint
make lint # go vet + staticcheck (make test-race for -race)
# Embed web app into binary (requires packages/app built first)
make embed-webappUse conventional commit-style messages and PR titles: type(scope): summary.
Valid types are feat, fix, docs, chore, refactor, and test. Scopes: tui, server, llm, session, provider, config, tool, plugin, acp, permission, mcp, vcs, bus, storage, agent, skill, command, project.
- Follow standard Go conventions (
gofmt,go vet) - Prefer early returns over
elseblocks - Keep functions focused and small; extract helpers when they name a real concept
- Use
slogfor structured logging (already used throughout) - Error handling: return errors up the stack with
fmt.Errorf("context: %w", err). Don't swallow errors silently. - Prefer value receivers for bubbletea
Update/Viewmethods (required by thetea.Modelinterface) - Use pointer receivers for methods that mutate state (e.g.
resize(),setFocus()) - Test files live next to source:
foo.go/foo_test.go
- Use table-driven tests where appropriate
- Test names:
TestFunctionName_ScenarioDescription - Run from repo root:
go test ./...or target a package:go test ./internal/tui/...
Things that break silently if you guess wrong.
earlyinitimport:cmd/tinycode/main.gomust keep the blank import_ "github.com/bobbyjohnstx/tinycode/internal/earlyinit", and that import must appear before any TUI package import. It sets lipgloss dark-background defaults before TUI packages initialize. Removing it, or importing a TUI package ahead of it, breaks all TUI colors with no error. It currently follows the standard-library imports. It is not_ "internal/earlyinit".- Plugin JSON-RPC field is
"args", not"arguments":pkg/plugin/protocol.gousesjson:"args". Sending"arguments"silently zero-values the struct — the tool runs with empty params. defaultAutoContinueMax = 0is intentional: The agent does not auto-continue by default. Setting this to a positive number enables unsupervised agent loops that burn tokens and run tools without consent.SetWorking(true)return value must be propagated:StatusBar.SetWorking()returns atea.Cmdthat drives the spinner tick chain. Discarding it freezes the spinner — the UI appears hung.- Config file 3-name fallback: Config loading tries
tinycode.jsonc→tinycode.json→config.jsonin each directory. Only checking one name silently ignores user config. internal/static/dist/is committed, not gitignored: The//go:embed dist/*directive ininternal/static/static.gorequires these files at compile time. Deleting them as build artifacts breaksgo build.LSPConfigaccepts both forms:"lsp": true(boolean shorthand) and"lsp": { "enabled": true, ... }(struct). Always expecting an object breaks boolean-shorthand users.- SQLite is pure Go (
modernc.org/sqlite), not cgo: No C toolchain needed. SettingCGO_ENABLED=1or switching tomattn/go-sqlite3breaks cross-compilation. - Project config walks UP the directory tree: Config files in nested directories merge with parent configs (innermost wins). Only checking the project root ignores monorepo nested configs.
- Plugin system is JSON-RPC over stdin/stdout: Plugins are standalone Go binaries spawned as child processes, not HTTP services.
pkg/plugin/is the public SDK. packages/is the web UI build: Not the Go runtime.make embed-webappbuildspackages/app(withsdk,ui,plugin, andtinycode).packages/tinycodeis six helpers undersrc/core/utilthat the web UI imports. The old TypeScript CLI, server, session, and PTY code is gone, along withpackages/desktop,packages/vscode-extension,packages/llm,packages/http-recorder,packages/effect-drizzle-sqlite, andpackages/script.safego.Go()must wrap all goroutine launches: Barego func()skips panic recovery — a panic in the goroutine crashes the process with no log.internal/safegoaddsrecover()+slog.Error.- Plugin tool timeout is 30s, hook timeout is 5s:
internal/plugin/manager.goconstants. Plugins that exceed these are killed. Changing them affects all plugins and shutdown timing. config.sharedefaults to"disabled": Not a working share/publish feature in Go — no/session/{id}/shareroutes; web PTY is unsupported (PTY_SUPPORTED=false). Do not advertise share or the in-browser terminal as working.- Manual summarize works:
POST /session/{id}/summarizeand TUI/compactrunProcessor.Compactand return200+{"compacted": bool}— not501. - First-run is doctor +
/connect: Usetinycode doctor(diagnose) and TUI/connect(configure models). There is notinycode setup. Legacy/tc-doctorbash skill is obsolete. - Skills paths are plural:
skills/. Project agents live at.tinycode/agent/*.md.
Use specialized agents instead of doing everything inline:
debugger— Finding and diagnosing bugs. Root-cause analysis, race conditions, stack traces.executor— Writing and editing code. Implementation work, refactors, applying fixes.architect— Designing solutions. Architecture decisions, API design, system-level trade-offs.