Rumilo Agent Guide
CLI that dispatches specialized AI research subagents for web research and git repository exploration.
Commands
bun run dev # Run CLI via bun (development)
bun run build # Build to dist/
bun run typecheck # TypeScript check (also: bun run lint)
No test suite currently exists (test/ is empty).
Architecture
CLI entry (src/cli/index.ts)
├── web command → runWebCommand() → runAgent() with web tools
└── repo command → runRepoCommand() → runAgent() with git tools
Control Flow
- CLI parses args with custom parser (no library), dispatches to command handlers
- Command handlers (
src/cli/commands/) load config, create workspace, configure tools, invokerunAgent() - Agent runner (
src/agent/runner.ts) wraps@mariozechner/pi-agent- createsAgent, subscribes to events, prompts, extracts final message - Tools are created via factory functions that close over workspace path for sandboxing
- Workspace (
src/workspace/manager.ts) is a temp directory (cleaned up unless--no-cleanup)
Two Agent Modes
| Mode | Tools | External Services |
|---|---|---|
web |
web_search, web_fetch, read, grep, ls, find |
Kagi (search), Tabstack (fetch→markdown) |
repo |
read, grep, ls, find, git_log, git_show, git_blame, git_diff, git_refs, git_checkout |
None (clones repo to workspace) |
Key Patterns
Tool Factory Pattern
All tools follow the same signature:
const createFooTool = (workspacePath: string): AgentTool => ({ ... })
Tools use @sinclair/typebox for parameter schemas. Execute functions return { content: [{type: "text", text}], details?: {...} }.
Workspace Sandboxing
Tools must constrain paths to workspace:
ensureWorkspacePath()insrc/agent/tools/index.tsvalidates paths don't escaperesolveToCwd()/resolveReadPath()insrc/agent/tools/path-utils.tshandle expansion and normalization
Config Cascade
defaultConfig → XDG_CONFIG_HOME/rumilo/config.toml → CLI flags
Config uses TOML, validated against TypeBox schema (src/config/schema.ts).
Model Resolution
Model strings use provider:model format. custom:name prefix looks up custom model definitions from config's [custom_models] section. Built-in providers delegate to @mariozechner/pi-ai.
Error Handling
Custom error classes in src/util/errors.ts extend RumiloError with error codes:
ConfigError,FetchError,CloneError,WorkspaceError,ToolInputError
Output Truncation
src/util/truncate.ts handles large content:
DEFAULT_MAX_LINES = 2000DEFAULT_MAX_BYTES = 50KBGREP_MAX_LINE_LENGTH = 500chars
Gotchas
- Grep requires ripgrep -
createGrepToolchecks forrgat tool creation time and throws if missing - Web tools require credentials -
KAGI_SESSION_TOKENandTABSTACK_API_KEYvia env or config - Shallow clones by default - repo mode uses
--depth 1 --filter=blob:limit=5munless--full - Pre-fetch injection threshold - web command with
-u URLinjects content directly if ≤50KB, otherwise stores in workspace - No
.jsextension in imports - source uses.jsextensions for ESM compatibility even though files are.ts
Adding a New Tool
- Create
src/agent/tools/newtool.tsfollowing the factory pattern - Export from
src/agent/tools/index.tsif general-purpose - Add to appropriate command's tool array in
src/cli/commands/{web,repo}.ts - Use
ToolInputErrorfor validation failures