co-mono/packages/coding-agent/DEVELOPMENT.md

223 lines
9 KiB
Markdown

# coding-agent Development Guide
This document describes the architecture and development workflow for the coding-agent package.
## Architecture Overview
The coding-agent is structured into distinct layers:
```
┌─────────────────────────────────────────────────────────────┐
│ CLI Layer │
│ cli.ts → main.ts → cli/args.ts, cli/file-processor.ts │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Mode Layer │
│ modes/interactive/ modes/print-mode.ts modes/rpc-mode.ts │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Core Layer │
│ core/agent-session.ts (central abstraction) │
│ core/session-manager.ts, core/model-config.ts, etc. │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ External Dependencies │
│ @mariozechner/pi-agent-core (Agent, tools) │
│ @mariozechner/pi-ai (models, providers) │
│ @mariozechner/pi-tui (TUI components) │
└─────────────────────────────────────────────────────────────┘
```
## Directory Structure
```
src/
├── cli.ts # CLI entry point (shebang, calls main)
├── main.ts # Main orchestration, argument handling, mode routing
├── index.ts # Public API exports
├── cli/ # CLI-specific utilities
│ ├── args.ts # parseArgs(), printHelp(), Args interface
│ ├── file-processor.ts # processFileArguments() for @file args
│ └── session-picker.ts # selectSession() TUI for --resume
├── core/ # Core business logic (mode-agnostic)
│ ├── agent-session.ts # AgentSession class - THE central abstraction
│ ├── bash-executor.ts # executeBash() with streaming, abort
│ ├── compaction.ts # Context compaction logic
│ ├── export-html.ts # exportSession(), exportFromFile()
│ ├── messages.ts # BashExecutionMessage, messageTransformer
│ ├── model-config.ts # findModel(), getAvailableModels(), getApiKeyForModel()
│ ├── model-resolver.ts # resolveModelScope(), restoreModelFromSession()
│ ├── session-manager.ts # SessionManager class - JSONL persistence
│ ├── settings-manager.ts # SettingsManager class - user preferences
│ ├── slash-commands.ts # loadSlashCommands() from ~/.pi/agent/commands/
│ ├── system-prompt.ts # buildSystemPrompt(), loadProjectContextFiles()
│ ├── oauth/ # OAuth authentication (Anthropic, etc.)
│ │ ├── anthropic.ts
│ │ ├── storage.ts
│ │ └── index.ts
│ └── tools/ # Tool implementations
│ ├── bash.ts, edit.ts, find.ts, grep.ts, ls.ts, read.ts, write.ts
│ ├── truncate.ts # Output truncation utilities
│ └── index.ts # Tool exports, allTools, codingTools
├── modes/ # Run mode implementations
│ ├── index.ts # Re-exports InteractiveMode, runPrintMode, runRpcMode
│ ├── print-mode.ts # Non-interactive: process messages, print output, exit
│ ├── rpc-mode.ts # JSON-RPC mode for programmatic control
│ └── interactive/ # Interactive TUI mode
│ ├── interactive-mode.ts # InteractiveMode class
│ ├── components/ # TUI components (editor, selectors, etc.)
│ │ ├── assistant-message.ts
│ │ ├── bash-execution.ts
│ │ ├── custom-editor.ts
│ │ ├── footer.ts
│ │ ├── model-selector.ts
│ │ ├── session-selector.ts
│ │ └── ... (other selectors)
│ └── theme/
│ ├── theme.ts # Theme loading, getEditorTheme(), etc.
│ ├── dark.json
│ ├── light.json
│ └── theme-schema.json
└── utils/ # Generic utilities
├── changelog.ts # parseChangelog(), getNewEntries()
├── clipboard.ts # copyToClipboard()
├── config.ts # APP_NAME, VERSION, paths (getAgentDir, etc.)
├── fuzzy.ts # Fuzzy string matching
├── shell.ts # getShellConfig()
└── tools-manager.ts # ensureTool() - download fd, etc.
```
## Key Abstractions
### AgentSession (core/agent-session.ts)
The central abstraction that wraps the low-level `Agent` with:
- Session persistence (via SessionManager)
- Settings persistence (via SettingsManager)
- Model cycling with scoped models
- Context compaction
- Bash command execution
- Message queuing
All three modes (interactive, print, rpc) use AgentSession.
### InteractiveMode (modes/interactive/interactive-mode.ts)
Handles TUI rendering and user interaction:
- Subscribes to AgentSession events
- Renders messages, tool executions, streaming
- Manages editor, selectors, key handlers
- Delegates all business logic to AgentSession
### SessionManager (core/session-manager.ts)
Handles session persistence:
- JSONL format for append-only writes
- Session file location management
- Message loading/saving
- Model/thinking level persistence
### SettingsManager (core/settings-manager.ts)
Handles user preferences:
- Default model/provider
- Theme selection
- Queue mode
- Thinking block visibility
## Development Workflow
### Running in Development
```bash
# From monorepo root
npx tsx packages/coding-agent/src/cli.ts
# With arguments
npx tsx packages/coding-agent/src/cli.ts --help
npx tsx packages/coding-agent/src/cli.ts -p "Hello"
```
### Type Checking
```bash
# From monorepo root
npm run check
```
### Building
```bash
# Build all packages
npm run build
# Build standalone binary
cd packages/coding-agent
npm run build:binary
```
## Adding New Features
### Adding a New Slash Command
1. If it's a UI-only command (e.g., `/theme`), add handler in `interactive-mode.ts` `setupEditorSubmitHandler()`
2. If it needs session logic, add method to `AgentSession` and call from mode
### Adding a New Tool
1. Create tool in `core/tools/` following existing patterns
2. Export from `core/tools/index.ts`
3. Add to `allTools` and optionally `codingTools`
4. Add description to `toolDescriptions` in `core/system-prompt.ts`
### Adding a New Selector
1. Create component in `modes/interactive/components/`
2. Use `showSelector()` helper in `interactive-mode.ts`:
```typescript
private showMySelector(): void {
this.showSelector((done) => {
const selector = new MySelectorComponent(
// ... params
(result) => {
// Handle selection
done();
this.showStatus(`Selected: ${result}`);
},
() => {
done();
this.ui.requestRender();
},
);
return { component: selector, focus: selector.getSelectList() };
});
}
```
## Testing
The package uses E2E tests only (no unit tests by design). Tests are in `test/`:
```bash
# Run tests
npm test
```
## Code Style
- No `any` types unless absolutely necessary
- No inline dynamic imports
- Use `showStatus()` for dim status messages
- Use `showError()` / `showWarning()` for errors/warnings
- Keep InteractiveMode focused on UI, delegate logic to AgentSession