Skip to content

Commit aa77a64

Browse files
committed
docs(claude): rewrite for the TypeScript codebase
CLAUDE.md still described the Go-era implementation (BubbleTea, gofmt conventions, a 7-phase migration plan that has since shipped). Replace with the current TS reality on pi-mono, real env vars used in src/, and pointers into the .settings/ folder for deeper context. Trim 50 lines in the process — the deep content belongs in .settings/, not here.
1 parent dd6a446 commit aa77a64

1 file changed

Lines changed: 156 additions & 189 deletions

File tree

CLAUDE.md

Lines changed: 156 additions & 189 deletions
Original file line numberDiff line numberDiff line change
@@ -1,232 +1,199 @@
1-
# CLAUDE.md — Codebase CLI
1+
# CLAUDE.md — codebase-cli
22

3-
## What This Is
3+
## What this is
44

5-
An AI coding agent CLI written in Go. Goal: build a **better Claude Code** — simpler
6-
architecture, any LLM provider, single binary, zero runtime dependencies.
5+
A TypeScript coding-agent CLI on top of the pi-mono runtime
6+
(`@earendil-works/pi-agent-core` + `@earendil-works/pi-ai`). We layer the
7+
TUI, tools, slash commands, permissions, OAuth, sessions, and the headless
8+
/ JSON-RPC entry points; pi-mono owns the agent loop and provider
9+
protocols.
710

8-
This is NOT a Claude Code clone. We study CC's source as a benchmark for feature coverage
9-
and learn from their architectural mistakes. Every feature we add should be a better
10-
implementation, not a copy-paste of their approach.
11+
Goal: a **better Claude Code** — simpler architecture, any LLM provider,
12+
single `npm i -g` install. Not a clone. We study CC's source as a feature-coverage
13+
benchmark and to learn from their architectural mistakes; we do not copy
14+
their code or prompt wording verbatim (open-source IP discipline).
1115

12-
## Quick Reference
16+
For deep context, read [`.settings/`](.settings/) first — `tenets.md`,
17+
`architecture.md`, `extending.md`, `testing.md`. Those are the canonical
18+
docs; this file is the quick reference.
19+
20+
## Quick reference
1321

1422
```
15-
Language: Go 1.24
16-
TUI: BubbleTea (Charm)
17-
Styling: Lipgloss
18-
Highlighting: Chroma
23+
Language: TypeScript (ESM), Node ≥ 20
24+
TUI: ink (React for terminals)
25+
Schema: typebox (runtime validation of tool args)
26+
Tests: vitest 2.x
27+
Lint/format: biome 2.x
28+
Build: tsc → dist/, no bundler
1929
Binary name: codebase
2030
Config dir: ~/.codebase/
21-
Sessions: ~/.codebase/sessions/
31+
Sessions: ~/.codebase/sessions/<cwd-hash>/
2232
```
2333

24-
## Build & Run
34+
## Build & run
2535

2636
```sh
27-
go build -o codebase .
28-
./codebase # run in current dir
29-
./codebase -dir /some/path # run in specific dir
30-
./codebase -model claude-sonnet-4-20250514 # override model
31-
./codebase -resume # resume previous session
32-
CODEBASE_NOBOOT=1 ./codebase # skip boot animation
37+
npm run build # tsc + chmod
38+
npm run check # typecheck + lint + tests (the pre-publish gate)
39+
node dist/cli.js # interactive TUI in current dir
40+
node dist/cli.js --new # skip auto-resume of prior session
41+
node dist/cli.js run "<prompt>" # one-shot headless
42+
node dist/cli.js run --output json "<prompt>"
43+
node dist/cli.js auth login # OAuth via codebase.foundation
44+
node dist/cli.js app-server # JSON-RPC over stdio (for IDE extensions)
3345
```
3446

35-
## Test
47+
## Test & bench
3648

3749
```sh
38-
go test ./... # all tests
39-
go test -run TestToolReadFile # specific test
40-
go test -v -count=1 ./... # verbose, no cache
50+
npm test # all unit tests (576+)
51+
npm test -- -t "name fragment" # filter
52+
npm run test:watch
53+
npm run bench:micro # vitest microbenchmarks (*.bench.ts)
54+
npm run bench # end-to-end LLM bench (needs API key)
4155
```
4256

43-
## Architecture Overview
44-
45-
See `docs/ARCHITECTURE.md` for the full blueprint and migration plan.
46-
47-
### Current Structure (pre-refactor)
57+
## Architecture in 60 seconds
4858

4959
```
50-
main.go Entry point, config loading, flag parsing
51-
app.go BubbleTea root model — routes between screens (boot/setup/chat)
52-
chat.go Main chat TUI (1,603 LOC — god object, being refactored)
53-
agent.go Agent loop — goroutine that drives LLM ↔ tool cycle
54-
tools.go Tool definitions + execution dispatcher (switch statement)
55-
git_tools.go Git-specific tools (status, diff, log, commit, branch)
56-
llm.go OpenAI-compatible LLM client + SSE streaming
57-
llm_anthropic.go Anthropic Messages API adapter + streaming
58-
permission.go Permission system (ask/trust-tool/trust-all)
59-
compact.go Conversation compaction via LLM summarization
60-
session.go Session persistence to ~/.codebase/sessions/
61-
commands.go Slash commands (/help, /clear, /compact, /status, etc.)
62-
glue.go Sidecar LLM for intent routing, narration, titles
63-
plan.go Planning mode (Q&A before building)
64-
render.go Viewport rendering helpers, tool block formatters
65-
search.go Web search (Tavily, Brave, SearXNG, DuckDuckGo)
66-
boot.go Demoscene boot animation (plasma, 3D cube, pixel font)
67-
audio.go Chiptune synthesizer (4-channel MOD tracker, pure Go)
68-
setup.go First-run setup wizard
69-
diagnostics.go Post-edit language checkers (Go, TypeScript, Python)
70-
notify.go Notification manager (status bar messages)
71-
highlight.go Syntax highlighting via Chroma
72-
theme.go Color themes (dark, light, retro)
73-
dotenv.go .env file loader
60+
User input → ChatApp (src/ui/App.tsx)
61+
→ /cmd → CommandRegistry (src/commands/)
62+
→ !cmd → runShellEscape (src/ui/shell-escape.ts)
63+
→ @path → collectAttachments → augmented prompt
64+
→ router (src/agent/router.ts) → chat | plan | agent
65+
→ bundle.agent.prompt(text) # pi-agent-core owns the loop
66+
→ AgentEvents stream over bundle.subscribe(...)
67+
→ useCoalescedAgentEvents flushes at 16ms (60fps cap)
68+
→ reducer (src/agent/events.ts) → ChatState → React render
7469
```
7570

76-
### Data Flow
77-
78-
```
79-
User Input → chatModel.Update()
80-
→ slash command? → commands.go handler
81-
→ agent message? → agent.go goroutine
82-
→ LLM streaming via llm.go / llm_anthropic.go
83-
→ tool calls dispatched via tools.go
84-
→ permission checks via permission.go
85-
→ results sent back as AgentEvent on channel
86-
→ chatModel renders events via render.go
87-
```
71+
Full src layout, data flow, and component responsibilities live in
72+
[`.settings/architecture.md`](.settings/architecture.md).
8873

89-
### Key Patterns
74+
## Environment variables
9075

91-
- **Channel-based concurrency**: Agent runs in goroutine, sends events on `eventCh`,
92-
TUI receives them in `Update()`. No shared mutable state.
93-
- **Segment-based rendering**: Conversation is `[]segment` (text/user/tool/divider/error).
94-
Avoids fragile ANSI string manipulation.
95-
- **Debounced viewport**: Streaming rebuilds throttled to 50ms to prevent TUI thrashing.
96-
- **Read-only parallel execution**: Tools declaring `EffectReadsFS` run concurrently.
97-
- **Atomic writes**: Multi-edit uses write→rename for crash safety.
76+
### Provider credentials
77+
Provided per-provider via pi-ai's resolver. The first-run wizard
78+
detects what's set and offers it. Common ones:
9879

99-
## Environment Variables
100-
101-
### Required (one of)
10280
```
103-
OPENAI_API_KEY OpenAI or compatible provider API key
104-
ANTHROPIC_API_KEY Anthropic API key (auto-detects protocol)
81+
OPENAI_API_KEY OpenAI (also catches any OAI-compatible endpoint)
82+
OPENAI_BASE_URL override endpoint for OAI-compatible providers
83+
ANTHROPIC_API_KEY Anthropic
84+
GROQ_API_KEY Groq
85+
OPENROUTER_API_KEY OpenRouter
10586
```
10687

107-
### Optional — LLM
108-
```
109-
OPENAI_BASE_URL Custom endpoint (Groq, Ollama, OpenRouter, etc.)
110-
OPENAI_MODEL Override default model
111-
```
88+
OAuth users sign in via `codebase auth login`; tokens land in
89+
`~/.codebase/credentials.json` and `process.env` is not required.
11290

113-
### Optional — Glue sidecar
91+
### Web search (only if `web_search` tool is invoked)
11492
```
115-
GLUE_API_KEY Separate key for cheap/fast model
116-
GLUE_BASE_URL Separate endpoint for glue
117-
GLUE_FAST_MODEL Fast model for intent/titles (default: same as main)
118-
GLUE_SMART_MODEL Smart model for planning/narration
93+
TAVILY_API_KEY Tavily (recommended)
94+
BRAVE_API_KEY Brave Search
95+
SEARXNG_URL self-hosted SearXNG instance
11996
```
12097

121-
### Optional — Web Search
98+
### Behavior toggles
12299
```
123-
TAVILY_API_KEY Tavily search (recommended)
124-
BRAVE_API_KEY Brave Search
125-
SEARXNG_URL Self-hosted SearXNG instance
126-
SEARCH_API_KEY Alias for TAVILY_API_KEY
100+
CODEBASE_FRESH=1 skip auto-resume of prior session (same as --new)
101+
CODEBASE_NO_SUGGESTIONS=1 disable ghost-text prompt suggestions
102+
CODEBASE_DEBUG=1 verbose stderr logging
103+
CODEBASE_DEBUG_INPUT=1 log every keystroke to ~/.codebase/logs/input.log
104+
NO_HYPERLINK=1 disable OSC 8 clickable file paths
127105
```
128106

129-
### Optional — Behavior
107+
### OAuth (only override if you know why)
130108
```
131-
CODEBASE_NOBOOT Skip boot animation
132-
CODEBASE_NOSOUND Skip boot audio
109+
CODEBASE_CLIENT_ID override OAuth client id
110+
CODEBASE_SCOPES override requested scopes
133111
```
134112

135-
## Coding Conventions
113+
`.env` and `.env.local` in the cwd are auto-loaded at startup
114+
(`src/dotenv/loader.ts`).
136115

137-
### Go style
138-
- Standard `gofmt` formatting, no exceptions
139-
- Error wrapping with context: `fmt.Errorf("toolReadFile: %w", err)`
140-
- Unexported by default. Only export what's needed cross-package (currently single package)
141-
- Table-driven tests with `t.Run()` subtests
142-
- Channel direction on parameters: `ch chan<- AgentEvent`, `stop <-chan struct{}`
116+
## Coding conventions
143117

144-
### File organization
145-
- One responsibility per file (aspiration — chat.go violates this, fix in progress)
146-
- Tests in `*_test.go` alongside source
147-
- Constants and types at top of file, functions below
148-
- Section headers with `// ──────────` dividers
118+
### TS style
119+
- ESM imports with `.js` extensions (TS NodeNext convention).
120+
- `import type { ... }` for type-only — biome enforces this.
121+
- Tabs for indentation (biome config). Single quotes off (double quotes).
122+
- Errors thrown as `Error` subclasses with descriptive names
123+
(`ConfigError`, `PermissionDeniedError`, `UserQueryCancelled`, etc.).
124+
- Default to no comments. Add one only when *why* is non-obvious.
125+
- No multi-paragraph JSDoc blocks. One short line max.
149126

150-
### Naming
151-
- Tool functions: `toolReadFile()`, `toolWriteFile()`, etc.
152-
- Event types: `EventTextDelta`, `EventToolStart`, etc.
153-
- States: `chatIdle`, `chatStreaming`, `chatPermission`, etc.
154-
- Messages (BubbleTea): `agentEventMsg`, `flashTickMsg`, etc.
127+
### Files
128+
- One responsibility per file. If a file passes ~400 lines, look for
129+
a seam — see how `Message.tsx` and `App.tsx` were split.
130+
- Tests colocated as `*.test.ts` / `*.test.tsx` next to source.
131+
- Benchmarks colocated as `*.bench.ts` next to source.
132+
- Component files use PascalCase (`Message.tsx`); modules use kebab-case
133+
(`tool-call-line.tsx`, `diff-summary.tsx`).
155134

156135
### What to avoid
157-
- Don't use `panic()` except for true programmer errors
158-
- Don't use `init()` except for command registration (commands.go)
159-
- Don't add dependencies without justification — stdlib first
160-
- Don't add features without updating `docs/ARCHITECTURE.md`
161-
162-
## Refactoring in Progress
163-
164-
The codebase is undergoing a major architectural refactoring. See `docs/ARCHITECTURE.md`
165-
sections 7-12 for the migration plan. The phases are:
166-
167-
1. **Tool Interface** — Replace switch dispatch with `Tool` interface + `Registry`
168-
2. **Permission Engine** — Effect-based policies instead of tool-name checks
169-
3. **LLM Provider Interface** — Adapter pattern instead of protocol branching
170-
4. **Agent State Machine** — Explicit FSM instead of implicit state in chatModel
171-
5. **Token Budget Manager** — Proactive budget instead of heuristic compaction
172-
6. **MCP Support** — External tool servers via mcp-go
173-
7. **Slash Commands** — Command interface with auto-discovery
174-
175-
When working on this codebase, check which phase is current and follow that plan.
176-
177-
## Competitive Context: Claude Code Benchmark
178-
179-
We have Claude Code's source code for reference. Key things to know:
180-
181-
### What CC does well (learn from)
182-
- Tools as self-contained modules with schema validation
183-
- Streaming tool executor with concurrency control
184-
- Slash command system (50+ commands)
185-
- MCP integration for extensibility
186-
- Git worktree isolation for parallel agents
187-
- Persistent cross-session memory
188-
189-
### What CC got wrong (do better)
190-
- **React/Ink for TUI**: 512K LOC for a terminal app. BubbleTea is right-sized.
191-
- **No clean tool interface**: BashTool is 160K bytes. Each tool is a snowflake.
192-
We use a `Tool` interface — adding a tool is mechanical.
193-
- **Permission monster**: 300K+ lines of AST parsing, ML classifiers, hooks cascade.
194-
We use effect-based policies — tools declare effects, policies match on effects.
195-
- **God object state**: 30+ field AppState. We use an explicit state machine (FSM).
196-
- **Three compaction strategies**: They don't coordinate. We use one strategy,
197-
proactive, with a calibrated token budget.
198-
- **Retrofitted MCP**: 119K lines because tool system wasn't designed for it.
199-
Our `Tool` interface treats local and MCP tools identically.
200-
- **Bun lock-in**: No real benefit for a CLI. We ship a single Go binary.
201-
202-
### Design philosophy difference
203-
CC optimizes for Anthropic-only with maximum features. We optimize for any-LLM-provider
204-
with maximum simplicity. Our thesis: 80% of CC's value in 5% of the code, by making
205-
better foundational choices.
206-
207-
When adding features, always ask: "How did CC do this, and what's a simpler way that
208-
avoids their pain points?" Check `docs/ARCHITECTURE.md` for the specific analysis.
209-
210-
## Project Files Recognized
211-
212-
The CLI reads these files from the project root for context:
213-
- `AGENTS.md` — agent instructions (OpenAI Codex convention)
214-
- `CLAUDE.md` — project instructions (Claude Code convention)
215-
- `CODEX.md` — project instructions (Codex convention)
216-
- `.cursorrules` — project instructions (Cursor convention)
217-
218-
All are injected into the system prompt at session start.
219-
220-
## Dependencies (keep minimal)
221-
222-
Direct dependencies only:
223-
- `charmbracelet/bubbletea` — TUI framework
224-
- `charmbracelet/bubbles` — TUI components (spinner, textinput, viewport)
225-
- `charmbracelet/lipgloss` — styling
226-
- `alecthomas/chroma/v2` — syntax highlighting
227-
- `lucasb-eyer/go-colorful` — color space conversions (boot animation)
228-
229-
Planned additions:
230-
- `mark3labs/mcp-go` — MCP client (Phase 6)
231-
232-
Do NOT add dependencies for things the stdlib can do (HTTP, JSON, crypto, etc.).
136+
- Don't `as unknown as` to smuggle a field through an interface. Fix the
137+
interface.
138+
- Don't add a feature flag for backwards-compat of code you're rewriting
139+
in the same PR. Just change it.
140+
- Don't add a runtime dependency for something stdlib + the existing deps
141+
can do.
142+
- Don't reach into pi-agent-core / pi-ai internals. Public API only; if
143+
it's not enough, fix it upstream.
144+
- Don't commit aspirational documentation. The code is the spec; docs
145+
follow the code.
146+
147+
## Competitive context: Claude Code
148+
149+
We have CC's source as a benchmark. The tenets we hold differently:
150+
151+
| Aspect | CC | codebase-cli |
152+
|---|---|---|
153+
| TUI framework | ink + 512K LOC | ink + ~22K LOC |
154+
| Tool interface | Each tool a snowflake; BashTool ~160K bytes | One `Tool` interface, ~45 implementations |
155+
| Permissions | AST parsers + ML classifiers + hook cascade | Effect-based policies, ~600 LOC |
156+
| State | 30+ field god object | Single immutable `ChatState`, reducer-driven |
157+
| Compaction | Three uncoordinated strategies | One proactive strategy with calibrated budget |
158+
| Distribution | Bun lock-in, custom bundler | Plain `npm i -g`, `tsc` only |
159+
| Providers | Anthropic-first | Any provider via pi-ai |
160+
161+
The Ink-LOC contrast is the headline number: ink is fine; CC's *use* of
162+
it is the problem. Our thesis: 80% of CC's value in 5% of the code, by
163+
making better foundational choices.
164+
165+
When you add a feature, ask: "how did CC do this, and what's a simpler
166+
way that avoids their pain points?"
167+
168+
## Recognized project files
169+
170+
The CLI auto-loads these from the project root and injects them into
171+
the system prompt:
172+
173+
- `AGENTS.md` (OpenAI Codex)
174+
- `CLAUDE.md` (Claude Code / this convention)
175+
- `CODEX.md`
176+
- `.cursorrules` (Cursor)
177+
178+
## Direct dependencies
179+
180+
Runtime:
181+
- `@earendil-works/pi-agent-core`, `@earendil-works/pi-ai` — agent loop + protocol adapters
182+
- `ink` + `react` — TUI
183+
- `typebox` — runtime schema validation for tool args
184+
- `diff` — LCS-paired diffs for the `edit_file` / `multi_edit` summary
185+
- `glob`, `ignore` — file matching for tools
186+
187+
Dev:
188+
- `vitest`, `@biomejs/biome`, `typescript`, `tsx`, `shx`, `@types/*`
189+
190+
Don't add a dep without a real second use case in mind. The stdlib +
191+
the deps above can do almost everything.
192+
193+
## In-flight features
194+
195+
- **MCP**: there's a `/mcp` placeholder slash command but real MCP
196+
client support hasn't shipped. The pi-mono roadmap calls it Phase 9.
197+
- **Hooks**: scaffold under `src/hooks/`; user-configurable pre/post-turn
198+
and pre/post-tool hooks. Check current state before relying on them.
199+
- **Skills**: scaffold under `src/skills/` for per-skill SYSTEM.md additions.

0 commit comments

Comments
 (0)