agentleFS
tuo-leituo-lei/vibe-replay/AGENTS.md

vibe-replay

AGENTS.md39 starsChanged 3 months agoMITView on GitHub

This is the single source of truth for agent instructions in this repo. Every coding agent reads it, directly or through a shim — see Agent setup at the bottom. Put project knowledge here, not in a tool-specific file. vibe-replay turns AI coding sessions into animated, interactive, self-contained HTML replays. Supports Claude (Code/Desktop/Cowork), Cursor, Codex, OpenCode, Hermes, Muse, Pi, and Grok Bot. AI Studio embeds Pi's provider registry and agent loop; no separate AI CLI is required. pnpm monorepo: packages/cli…

  • Reads credentials
  • Installs packages
  • Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.

    Of the AGENTS.md files we read, the 31,439 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.

  • In this repository3 other files it gives its agents.

The file

Show its rules and text

We split this AGENTS.md into the rules it gives an agent

15 rules. About 0 are common, 7 are distinctive, and 8 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈7

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 8

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    When to use which:

    line 32
  2. Very specific:

    All tables (replays + Better Auth auth tables) are defined in cloudflare/src/db/schema.ts

    line 50
  3. Very specific:

    lastIndexOf("</head>"): Use lastIndexOf, not indexOf — minified JS in the viewer bundle may contain the string </head>

    line 59
  4. Very specific:

    Before commit: run pnpm lint:check and fix any errors or warnings (it uses --deny-warnings).

    line 104
  5. Very specific:

    Before PR: run pnpm verify. Keep its stages sequential.

    line 105
  6. Distinctive:

    After changes: update AGENTS.md / README.md / CONTRIBUTING.md if anything becomes outdated.

    line 108
  7. Distinctive:

    Test with both small (~30 scenes) and large (~500 scenes) sessions

    line 112
  8. Distinctive:

    When creating a release for npm/GitHub, do this in order:

    line 117
  9. Distinctive:

    Verify displayed CLI version matches package version:

    line 122
  10. Very specific:

    Only then create tag/release/publish for that same version.

    line 125
  11. Very specific:

    If tag/release is updated but packages/cli/package.json is not, CLI will still show the old version.

    line 127
  12. Distinctive:

    Every agent reads this file.

    line 165
  13. Very specific:

    Keep AGENTS.md under 32 KiB — Codex's default project_doc_max_bytes.

    line 194
  14. Very specific:

    Create it at skills/<name>/SKILL.md, then link both discovery locations:

    line 214
  15. Distinctive:

    Never copy a skill into a discovery directory — the two locations drift silently and nothing catches it until an agent acts on a stale …

    line 221

≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

AGENTS.md — vibe-replay

This is the single source of truth for agent instructions in this repo. Every coding agent reads it, directly or through a shim — see Agent setup at the bottom. Put project knowledge here, not in a tool-specific file.

What is this

vibe-replay turns AI coding sessions into animated, interactive, self-contained HTML replays. Supports Claude (Code/Desktop/Cowork), Cursor, Codex, OpenCode, Hermes, Muse, Pi, and Grok Bot. AI Studio embeds Pi's provider registry and agent loop; no separate AI CLI is required.

pnpm monorepo: packages/cli (npm: vibe-replay), packages/viewer (React → single HTML), packages/types (shared types), website/ (Astro), cloudflare/ (Workers API).

Commands

pnpm install               # Install deps
pnpm build                 # Full build: viewer → cli
pnpm start                 # Build + run interactive picker
pnpm dev                   # Viewer (Vite HMR) + CLI (tsx + Node fs.watch restarts) 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:cloudflare       # Run Cloudflare worker 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)
pnpm typecheck             # Strict TypeScript + website checks
pnpm verify                # Sequential pre-PR gate: lint, types, tests, build

When to use which:

  • pnpm start — validate full user flow (build + run)
  • pnpm dev — daily iteration with full HMR: viewer auto-reloads via Vite, and the CLI auto-restarts through the launcher's Node fs.watch watcher rather than tsx watch (which would intercept interactive stdin)
  • pnpm dev:website — website + viewer iteration: Astro HMR + /view/ redirects to Vite viewer
  • Dev launchers reserve ports for their entire lifetime. If one component's port is explicitly overridden, automatic selection skips that port for the other component as well. Use VIBE_API_PORT/VIBE_VIEWER_PORT for pnpm dev and VIBE_VIEWER_PORT/VIBE_WEBSITE_PORT for pnpm dev:website.

Database (D1 + Drizzle)

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.sql is 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

Gotchas

  • </ escaping: JSON in <script> tags MUST escape </ as <\/ — browsers close the tag otherwise (see generator.ts)
  • lastIndexOf("</head>"): Use lastIndexOf, not indexOf — minified JS in the viewer bundle may contain the string </head>
  • Shared types: Scene, Annotation, DataSourceInfo, ReplaySession live in packages/types (@vibe-replay/types). CLI and viewer re-export from there. Provider-specific and viewer-specific types remain in their respective packages.
  • Viewer bundle: The viewer ships as a self-contained HTML artifact. There is no hard size cap; keep an eye on bundle size and runtime performance as features grow.
  • Viewer styling: Tailwind v4 via the @tailwindcss/vite plugin (no postcss.config/autoprefixer). The terminal theme lives in packages/viewer/tailwind.config.js, loaded from src/styles/index.css via @config (v4's legacy-config bridge). The website is also on v4.
  • Self-contained HTML: Output must make zero automatic external requests. Viewer assets are inlined; remote replay images follow the explicit-consent rule below.
  • Remote replay images: ReplayImage renders data:image/* sources immediately, but HTTP(S) images require an explicit per-image click and use no-referrer. Never attach an external URL to <img src> before consent.
  • Multi-file sessions: Claude Code /resume creates new JSONL files. Parser accepts string | 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 (discovery perf: see packages/provider-cursor/AGENTS.md): 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 at agent-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. See packages/provider-cursor/src/cursor/sdk-reader.ts. Detection is by sessionId prefix agent- — IDE chat sessions (UUID-only) skip the SDK SQLite probe. Newer stores add runs.usage_json (camelCase: inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens) and it is the only token source for SDK sessions; optional columns are probed with PRAGMA table_info so older stores still parse. Tool duration comes from the first/last run_events.created_at of a call. Tool enrichment pairs positionally but validates the tool name (Edit/Write/MultiEdit count as one family) and prefers a matching path/command, leaving a block unenriched rather than attaching another tool's result.
  • Cursor subagents: IDE delegation trajectories live beside the parent transcript at agent-transcripts/<sessionId>/subagents/<subAgentId>.jsonl. Parent Task/Subagent calls do not carry the file ID, so packages/provider-cursor/src/cursor/parser.ts links them by normalized delegated-prompt containment and intentionally leaves unmatched files unattached rather than guessing by completion order. Current files contain tool calls and assistant text/reasoning, but usually no tool IDs/results, timestamps, token usage, or actual subagent model.
  • Cursor structured subagents: Global-state composer.composerHeaders (stored in ItemTable) may identify child composers through subagentInfo.parentComposerId/toolCallId. Discovery hides those children from the top-level session list, and parsing links them to the exact parent Agent tool call. Structured IDs take precedence; transcript prompt containment remains the legacy fallback.
  • Cursor project paths: ~/.cursor/projects/<encoded> encodes the workspace path by replacing / with - and dropping each segment's leading dot, so ~/.cursor arrives as Users-me-cursor. decodeProjectDir resolves the ambiguity by walking the real filesystem, trying <candidate> then .<candidate> at each level. The walk now returns the deepest directory it could confirm: once a real parent is found, everything left over is kept as one segment rather than split on -, because these workspaces are routinely deleted and splitting shredded their run ids (a scratch dir ending in a UUID became five path segments and a project named after the UUID's tail). Only when nothing below the root resolves does it fall back to replacing every - with /.
  • Agent run workspaces: Automation creates one scratch workspace per run (Cursor SDK artifacts, PR-review worktrees, /var/folders temp dirs), each its own project. agentRunWorkspaceParent in dashboard-utils.ts spots a run id at the end of a directory name — a UUID or a hex digest of 12+ chars, short enough digests are left alone so ros-4 and PR numbers don't match — and rolls the session up under the directory that holds it. rollupProject applies that alongside the Claude .claude/worktrees rule, so every surface gets it. Sessions, Replays, and Projects hide these workspaces by default (agentRuns=true in the URL shows them); the filter runs on the raw project path, since the rollup produces a parent that no longer looks like a run workspace.
  • Project identity: packages/types/src/project-identity.ts is the shared classification layer for raw workspace paths. It preserves the generic run-id safeguards, recognizes Cursor SDK automation roots/workflows (including hyphen/underscore path variants), carries repository/PR metadata, and exposes canonical keys consumed by scanner aggregation and viewer rollups. Raw top-project entries remain available so the Projects agentRuns=true toggle can reveal individual workspaces.
  • Cursor duplicate transcripts: Discovery coalesces duplicate transcript copies by session ID without adding their byte counts, and parsing removes identical records only across distinct files. Inline tool blocks are authoritative; mtime sidecars may fill unresolved tools but must not create duplicate calls.
  • Claude Cowork replay duplicates: Cowork audit.jsonl can contain a host-loop user record plus an isReplay: true copy of the same prompt. Discovery excludes replay copies that match an original by content/UUID, and rich scans use the Cowork parser so dashboard prompt analytics match replay output.
  • Cowork result billing: each type: "result" record is one completed host-loop run's final bill — usage, modelUsage, total_cost_usd and duration_ms are per-run, not cumulative. Session totals are the sum over UUID-deduplicated results; never mix them with assistant message.usage snapshots (those are partial streams and undercount output badly), and never add usage to modelUsage or total_cost_usd to modelUsage.*.costUSD. Audits with no result records fall back to assistant snapshots plus the timestamp-gap duration estimate. system/api_retry maps to apiErrors with attempt metadata only — the raw error text is never stored.
  • Provider-reported cost: ProviderParseResult.reportedCostUsd wins over the local pricing table in transform.ts, which is what makes cost available for model generations pricing.ts does not know.
  • AI Studio runtime: packages/cli/src/ai-runtime.ts owns the embedded Pi Models registry, provider-owned API-key/OAuth flows, and atomic 0600 auth/settings files. feedback.ts runs Coach, Translate, and Tone through @earendil-works/pi-agent-core with a single structured result tool and no filesystem, network, or MCP tools. The initial registry contains OpenAI API key, ChatGPT plan OAuth, Codex OAuth, OpenRouter API key/OAuth, and OpenCode Zen API key providers. The editor can also configure one OpenAI-compatible proxy (custom-openai), discover models from /models or /model, and send Chat Completions requests through Pi. Shared provider settings live in packages/viewer/src/components/AiProviderSettings.tsx and render the same provider/model/auth/custom-endpoint controls inline in Settings or inside the AI Studio modal; model choices are searchable and the selection is remembered in browser storage and synced to ~/.vibe-replay/ai-settings.json. Vibe Replay prefers gpt-5.6-luna and never reads Pi's default. Endpoint metadata lives in a separate atomic 0600 ~/.vibe-replay/ai-providers.json file (VIBE_REPLAY_AI_CONFIG can override it); custom API keys remain in the credential store and are never returned by provider discovery. HTTP custom endpoints are limited to loopback; remote endpoints must use HTTPS. Provider setup is intentionally separate from replay/session credentials.
  • opencode: SQLite discovery and parsing support legacy and v2 stores. See packages/provider-opencode/AGENTS.md.
  • Hermes: Sessions live in SQLite at ~/.hermes/state.db (FTS5-backed), plus one DB per named profile at ~/.hermes/profiles/<name>/state.db. Discovery scans all of them (hermesDbPaths() in packages/provider-hermes/src/hermes/sqlite.ts), dedups by session id, and writes <dbPath>#session:<id> marker paths; parse resolves the right DB from the marker before falling back to a scan. hermesRootDir() mirrors Hermes's get_default_hermes_root(): HERMES_HOME inside ~/.hermes is profile mode (root stays ~/.hermes); outside it, that path itself is the root. Caveat: sql.js reads raw file bytes and cannot replay un-checkpointed -wal frames, so sessions still sitting in the WAL (long-running gateway) are invisible until Hermes checkpoints. Tables: sessions (token/cost/git columns maintained by Hermes) + messages (OpenAI-style: assistant rows carry tool_calls JSON + reasoning; tool rows carry tool_name + tool_call_id + result content). Discovery writes <dbPath>#session:<id> marker paths and reads ~/.hermes/.update_check for the version. Context compaction is recorded two ways: compacted=1 rows (pre-compaction history, kept in full) and a user row prefixed [CONTEXT COMPACTION (→ subtype: "compaction-summary", mirroring claude-code); every compacted=1 run boundary emits its own compaction event, summary-only stores fall back to marker-derived compaction metadata, and discovery excludes those marker rows from prompt counts and firstPrompt. Provider cost: reportedCostUsd comes from sessions.actual_cost_usd, falling back to estimated_cost_usd when positive (cost_status "included"/"unknown" store 0). Parse is SQLite-backed, so background scan uses buildLightweightHermesScanResult in scanner.ts. Tool names map to the viewer vocabulary in tool-mapping.ts. Bot Mode: each Bot is a profile (~/.hermes/profiles/<bot>/), its canonical Bot Chat is a hidden=1 session (title: "Bot Chat", profile_name: "<bot>", cwd: "") with the same storage shape; discovery treats cwd-less sessions as ~/.hermes/profiles/<bot> so bots appear as distinct projects, and parse infers per-tool *_durationMs from assistant.timestamp → tool.timestamp so activity timelines no longer show large unattributed gaps. See packages/provider-hermes/.
  • Grok Bot: Cloud-box JSONL at /home/box/agent-data/agent-transcripts (and sand-data); env GROK_BOT_TRANSCRIPTS_DIR / VIBE_REPLAY_GROK_BOT_DIR; optional ~/.grok-bot/agent-transcripts. Mac sand-client-persistence replicas are in scope. Details: packages/provider-grok-bot/AGENTS.md.
  • Skip progress lines: 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. Home cold starts use full-screen discovery until the source catalog and replay list are ready; cached source snapshots bypass it, while replay-only caches do not. /api/sources/stream provides early provider storage detection, per-provider reading states, bounded recent-session previews, and preparation counts; local provider reads run concurrently, while final output keeps registry order and session observers run sequentially; shared discovery replays provider states to late SSE subscribers; rich scans continue in the background. Keep cache writes best-effort and never block generation/parsing on cache failures.
  • Replay compaction snapshots: Generated replay cards must use only the replay summary's own compactionCount; a missing legacy field means zero. Never fall back to the current source scan, or a replay generated before a later source compaction is mislabeled.
  • SSH sources: ~/.vibe-replay/config.json may define generic OpenSSH targets under remoteSources. packages/cli/src/remote.ts indexes remote Codex, Claude Code, and Pi JSONL files with the system ssh client, stages them in a per-target read-only cache through manifest-sized bounded streams, and reuses the existing providers. Leftover .sync-* dirs and 30-day-idle unconfigured target caches are pruned. SSH credentials stay in the user's OpenSSH configuration/agent; do not add keys or passwords to the vibe-replay config. Remote Codex /resume titles prefer the latest explicit thread_name from session_index.jsonl, then fall back to a read-only query of state_5.sqlite; the live database and WAL are never copied, and Python sqlite3 plus a schema-aware sqlite3 CLI fallback are supported. Metadata-only, missing, or unreadable sources remain visible with no-prompts or unreadable status and cannot generate an empty replay. Remote repository identity is local dashboard metadata and is stripped from share/export payloads. Remote Live mode is disabled. Remote source identity is carried separately from provider/data-source identity so equal session IDs on different targets do not merge.
  • Dashboard Settings: The editor dashboard's Settings tab manages the same remoteSources entries through local /api/settings routes. It validates ids, hosts, providers, and timeouts, writes the config atomically while preserving unrelated keys, and offers a bounded SSH probe. Never add passwords or private keys to this UI or config.
  • SSH insight privacy: Remote sessions may contribute to local dashboard insights, but aggregateDailyInsights excludes SSH locations from the optional cloud sync so remote project/provider aggregates stay on the machine.
  • Windows support: Claude Desktop and Cowork search standard %APPDATA%/%LOCALAPPDATA% roots plus MSIX package storage under %LOCALAPPDATA%\Packages\Claude_*\LocalCache\Roaming; Claude project-memory lookup encodes drive paths as C--Users-...; Cursor encodes workspace dirs as C:\a\b → C-a-b (drive colon dropped, separators → -); decodeProjectDir has a win32 branch that resolves these against the real filesystem (POSIX uses / root, Windows uses the drive root). Cursor on Windows stores IDE chats only in the globalStorage/state.vscdb under %APPDATA%\Cursor (there is no ~/.cursor/chats dir). Replay output normalizes file paths to / for cross-platform display via redactFilePath in transform.ts — never apply that to prose. Build scripts shell out to scripts/copy-file.mjs instead of mkdir -p/cp (not available in PowerShell). .gitattributes forces eol=lf so Windows clones don't trip oxfmt --check with CRLF.
  • Pi harness tools (discovery perf: see packages/provider-pi/AGENTS.md): Pi sessions use both native tool names (bash, edit, write) and harness names (exec_command, apply_patch). The Pi provider maps exec_command to replay Bash and parses apply_patch into replay Edit inputs (including all touched file_paths, with the first file represented by the single-diff viewer). Harness tools report failure through details.exit_code rather than the isError flag native tools use, so a non-zero exit code marks the result as an error. Native edit can carry several replacements; all of them are joined into the single old_string/new_string diff the viewer renders, and the tool stays named Edit.
  • Pi token accounting: compaction and branch_summary entries carry the usage of a separate summarization call, so their usage adds to session totals (matching Pi's own billed totals). tokensBefore is context size, not usage, and only feeds compactions[].preTokens. Summary usage never enters turnStats. Session model is the last selected model, which is what discovery reports.
  • Pi compaction diagnostics: packages/provider-pi/src/pi/parser.ts preserves successful compaction entries and emits privacy-safe diagnostics events. A successful compaction is labeled automatic-context only as an inferred classification when durable evidence (such as a preceding assistant stopReason: "length") supports it; Pi v3 JSONL does not persist the compaction_start/compaction_end or session_compact_failed lifecycle events, so missing failure events are inconclusive. Explicit persisted messages such as Auto-compaction failed: ... become failed compaction diagnostics, while unrelated stopReason: "error" assistant messages remain assistant-api-error events. get_compaction_diagnostics in packages/cli/src/local-assistant.ts and the replay StatsPanel expose these distinctions without exposing raw error text in metadata.
  • Pi replay metric scope: Assistant records with usage but no visible content remain in aggregate token/cost totals and are reported as a data-quality note, while still producing no empty replay scene. Viewer per-turn joins use TurnStat.turnIndex; sparse rows remain no-data rather than shifting later prompts. Context charts use the configured meta.contextLimit when available and distinguish recorded compactions from inferred context drops; the chart's prompt-footprint values are provider-reported accounting, not guaranteed actual model context size.
  • Usage index (tool / MCP / skill): scanner.ts derives one UsageEvent per invocation (name, turn, timestamp, duration, status, subagent) plus a per-session SessionUsageSummary; only the latest 100 detail events are retained, and tool inputs/results are never stored. MCP naming differs per provider and is normalized in parseMcpUsage: mcp__<server>__<tool> (Claude/Codex), CallMcpTool {server, toolName} (Cursor SDK), mcp-<server>-<tool> (Cursor IDE — split at the last dash because server IDs are kebab-case, and the normalized input's server/tool_name wins), mcp_<server>_<tool> (older Cursor — split at the first underscore because tool names are snake_case, with mcp_auth/mcp_get_tools/mcp_meta_tool* excluded as MCP management tools), and Pi's single mcp tool (server field, or <server>_<tool> in tool). An MCP call is counted under mcpServers/mcpTools only — never also as a tool — so the Tool facet lists real tools instead of repeating the MCP facets. Cowork names servers by UUID, so its parser exposes mcpServerNames from the sibling local_{id}.json remoteMcpServersConfig. OpenCode/Hermes and deferred Cursor scans emit no usage events — they say so in dataQualityNotes rather than looking like zero usage. Cursor reports the same server under several ids (user-<name>, <name>::mcpScope:profile:...:cfg:...); stripCursorServerScope folds them so one server is one facet. /api/scan/results strips events; per-session events come from /api/usage/events, and /api/usage/rollup serves the compact per-session { startTime, usage } projection the Insights page aggregates client-side (engine/usage-rollup.ts, range-filtered by instant so switching 7d/30d/90d costs no request). The dashboard renders Tool and MCP server facets plus a per-session breakdown (engine/session-usage.ts) built from the summary alone, so expanding a card costs no request; the MCP tool facet is a drilldown that only appears once a server or tool is selected, and long facet lists scroll inside their section so the ones below stay reachable. Context compaction counts are indexed with each scan result (OpenCode/Hermes compute them during lightweight discovery), exposed as a URL-synced dashboard signal filter, and shown directly on session/replay cards. The scan-results cache key carries SCANNER_VERSION, so a bump can't serve results in the previous shape. Insights renders a "Tools & MCP" card (top tools / MCP servers / MCP tools / skills, each with calls and session reach). Cursor SQLite sessions are scanned twice: a fast pass with rich parsing deferred, then a background backfillDeferredUsage pass in server.ts that indexes their usage and rewrites the scan cache (progress in /api/scan/status as usageBackfill).
  • Context composition privacy: ContextBreakdown contains aggregate counts, UTF-8 byte sizes, and provider-estimated tokens only—never raw system prompts, tool descriptions, schemas, or conversation text. Cursor reads the latest promptTokenBreakdown snapshot; Cowork counts its persisted system prompt and enabled MCP definitions; Codex counts rollout base/developer instructions; Pi counts unique MCP descriptors observed in active-branch tool-result metadata. Byte measurements are not token counts, Cursor snapshots are not cumulative usage, and Pi observations may be incomplete. Keep these distinctions in UI copy and preserve the field through parser → replay/scan → durable local insights.
  • CLI session workflows: See packages/cli/AGENTS.md for reference resolution, saved snapshots, diagnostic coverage, pagination, and read-only storage recovery.

Rules

  • Always use pnpm — never npm/yarn
  • TypeScript strict mode, ESM throughout
  • oxlint for linting, oxfmt for formatting. The lefthook pre-commit hook runs both on staged files for every agent. Claude Code additionally fixes each file right after editing it; other agents should run pnpm lint themselves before finishing.
  • Before commit: run pnpm lint:check and fix any errors or warnings (it uses --deny-warnings). Do NOT commit code that fails lint.
  • Before PR: run pnpm verify. Keep its stages sequential; concurrent full checks can cause integration-test timeouts.
  • 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 AGENTS.md / README.md / CONTRIBUTING.md if anything becomes outdated. Never edit CLAUDE.md to record project knowledge — it is a shim (see Agent setup).
  • Viewer changes → pnpm build (rebuilds both packages)
  • CLI-only changes → pnpm --filter vibe-replay build
  • Shared types changes → edit the relevant source under packages/types/; both CLI and viewer pick the changes up automatically
  • Test with both small (~30 scenes) and large (~500 scenes) sessions
  • Test modification policy — see packages/cli/test/README.md before changing any test

Release checklist (important)

When creating a release for npm/GitHub, do this in order:

  1. Confirm with user first (no autonomous publish/version bump).
  2. Bump packages/cli/package.json version to the target release version.
  3. Build CLI: pnpm --filter vibe-replay build.
  4. Verify displayed CLI version matches package version:
    • node packages/cli/dist/index.js --version
    • Note: startup banner vX.Y.Z comes from packages/cli/src/version.ts reading packages/cli/package.json.
  5. 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.

Key files

WhatWhere
CLI entrypackages/cli/src/index.ts
Shared typespackages/types/src/index.ts
CLI typespackages/cli/src/types.ts
Transform (turns → scenes)packages/cli/src/transform.ts
HTML generationpackages/cli/src/generator.ts
Editor serverpackages/cli/src/server.ts
AI provider/runtimepackages/cli/src/ai-runtime.ts / packages/cli/src/feedback.ts
Provider interfacepackages/provider-contract/src/index.ts (contract) / packages/providers-default/src/index.ts (registry)
Cursor SDK readerpackages/provider-cursor/src/cursor/sdk-reader.ts
opencode providerpackages/provider-opencode/src/opencode/
Hermes providerpackages/provider-hermes/src/hermes/
Viewer entrypackages/viewer/src/App.tsx
Playback engine (pure)packages/viewer/src/engine/
Playback hookpackages/viewer/src/hooks/usePlayback.ts
Session loadingpackages/viewer/src/hooks/useSessionLoader.ts
View preferencespackages/viewer/src/hooks/useViewPrefs.ts
DB schema (all tables)cloudflare/src/db/schema.ts
Auth configcloudflare/src/auth.ts
Worker (Hono routes)cloudflare/src/worker.ts
Drizzle configcloudflare/drizzle.config.ts
Drizzle migrationscloudflare/drizzle/
E2E test helperse2e/helpers.ts
E2E: generated HTMLe2e/generated-html.test.ts
E2E: editor servere2e/editor-server.test.ts
E2E: CLI smokee2e/cli-smoke.test.ts
E2E: auth workere2e/auth-worker.test.ts
Agent instruction wiringAGENTS.md, CLAUDE.md, .agents/skills/, .cursor/rules/
Ask Replay paritydocs/ai-chat-feature-parity.md
Session observabilitydocs/session-observability-audit.md

Agent setup

Every agent reads this file. Only the plumbing differs.

AgentInstructionsSkills
CodexAGENTS.md (native).agents/skills/ (native)
CursorAGENTS.md (native) + .cursor/rules/—
PiAGENTS.md (native, preferred over CLAUDE.md).agents/skills/ (native)
opencodeAGENTS.md (native)—
Claude CodeCLAUDE.md (@AGENTS.md + Claude-only notes).claude/skills/ (native)

Shims exist because Claude Code reads CLAUDE.md and not AGENTS.md, and because it discovers skills under .claude/skills/ while Codex and Pi use .agents/skills/:

  • CLAUDE.md is a real file whose first line is @AGENTS.md. An import is used rather than a symlink because Windows symlinks need Administrator or Developer Mode, and this repo supports Windows contributors.
  • The replay skill has exactly one real copy, at skills/replay/ — the published path that .claude-plugin/plugin.json and the README install command point at. .agents/skills/replay and .claude/skills/replay are both symlinks into it. On a Windows clone, run git config core.symlinks true and re-checkout before running the tests; the wiring guard intentionally rejects a copied directory.

test/agent-instructions.test.ts in packages/cli guards this wiring. It fails if CLAUDE.md stops importing AGENTS.md, if either skill symlink breaks or turns back into a copy, or if AGENTS.md outgrows the size limit below.

Size limit

Keep AGENTS.md under 32 KiB — Codex's default project_doc_max_bytes. Past it Codex truncates the file and silently stops reading later sections. The guard test enforces this. Check the actual file size before adding content; if space is tight, split rather than append.

To split, move a package's gotchas into a nested pair inside that package:

packages/provider-cursor/AGENTS.md   # the content
packages/provider-cursor/CLAUDE.md   # one line: @AGENTS.md

Both files are needed. Codex, Cursor, Pi, and opencode layer nested AGENTS.md; Claude Code only layers nested CLAUDE.md, so it needs the sibling shim to see the same text. Nested files load when an agent touches that subtree and cost nothing elsewhere. Do not use .claude/rules/ for this — only Claude Code reads it, which reintroduces exactly the split this layout removes.

Adding a skill

Create it at skills/<name>/SKILL.md, then link both discovery locations:

ln -s ../../skills/<name> .agents/skills/<name>   # Codex, Pi
ln -s ../../skills/<name> .claude/skills/<name>   # Claude Code

Never copy a skill into a discovery directory — the two locations drift silently and nothing catches it until an agent acts on a stale version.

See CONTRIBUTING.md for full architecture details.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.