Architecture

An overview of cast's source layout and key design decisions. For contributors and the curious.

Source Layout

src/
  core/               Agent logic (no UI dependency)
    loop.ts           Agent loop — streaming, tool dispatch, compaction
    tools.ts          17 tool definitions + executors
    tools/
      bash.ts         Shell execution
      files.ts        Read/write/edit
      search.ts       Find/grep/ls
      web.ts          web_search (DuckDuckGo), web_fetch (Jina Reader)
      task.ts         Sub-agent delegation
      shared.ts       Shared types (ToolResult, ConfirmBash)
    llm.ts            LLM interaction, streaming, retry, prompt caching
    session.ts        Session persistence, token estimation, compaction
    mcp.ts            MCP server connection (stdio + streamable HTTP)
    personas.ts       Persona loading (project > global > builtin)
    rules.ts          Cursor-compatible rule system (always/auto/lazy/manual)
    skills.ts         Agent Skills spec implementation
    plugins.ts        Marketplace catalogs + plugin install (skills)
    config.ts         AppConfig, model validation, onboarding
    context-files.ts  AGENTS.md / CLAUDE.md discovery
    project.ts        System prompt assembly, trust gating
    startup.ts        Unified startup orchestration
    runner.ts         Queue management (steering, follow-ups)
    run.ts            Non-interactive runner (cast run)
    vendors.ts        Reasoning metadata, think-block parsing
    upgrade.ts        Self-update via GitHub releases
    permissions.ts    Dangerous bash command detection
    plan.ts           Plan mode state, file I/O, read-only bash gate
    frontmatter.ts    Minimal YAML frontmatter parser
    prompts.ts        Prompt file loading
    readline.ts       Readline utilities, models cache
    settings.ts       User settings persistence
    stdin-manager.ts  Stdin ownership between Ink and child processes
    subagents.ts      Sub-agent prompt loading
    help.ts           Banner, help text, changelog
    ...
  ui/                 Ink TUI components
    App.tsx           Top-level layout
    Composer.tsx      Input with autocomplete, image paste
    ChatLog.tsx       Message rendering
    commands.ts       Slash command handlers
    themes/           Color theme registry and definitions
    input/            Keybindings, input handling
    ...
  pickers/            Onboarding pickers (model, persona, reasoning)
  index.ts            CLI entry point

Key Design Decisions

Single OpenAI-Compatible Provider

cast speaks one API: the OpenAI chat completions format. Any provider that implements this API works — OpenRouter, OpenAI, Ollama, vLLM, LiteLLM, Azure OpenAI. No provider-specific code paths.

Parallel Tool Execution

Tool calls within one assistant message run concurrently via Promise.all. If the model requests bash, read, and grep in a single response, all three execute simultaneously.

Hashline LRU Cache

read, edit, and grep share a process-local LRU (20 entries, ~4 MB worst case) keyed by absolute path. On hit, the file's mtime is re-stated and a mismatch drops the entry; on miss the file is read once and per-line sha1s are computed in a single pass. write and edit invalidate the entry on success, so a follow-up read never sees a stale snapshot. This is invisible to the model — it just means a read → edit round trip (and a re-grep over the same files) skips both the file I/O and the per-line hashing.

Context Compaction

When the conversation exceeds ~75% of the context window, older messages are summarized by the LLM. The split is ~60/40 (old/recent), snapped to turn boundaries so tool calls and results stay together. File paths are extracted deterministically from tool calls and appended to the summary.

MCP Integration

MCP tools are namespaced as mcp_<server>_<tool> and converted to the same Tool/ToolResult shapes the built-in tools use. The rest of the codebase doesn't need to know MCP tools are different.

Trust Gating

A single trust decision per project gates all local resources (skills, MCP, context files, personas). Global resources (~/.cast/) always load. Asked once, remembered in settings.json.

Plan Mode

Plan mode is a restricted agent state: read-only bash (a curated allowlist of inspection binaries), unrestricted read, and write/edit narrowed to the session's plans directory — the model authors the plan with the same tools it edits real code with, no dedicated plan-write/plan-edit/plan-read tool (see Plan Mode). Plan files persist as markdown with checkbox tracking.

Development

npm install --ignore-scripts
npm start               # Run from source (tsx)
npm run check           # Type check + lint (tsc + biome)
npm test                # Unit tests (vitest)
npm run build           # Bundle into dist/index.js (esbuild)
npm run format          # Auto-format (biome)

Testing

Build

The bundle step (esbuild) produces a single dist/index.js file — self-contained, no node_modules needed at runtime.