Method / Edition 01
A list you can keep.
The catalog is a YAML file, a database, and a page. Editing the first updates the other two through the same pipeline every time. Here is the contract and the moving parts.
01 / Source
One YAML file is the whole catalog.
data/apps.yaml is the source of truth. Every entry carries its name, logo, screenshot, official site, source repository, and description. No CMS, no admin dashboard, no hidden database edits.
- slug: t3code
name: T3 Code
tagline: The open-source control plane for coding agents.
description: >-
A control surface for the agents already on your machine.
surface: web
siteUrl: https://t3.codes
repoUrl: https://github.com/pingdotgg/t3code
logo: /apps/t3code/logo.png
screenshot: /apps/t3code/screenshot.webp
platforms: [web, macos, windows, linux, ios, android]
agents: [claude-code, codex, cursor]
license: MIT
featured: true
addedAt: 2026-09-18The schema enforces HTTPS URLs, asset paths under /apps/, unique slugs, valid dates, and a non-empty surface type.
02 / Pipeline
Edit, validate, mirror, reload.
The sync is a full mirror inside one transaction. Add, edit, delete, or reorder entries; the database is rebuilt to match the file. An unchanged file writes no app rows.
Edit the list
Append an entry in data/apps.yaml. Keep the slug stable.
Add the images
Drop a logo and, when available, a 16:10 screenshot under apps/web/public/apps/<slug>/.
Validate and sync
bun run catalog:sync checks the schema and assets, then mirrors the list into the database.
Reload the surface
The API syncs on boot. The web reads /api/catalog once and filters locally.
$ bun run catalog:sync
Catalog valid · 8 apps · GUI 05 · WEB 01 · TUI 01 · IDE 01 · assets 16/16
Synced 8 apps from data/apps.yaml
+8 added · ~0 updated · −0 removed · 0 unchanged
database sqlite · rows mirrored · hash cd6fc1d4fb92Idempotent
Sync twice and the second run reports zero changes. A hash of the normalized list is stored with the last sync time.
SQLite or Postgres
One Kysely schema, two dialects. Set DATABASE_DRIVER and DATABASE_URL; tables are created on boot.
03 / Contract
What every row has to answer.
The required fields match the question a reader asks before opening a product: what is it called, what does it look like, where does it live, and what does it do.
04 / Rules
Four rules, no leaderboard.
One entry, one surface.
Each app gets its primary form factor. Agent support is recorded as metadata, not used to inflate a category.
Official material only.
Logos and screenshots come from the product's own site or repository. They belong to the projects, not this index.
Fast is a feature.
The API sends one payload. Search, surface filters, and sorting happen in the client. No request per keystroke.
No paid placement.
This is a field guide, not a leaderboard. Curated order is editorial; it is not a score, and it is not for sale.
05 / Adding
Add a surface.
- 01
Create apps/web/public/apps/<slug>/ and add logo.png plus a screenshot when available.
- 02
Append the entry to data/apps.yaml. Keep the slug lowercase and stable; it is the URL.
- 03
Run bun run catalog:sync. It validates the file and both assets before touching the database.
- 04
Run bun run dev. The API re-syncs on boot and the web reads the new payload.
06 / Credits
What is borrowed.
The design language — warm monochrome, hairline rules, dense tabular type, and a restrained clay accent — follows the qualities of slopalytics.com. The type is DM Sans with DM Mono for labels. The stack is Bun, Next.js, NestJS, Kysely, Tailwind, and shadcn-style primitives.
Product names, logos, screenshots, and marks belong to their respective owners. They are used here to identify each surface. No endorsement is implied. Every screenshot links back to the product's official site.
Surface over scoreboard.