vibe-replay turns AI coding sessions into animated, interactive web replays as self-contained HTML files. Supports Claude Code, Cursor, and Codex.
pnpm monorepo: packages/cli (npm: vibe-replay), packages/viewer (React → single HTML), packages/types (shared types), website/ (Astro), cloudflare/ (Workers API).
pnpm install # Install deps
pnpm build # Full build: viewer → cli
pnpm start # Build + run interactive picker
pnpm dev # Viewer (Vite HMR) + CLI (tsx watch) together
pnpm dev:dashboard # Dev mode with dashboard flag (-d)
pnpm dev:website # Website (Astro HMR) + Viewer (Vite HMR) together
pnpm test # Run unit tests
pnpm test:e2e # Run E2E tests (requires pnpm build first)
pnpm lint # Lint + format (auto-fix)
pnpm lint:check # Lint check (no fix, for CI)When to use which:
pnpm start— validate full user flow (build + run)pnpm dev— daily iteration with full HMR: viewer auto-reloads via Vite, CLI auto-restarts viatsx watchpnpm dev:website— website + viewer iteration: Astro HMR +/view/redirects to Vite viewer
Schema is managed by Drizzle ORM — single source of truth in cloudflare/src/db/schema.ts.
When you change the schema:
cd cloudflare
pnpm db:generate # Drizzle reads schema.ts, generates SQL migration in drizzle/
pnpm db:migrate:local # Apply to local D1
pnpm db:migrate:remote # Apply to production D1 (requires auth)- All tables (replays + Better Auth auth tables) are defined in
cloudflare/src/db/schema.ts - Migration files live in
cloudflare/drizzle/— commit them to git - Never hand-edit migration files — always use
drizzle-kit generate schema.sqlis kept as a reference but migrations are the source of truth- Better Auth uses
drizzleAdapter— it reads/writes auth tables through the same Drizzle instance
</escaping: JSON in<script>tags MUST escape</as<\/— browsers close the tag otherwise (seegenerator.ts)lastIndexOf("</head>"): UselastIndexOf, notindexOf— minified JS in the viewer bundle may contain the string</head>- Shared types:
Scene,Annotation,DataSourceInfo,ReplaySessionlive inpackages/types(@vibe-replay/types). CLI and viewer re-export from there. Provider-specific and viewer-specific types remain in their respective packages. - Viewer size limit: Keep under 800KB after build. This is why we use
markedinstead ofreact-markdown. Watch for size regressions when adding features. - Self-contained HTML: Output must make zero external requests. Everything inlined.
- Multi-file sessions: Claude Code
/resumecreates new JSONL files. Parser acceptsstring | string[]and merges by slug+project. - Cursor tri-source: Sessions come from SQLite
store.db(primary),globalStorage/state.vscdb, or JSONL (fallback). Discovery merges all sources. DB data is source of truth; JSONL supplements missing thinking/images. - Cursor SDK: SDK agents (TypeScript
@cursor/sdk) write to~/.cursor/projects/<workspace>/sdk-agent-store/<projectHash>/index.db(tables:agents,runs,run_events) and a parallel JSONL transcript atagent-transcripts/<agentId>/<agentId>.jsonl. The transcript is the source of user prompts (SDK doesn't store them in events) and the SDK index.db supplies tool results, structured per-run timing, and per-turn model. Seeproviders/cursor/sdk-reader.ts. Detection is by sessionId prefixagent-— IDE chat sessions (UUID-only) skip the SDK SQLite probe. - Skip
progresslines: These are subagent streaming artifacts in JSONL. - sql.js (WASM): Used instead of native SQLite bindings for portability — no C++ compiler needed.
- Session discovery cache: CLI picker + local dashboard use file cache at
~/.vibe-replay/cache/*.json(stale-while-refresh UX). Cache validity is tied to CLI release version (CLI_VERSION) plus envelope version, so caches auto-invalidate across releases. Keep cache writes best-effort and never block generation/parsing on cache failures.
- Always use pnpm — never npm/yarn
- TypeScript strict mode, ESM throughout
- oxlint for linting, oxfmt for formatting — both run automatically via PostToolUse hook and pre-commit hook
- Before commit: run
pnpm lint:checkand fix any errors. Do NOT commit code that fails lint. - Before commit: security review — check for leaked secrets, API keys, tokens, credentials, .env files
- Never bump versions or publish without explicit user confirmation
- After changes: update CLAUDE.md / README.md / CONTRIBUTING.md if anything becomes outdated
- Viewer changes →
pnpm build(rebuilds both packages) - CLI-only changes →
pnpm --filter vibe-replay build - Shared types changes → edit
packages/types/src/index.ts, both CLI and viewer pick them up automatically - Test with both small (~30 scenes) and large (~500 scenes) sessions
- Test modification policy — see
packages/cli/test/README.mdbefore changing any test
When creating a release for npm/GitHub, do this in order:
- Confirm with user first (no autonomous publish/version bump).
- Bump
packages/cli/package.jsonversionto the target release version. - Build CLI:
pnpm --filter vibe-replay build. - Verify displayed CLI version matches package version:
node packages/cli/dist/index.js --version- Note: startup banner
vX.Y.Zcomes frompackages/cli/src/version.tsreadingpackages/cli/package.json.
- Only then create tag/release/publish for that same version.
If tag/release is updated but packages/cli/package.json is not, CLI will still show the old version.
| What | Where |
|---|---|
| CLI entry | packages/cli/src/index.ts |
| Shared types | packages/types/src/index.ts |
| CLI types | packages/cli/src/types.ts |
| Transform (turns → scenes) | packages/cli/src/transform.ts |
| HTML generation | packages/cli/src/generator.ts |
| Editor server | packages/cli/src/server.ts |
| Provider interface | packages/cli/src/providers/types.ts |
| Cursor SDK reader | packages/cli/src/providers/cursor/sdk-reader.ts |
| Viewer entry | packages/viewer/src/App.tsx |
| Playback engine (pure) | packages/viewer/src/engine/ |
| Playback hook | packages/viewer/src/hooks/usePlayback.ts |
| Session loading | packages/viewer/src/hooks/useSessionLoader.ts |
| View preferences | packages/viewer/src/hooks/useViewPrefs.ts |
| DB schema (all tables) | cloudflare/src/db/schema.ts |
| Auth config | cloudflare/src/auth.ts |
| Worker (Hono routes) | cloudflare/src/worker.ts |
| Drizzle config | cloudflare/drizzle.config.ts |
| Drizzle migrations | cloudflare/drizzle/ |
| E2E test helpers | e2e/helpers.ts |
| E2E: generated HTML | e2e/generated-html.test.ts |
| E2E: editor server | e2e/editor-server.test.ts |
| E2E: CLI smoke | e2e/cli-smoke.test.ts |
| E2E: auth worker | e2e/auth-worker.test.ts |
See CONTRIBUTING.md for full architecture details.