Rules for AI agents (Claude Code, Codex, Gemini CLI, etc.) working in this repo.
- ESM only.
"type": "module"in package.json. All imports must use.jsextensions. No CommonJSrequire(). - Node.js 20+. Use native
fetch,FormData,Blob,AbortSignal.timeout()— no polyfills. - No new runtime dependencies without explicit approval. Current dep count is intentionally minimal. (
@modelcontextprotocol/sdkwas approved for the MCP server;keytaris an optional, lazily-loaded native dep.) - tsup is the bundler. Do not introduce webpack, rollup, esbuild directly, or vite.
- No
any. TypeScript strict mode is on. Useunknownand narrow — do not cast toany.
All transcription goes through TranscriptionProvider in src/transcription/types.ts. Adding a new provider = new class implementing that interface + entry in src/transcription/registry.ts. Never call a provider API directly from a command file.
Platform audio backends live in src/audio/. getAudioFactory() in src/audio/capture.ts lazy-imports the right one. Never reference LinuxPulseCapture / MacAvFoundationCapture / WindowsDshowCapture directly from commands.
All user-facing errors go through the typed error classes in src/errors.ts. Each class has a specific exitCode (see the exported ExitCode map). Don't process.exit() directly from business logic — throw the appropriate error class and let handleError() in src/index.ts catch it.
- Every command receives an
AgentContext(src/agent/context.ts) and emits results viactx.ok(command, payload, humanRender?)— never write results straight to stdout. Human-only chatter goes throughctx.note()(stderr, suppressed by--quiet). - The JSON envelope shape in
src/agent/output.tsand per-commanddatapayloads are a public contract. BumpSCHEMA_VERSIONon any breaking change. src/agent/manifest.tsis the single source of truth for the command/tool surface; the MCP server (src/agent/mcp.ts) derives tools from it. Keep them in sync.- Local providers must not trigger upload consent — gate
ensureUploadConsent()behindproviderUploads().
- No secrets in source. API keys come from env vars only. No
.envfiles committed. No keys inconfig.json. - No
shell: trueinspawn/execcalls. UseexecFile(notexec) with an explicit args array to prevent command injection. FFmpeg args must be built from validated inputs. - Upload consent. Any code path that sends audio to a cloud API must go through
src/consent.tsfirst. - No logging of full API keys. Use
redactKey()fromsrc/log.ts.
- Test files go in
test/unit/. Use vitest + msw for HTTP mocking. - Network calls in tests must be intercepted by msw — no real API calls in CI.
resetConfigCache()must be called inbeforeEach/afterEachwhen testing config.
src/tui/recorder.tsxkeyboard bindings — they are designed to avoid conflicts with Flow Clock / terminal shortcuts.- WAV capture parameters (16kHz, mono, pcm_s16le) — optimized for Whisper, changing these affects transcription quality.
- Exit codes in
src/errors.ts— consumers may script against them. - The
--yes/RECMP3_SKIP_CONSENTconsent bypass — required for CI/scripting use cases.