rowboat/CLAUDE.md
Ramnique Singh f39daa9f5d refactor(x)!: models.json v2 — providers carry credentials, models live in assistantModel/taskModels
The v1 config fossilized three schema generations: a top-level
provider/model pair (single-provider era), a providers map duplicating
credentials AND caching model lists (pre-dynamic-listing era), plus
defaultSelection and flat category overrides (hybrid era) — while several
effective models existed only as hidden curated branches in code.

v2 is the intended shape:
  { version: 2,
    providers: { <instance-id>: { flavor, apiKey?, baseURL?, … } },
    assistantModel: { provider, model },
    taskModels: { knowledgeGraph?, meetingNotes?, liveNoteAgent?,
                  autoPermissionDecision?, chatTitle? },
    deferBackgroundTasks? }

- one-time boot migration (core/models/migrate.ts, invoked from
  ensureConfig): evaluates the v1 resolution rules — including the curated
  signed-in defaults — one last time and writes the answers explicitly, so
  the simplified resolvers produce identical effective models; task
  overrides are written only where the old effective model differs from
  inherit-from-assistant; curated ids survive solely as frozen literals in
  the migration
- defaults.ts: getDefaultModelAndProvider reads assistantModel, period;
  getCategoryModel/getChatTitleModel are "override else assistant"; all
  SIGNED_IN_* constants and the repo's gpt-5.4 bootstrap deleted
- rowboat sign-in = connecting a provider: with no saved assistant, the
  initial model is picked via selectInitialModel (backend recommendation
  if the gateway lists it, else first listed) and saved; sign-out clears
  rowboat-referencing selections (same dangling-ref cleanup as removing
  any provider). A saved assistant is never replaced — signing in no
  longer hijacks a BYOK selection
- IPC: models:saveConfig replaced by models:setProvider/removeProvider;
  models:updateConfig speaks v2 keys; models:test takes {provider, model}
- renderer call sites (settings dialog, onboarding, composer) translated;
  the settings dialog's delete-provider path collapses from ~100 lines of
  top-level-pair juggling to removeProvider + best-effort promotion
- migration dry-run verified against a real-world signed-in config

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 17:14:30 +05:30

6.6 KiB

CLAUDE.md - AI Coding Agent Context

This file provides context for AI coding agents working on the Rowboat monorepo.

Quick Reference Commands

# Electron App (apps/x)
cd apps/x && pnpm install          # Install dependencies
cd apps/x && npm run deps          # Build workspace packages (shared → core → preload)
cd apps/x && npm run dev           # Development mode (builds deps, runs app)
cd apps/x && npm run lint          # Lint check
cd apps/x/apps/main && npm run package   # Production build (.app)
cd apps/x/apps/main && npm run make      # Create DMG distributable

Monorepo Structure

rowboat/
├── apps/
│   ├── x/                 # Electron desktop app (focus of this doc)
│   ├── rowboat/           # Next.js web dashboard
│   ├── rowboatx/          # Next.js frontend
│   ├── cli/               # CLI tool
│   ├── python-sdk/        # Python SDK
│   └── docs/              # Documentation site
├── CLAUDE.md              # This file
└── README.md              # User-facing readme

Electron App Architecture (apps/x)

The Electron app is a nested pnpm workspace with its own package management.

apps/x/
├── package.json           # Workspace root, dev scripts
├── pnpm-workspace.yaml    # Defines workspace packages
├── pnpm-lock.yaml         # Lockfile
├── apps/
│   ├── main/              # Electron main process
│   │   ├── src/           # Main process source
│   │   ├── forge.config.cjs   # Electron Forge config
│   │   └── bundle.mjs     # esbuild bundler
│   ├── renderer/          # React UI (Vite)
│   │   ├── src/           # React components
│   │   └── vite.config.ts
│   └── preload/           # Electron preload scripts
│       └── src/
└── packages/
    ├── shared/            # @x/shared - Types, utilities, validators
    └── core/              # @x/core - Business logic, AI, OAuth, MCP

Build Order (Dependencies)

shared (no deps)
   ↓
core (depends on shared)
   ↓
preload (depends on shared)
   ↓
renderer (depends on shared)
main (depends on shared, core)

The npm run deps command builds: shared → core → preload

Key Entry Points

Component Entry Output
main apps/main/src/main.ts .package/dist/main.cjs
renderer apps/renderer/src/main.tsx apps/renderer/dist/
preload apps/preload/src/preload.ts apps/preload/dist/preload.js

Build System

  • Package manager: pnpm (required for workspace:* protocol)
  • Main bundler: esbuild (bundles to single CommonJS file)
  • Renderer bundler: Vite
  • Packaging: Electron Forge
  • TypeScript: ES2022 target

Why esbuild bundling?

pnpm uses symlinks for workspace packages. Electron Forge's dependency walker can't follow these symlinks. esbuild bundles everything into a single file, eliminating the need for node_modules in the packaged app.

Key Files Reference

Purpose File
Electron main entry apps/x/apps/main/src/main.ts
React app entry apps/x/apps/renderer/src/main.tsx
Forge config (packaging) apps/x/apps/main/forge.config.cjs
Main process bundler apps/x/apps/main/bundle.mjs
Vite config apps/x/apps/renderer/vite.config.ts
Shared types apps/x/packages/shared/src/
Core business logic apps/x/packages/core/src/
Workspace config apps/x/pnpm-workspace.yaml
Root scripts apps/x/package.json

Feature Deep-Dives

Long-form docs for specific features. Read the relevant file before making changes in that area — it has the full product flow, technical flows, and (where applicable) a catalog of the LLM prompts involved with exact file:line pointers.

Feature Doc
Live Notes — single live: frontmatter block (one objective + optional cron / windows / eventMatchCriteria) that turns a note into a self-updating artifact, panel UI, Copilot skill, prompts catalog apps/x/LIVE_NOTE.md
Calls (video mode) — one hands-free call engine with four presets (voice / video / share screen / practice coaching), device-derived surfaces (full-screen ⇄ floating popout), frame pipeline, prompts catalog apps/x/VIDEO_MODE.md
Analytics — PostHog event catalog, person properties, use-case taxonomy, how to add a new event apps/x/ANALYTICS.md
Turn/session runtime — event-sourced storage, reference model, the npm run inspect debugger apps/x/packages/core/docs/turn-runtime-design.md, session-design.md

Common Tasks

LLM configuration

  • Config file: ~/.rowboat/config/models.json (v2; v1 files are migrated on boot by core/models/migrate.ts)
  • Schema: { version: 2, providers: { <id>: { flavor, apiKey?, baseURL?, … } }, assistantModel?: { provider, model }, taskModels?: { knowledgeGraph?, meetingNotes?, liveNoteAgent?, autoPermissionDecision?, chatTitle? }, deferBackgroundTasks? }
  • Providers carry credentials only (no model fields) — model lists are always fetched live via the unified catalog (core/models/catalog.ts, models:list IPC). Model choices live in assistantModel (the one primary) and taskModels (optional overrides that otherwise inherit the assistant).
  • Models catalog cache: ~/.rowboat/config/models.dev.json (OpenAI/Anthropic/Google only)

Add a new shared type

  1. Edit apps/x/packages/shared/src/
  2. Run cd apps/x && npm run deps to rebuild

Modify main process

  1. Edit apps/x/apps/main/src/
  2. Restart dev server (main doesn't hot-reload)

Modify renderer (React UI)

  1. Edit apps/x/apps/renderer/src/
  2. Changes hot-reload automatically in dev mode

Add a new dependency to main

  1. cd apps/x/apps/main && pnpm add <package>
  2. Import in source - esbuild will bundle it

Verify compilation

cd apps/x && npm run deps && npm run lint
cd apps/x && npm run typecheck   # dev tsconfigs — the only gate that typechecks *.test.ts

Tech Stack

Layer Technology
Desktop Electron 39.x
UI React 19, Vite 7
Styling TailwindCSS, Radix UI
State React hooks
AI Vercel AI SDK, OpenAI/Anthropic/Google/OpenRouter providers, Vercel AI Gateway, Ollama, models.dev catalog
IPC Electron contextBridge
Build TypeScript 5.9, esbuild, Electron Forge

Environment Variables (for packaging)

For production builds with code signing:

  • APPLE_ID - Apple Developer ID
  • APPLE_PASSWORD - App-specific password
  • APPLE_TEAM_ID - Team ID

Not required for local development.