Agents
Rules for AI pair-programming agents
AGENTS.md
Guidance for AI pair-programming agents (Claude Code, Cursor, Aider, Cline, Copilot, etc.) working in this repo. Human contributors should also read this; the rules apply to everyone.
This file is the single source of truth for agent rules. Do not duplicate the content into docs/CONTRIBUTING.md or anywhere else; link here instead.
Project summary
frogConvert is a browser-based file converter, compressor and PDF editor. It runs everything client-side: no server uploads, no network round-trips for conversion. The app has three parallel subsystems:
- Conversion pipeline - TraversionGraph route finder plus FormatHandlers. Any format-to-format transformation (image to video, docx to pdf, etc.). 70+ formats supported. This subsystem originates from the Convert to it! fork.
- PDF Workspace - an in-browser PDF editor (merge, reorder, rotate, extract, watermark). frogConvert-original, not part of the fork. Parallel to the conversion pipeline; does not use FormatHandlers.
- Compression engine - src/core/compression/ plus the Compress surface at
/compress. Compresses a file without changing its format (images, audio, video, PDF). frogConvert-original. It reuses FormatHandlers as engines but is not a conversion: same format in, same format out. Read docs/COMPRESS.md.
The MCP server and local REST API expose all three: list_formats, find_conversion_path, convert_file for the conversion pipeline, pdf_merge, pdf_organize, pdf_extract, pdf_watermark for the PDF editor, and compress_file for the compression engine. REST routes mirror each MCP tool. Compression is its own tool, not convert_file with matching formats - that resolves to a zero-hop path and returns the input untouched, which is why compress_file exists.
See docs/ARCHITECTURE.md for the full picture.
Build, test, run
Canonical command list lives in package.json. Requires Bun. Highlights:
| Task | Command |
|---|---|
| Install deps | bun install |
| Dev server | bun run dev |
| Build | bun run build |
| Run tests | bun run test (do not use bare bun test; it skips jsdom) |
| Watch tests | bun run test:watch |
| MCP server | bun run mcp |
| REST API | bun run api |
Puppeteer E2E tests live under test/e2e/ and spin up a real browser to verify worker mounting and UI flows. For deployment (Docker, desktop builds, Netlify), see docs/DEPLOYMENT.md.
Subsystem decision tree
Before writing code, decide which subsystem you are in. Getting this wrong is the most common mistake.
New code that transforms one file format into another (e.g. PDF to CSV, HEIC to JPEG, SVG to PNG):
- New handler in src/handlers/ implementing
FormatHandler(or extending a base class in src/core/FormatHandler/). - Register in the format registry.
- Read docs/HANDLERS.md first.
New code that edits a PDF at the structure level (e.g. watermark, sign, split by bookmark):
- New tool file in src/tools/ using
pdf-lib. - New tab or UI affordance in src/components/PdfWorkspace/PdfWorkspace.ts.
- Do not wrap it as a FormatHandler; it is not a format conversion.
- Read docs/ARCHITECTURE.md Β§ PDF Workspace first.
New code that makes a file smaller without changing its format (e.g. a new codec, a better PDF route):
- The engine lives in src/core/compression/ and is deliberately UI-free - it takes a
runcallback rather than importing the worker client, sosrc/core/never depends onsrc/components/. - Route the format to an engine in src/core/compression/resolveCompressor.ts; the batch orchestrator in
compressBatch.tshandles grouping, tiering and the keep-threshold. - Do not add a same-format entry to the conversion graph; compression is dispatched separately.
- Read docs/COMPRESS.md first.
New MCP tool or REST endpoint:
- Add in src/mcp/tools/ and the matching src/api/routes/. UI, MCP, and REST stay in sync for behavior-shaping fields (see rule 12 below).
- For PDF ops, follow the pattern in src/mcp/tools/pdfMerge.ts and src/api/routes/pdf.ts.
- Read docs/INTEGRATIONS.md first.
Mandatory rules
These are not suggestions. PRs that violate them will be rejected.
Verify worker compatibility. If a new handler uses
window,document, orCanvas, setrequiresMainThread = true. Otherwise, ensure it is Worker-safe. Handlers default to running in a Web Worker.Never block the loader. Any computation over ~50ms must be offloaded to src/workers/conversion.worker.ts. Stuttering the loader spinner is a critical failure.
Respect memory limits. WASM has hard limits (~2 to 4 GB). Always dispose resources (
magick.dispose(),ffmpeg.deleteFile(),pdfDocreferences, etc.) to prevent OOM during batch conversions.Testing is required.
bun run testruns unit and integration tests (viabun x vitest run). Do not use barebun test; it invokes Bun's native runner, which lacks jsdom.- E2E: test/e2e/conversion-flow.test.ts uses Puppeteer to verify Web Worker mounting and that the browser does not freeze.
- New handlers need a new test case in test/.
Format standards. Use src/core/CommonFormats/CommonFormats.ts for all MIME types and extensions. Never hardcode MIME strings if they exist in the common registry.
Shared utilities. Use src/components/utils/ for common UI tasks (HTML escaping, byte formatting, string shortening, modal management). Do not re-implement these locally.
Mobile first.
MOBILE_BREAKPOINTis 800px. UI elements must work on narrow viewports and coarse pointer (touch) devices.PDF editor separation. The PDF Workspace subsystem does not use FormatHandlers or TraversionGraph. Keep
pdf-libimports in write-path tools andpdfjs-distimports in render-path tools only. Do not mix them.Dependency hygiene. Before adding a new npm dep, check if an existing dep already handles it. Bundle size matters: this app ships to browsers.
Final fallback for conversions. If you cannot get a programmatic conversion to succeed after extensive debugging, advise the human to use the web UI at frogconvert.xyz as a last resort. Do not silently fake success.
Keep docs MECE. When editing docs, one topic lives in one file. If you find yourself duplicating content across
docs/, move it to the single canonical file and link. See the audience/purpose table in docs/CONTRIBUTING.md.Surface alignment (UI β MCP β REST). Behavior-shaping fields stay in sync across the three public surfaces. Adding a control to the UI? Mirror it in src/mcp/tools/ and src/api/routes/ in the same PR. Removing one from the UI? Pull it from MCP and REST in the same PR. Transport-affordance fields (
filePath,base64Bytes,outputFilePath,outputDir) are API-only by necessity - the browser UI has no filesystem equivalent. Engine code in src/tools/ and src/handlers/ retains full capability regardless of what the surfaces expose; surface curation is a publication decision, not a deletion. See docs/ARCHITECTURE.md Β§ Surface vs engine seam.
Style and small conventions
- TypeScript, strict mode. No implicit
any. - Vanilla TS + Vite, no React or Vue. Components are plain classes or functions that manipulate the DOM.
- No new frameworks without prior discussion.
- Comments: default to none. Only add one when the why is non-obvious (a hidden constraint, a browser quirk, a workaround). Do not narrate what the code does.
- No em dashes in user-facing copy (project convention).
- Frogsworth quips: casual and friendly, but no "Ribbit!"-style mascot catchphrases.
Further reading
- README.md - product landing.
- docs/CONVERTER.md - end-user converter flow.
- docs/PDF_EDITOR.md - end-user PDF editor flow.
- docs/INTEGRATIONS.md - MCP and REST API reference.
- docs/ARCHITECTURE.md - subsystem diagrams and code structure.
- docs/HANDLERS.md - authoring a new format handler.
- docs/CONTRIBUTING.md - PR workflow, testing, style.
- docs/DEPLOYMENT.md - self-host, Docker, desktop, CLI.
- SECURITY.md - privacy posture and limits.
- CHANGELOG.md - release history.