Imported from ta3113ta/coding-agent (
AGENTS.md). Install upstream withnpx skills add ta3113ta/coding-agent. Copyright stays with the author.
Coding Agent Architecture
This project uses a minimal core + compile-time plugins design. The core defines contracts and the agent loop; all implementations live under plugins/.
Documentation
- AGENTS.md — keep under 200 lines (high-level index; link out for detail)
- Every other
*.md— keep under 300 lines - Over budget → split into a new file or prune redundant content; do not compress prose to fit
- Cursor rule:
.cursor/rules/documentation-limits.mdc
Core (do not add implementations here)
| Package | Purpose |
|---|---|
agent/ |
Agent loop only — LLM + tools until done |
types/ |
Neutral types: Message, ToolDefinition, CompleteRequest/Response |
llm/provider.go |
Provider interface + provider registry |
tools/tool.go |
Tool interface + Registry dispatch |
config/ |
Env/flag configuration |
session/ |
Session types + Store interface |
permission/ |
Permission hook contract + chain |
compaction/ |
Context compaction contract |
spawn/ |
Sub-agent spawning contract |
plan/ |
Plan mode + todo tracking types and session state |
retry/ |
LLM retry policy (Do, transient errors, backoff) |
plugin/ |
Plugin interfaces + Bootstrap() |
Rule: If it talks to an external API, runs shell commands, or defines a persona — it is a plugin, not core.
Plugins (add new features here)
plugins/
├── builtin/builtin.go # single registration list
├── tools/ # one package per tool
├── providers/ # one package per LLM provider
├── prompt/ # system prompt contributors
├── skills/ # skill discovery + index injection
└── runner/ # REPL, one-shot, HTTP, etc.
Register every new plugin in plugins/builtin/builtin.go.
How to add a new tool
- Create
plugins/tools/mytool/my_tool.go - Implement
tools.Tool(Name,Definition,Execute) - Add a
Pluginstruct withRegister()that callsplugin.RegisterTools() - Append
mytool.Plugin{}tobuiltin.Default
File editing tools
| Tool | Use when |
|---|---|
read_file |
Read/explore files (with line numbers) |
grep / glob |
Search the codebase (ripgrep; requires rg on PATH) |
str_replace |
Edit existing files (primary) |
write_file |
Create new files, or fallback |
See rationale and alternatives in ADR-0001, ADR-0009
Skills
At bootstrap the agent discovers SKILL.md from project (.cursor/skills/), personal (~/.cursor/skills/), and bundled (plugins/skills/builtin/) sources, then injects a skill index into the system prompt — the agent loads full content with read_file when a task is relevant.
See rationale and alternatives in ADR-0002
Streaming
The REPL runner streams assistant text tokens via an optional OnStream callback on CompleteRequest — see ADR-0003
Prompt caching
The provider applies top-level automatic cache_control when CompleteRequest.PromptCache.Enabled — see ADR-0004
Session management
Conversation history is persisted as JSON via the session.Store contract and filestore plugin — auto-save after each turn, display name, ephemeral mode (--no-session), startup flags -c/-r, resume via CLI or REPL slash commands — see ADR-0005
Permission hooks
Before registry.Dispatch the agent calls permission.Chain — script hooks from .coding-agent/hooks.json (preToolUse) followed by an interactive REPL prompt for risky tools — see ADR-0006
Context compaction
Before provider.Complete the agent calls compaction.Compactor — auto-summarize when projected context exceeds contextWindow - reserveTokens, or manual /compact [instructions] — see ADR-0007
Sub-agent spawning
The parent agent calls the task tool to spawn a sub-agent synchronously — the sub-agent uses an in-memory temporary session, a tool set per profile, and does not touch the parent archive — see ADR-0008
Plan mode + todo tracking
Plan mode restricts tools to read-only research; create_plan saves a draft for /approve (switches to agent mode; optional trailing text runs implementation). /plan [task] enters plan mode or plans in one shot. todo_write tracks in-session tasks persisted in session JSON — see ADR-0010
Parallel tool execution
When an assistant turn has multiple tool calls, permission preflight runs sequentially, then allowed tools dispatch concurrently; archive messages preserve original call order — see ADR-0011
Error recovery / retry policy
Before giving up on a turn, the agent retries transient provider.Complete failures (429/5xx/network/empty response) with exponential backoff; SDK retries are disabled so one policy applies — see ADR-0012
Architecture Decision Records
New features that affect architecture (tool contract, agent loop, bootstrap flow, discovery model, etc.) must have an ADR in docs/adr/ before implementation.
Format: NNNN-short-title.md — see ADR-0001
Template:
- Status / Date
- Context — problem to solve
- Decision — what we chose to do
- Alternatives Considered — other options and why we did not choose them
- Consequences — pros and cons
How to add a new LLM provider
- Create
plugins/providers/myprovider/myprovider.go - Implement
llm.Provider(Complete) - Add
PluginwithRegister()callingplugin.RegisterProvider() - Add provider name constants to
config/config.go - Append to
builtin.Default
How to add a prompt plugin
- Create
plugins/prompt/myname/prompt.go - Call
plugin.AppendPrompt(app, "...")inRegister() - Append to
builtin.Default
Multiple prompt plugins are concatenated in registration order.
How to add a runner plugin
- Create
plugins/runner/myname/runner.go - Implement
plugin.Runner(Run(ctx, plugin.AgentHandle)) - Set
app.RunnerinRegister() - Append to
builtin.Default(only one runner should win — last one registered wins)
Bootstrap flow
main.go
→ plugin.LoadConfigFromEnv()
→ plugin.Bootstrap(cfg, builtin.Default...)
1. Each plugin Register(app)
2. Tools collected into Registry
3. Providers registered in llm registry
4. Prompts concatenated
5. llm.NewProvider(cfg) resolves active provider
→ agent.New(provider, tools, model, prompt, cache, verbose, sessionStore, providerName, app.Permission, app.Compactor, app.PlanState, cfg.PlanEnabled, cfg.ParallelToolsEnabled, cfg.RetryPolicy())
→ app.Runner.Run(ctx, agent)
What we defer
- Runtime
.soplugins init()auto-registration (explicitbuiltin.Defaultlist is easier to debug)- Additional hook events (
postToolUse,beforeShellExecution, MCP) — see ADR-0006 v2
Checklist before adding code
- Is this a contract (interface, types, loop)? → core package
- Is this an implementation? →
plugins/ - Did you register it in
plugins/builtin/builtin.go? - Did you avoid importing
agentfromplugin? (useplugin.AgentHandleinstead) - New feature with architectural impact → write an ADR in
docs/adr/and link from AGENTS.md or README.md