ShipVeryFastShipVeryFast
Documentation

Working with AI coding agents

ShipVeryFast ships first-class guidance for AI coding agents (Claude Code, Cursor, Codex, and friends) so they edit the codebase the way a senior maintainer would. This page explains the three guidance files, the conventions agents must honor, and the hard rules that keep the repo healthy.

The guidance lives in version control, not in a chat history. Point your agent at the repo root and it reads CLAUDE.md, AGENTS.md, or .cursorrules automatically, no prompt engineering required.

Three guidance files kept in sync

The same conventions are written for three different agent ecosystems. They are deliberately redundant so that whichever file your tool reads, it gets the same rules.

FileAudienceRole
CLAUDE.mdClaude CodeThe canonical, longest guide: architecture, commands, patterns.
AGENTS.mdCodex, Cursor, Claude Code, othersA tool-agnostic mirror of CLAUDE.md, commands, layout, conventions, gotchas.
.cursorrulesCursorA tight, scannable rules digest that links back to the other two.

AGENTS.md states the contract explicitly: "This mirrors CLAUDE.md, keep the two in sync." When you change a convention, update all three so no agent gets stale instructions.

Repo conventions agents must follow

Every API route in the boilerplate follows the same four-part shape. Agents adding or editing route handlers under app/api/ are expected to reproduce it. The reference implementations are app/api/security/alerts/route.ts and app/api/ai/chat/route.ts.

  • Validate input with Zod, parse the request body against a schema before doing anything else.
  • Gate with the session, call getServerSession(authOptions) from @/libs/auth and reject unauthenticated callers.
  • Rate-limit, use a limiter from @/libs/rateLimiter. The module exports purpose-built limiters: apiRateLimiter, authRateLimiter, sensitiveOpRateLimiter, magicLinkRateLimiter, and aiRateLimiter.
  • Log with securityLogger and return NextResponse.json({ error, details }, { status }) on failure.

For environment variables, agents must add each new key to the Zod schema in libs/config.ts and to .env.example. Integration keys are marked .optional() so the app still boots without them (graceful degradation). See Environment variables for the full list.

AI / LLM rules: never mix SDKs

The boilerplate has a strict policy for LLM code, repeated verbatim across all three guidance files. The Claude path must use the official @anthropic-ai/sdk with model claude-opus-4-8; the OpenAI path uses the official openai SDK. No OpenAI-compatible shims for Claude, and never mix the two SDKs in one code path.

Both sit behind the provider-agnostic interface in libs/ai/. Agents call the shared surface rather than instantiating a vendor client inline:

// libs/ai/index.ts
export const DEFAULT_PROVIDER: ProviderId = 'anthropic';

export function getProvider(id: ProviderId): Provider { /* ... */ }
export function availableProviders(): Provider[] { /* ... */ }
export function providerCatalogue() { /* ... */ }

// Every provider implements Provider.streamChat(...)

See The AI assistant for how the streaming chat endpoint and dashboard UI are wired up on top of this layer.

Styling and structure rules

Visual changes should stay inside the existing design system so the UI remains coherent. The guidance files spell out the conventions agents should respect:

  • Palette: Tailwind with a slate / indigo palette, font-display for headings, and rounded-2xl cards bordered with border-slate-200 dark:border-slate-800.
  • Theming: components/theme/theme-provider.tsx injects a global stylesheet of !important overrides. Be careful when adding header buttons or gradient backgrounds, legacy selectors can catch them. Toggles use inline-flex + translate-x, not an absolute thumb.
  • Imports: components are exposed through barrel exports; prefer the established import surfaces over deep relative paths.

One concrete charting gotcha from AGENTS.md: recharts gradient <defs> ids must be unique per instance, so generate them with useId.

The pre-commit coverage ratchet

A Husky pre-commit hook (.husky/pre-commit) runs lint, unit tests, and a coverage check on every commit:

# .husky/pre-commit (paraphrased)
npm run lint
npm run test:unit
npm run test:coverage:unit -- --passWithNoTests

The thresholds live in jest.config.js under coverageThreshold.global:

MetricMinimum
branches10
functions5
lines10
statements10

These are intentionally low, a ratchet, not a target. The rule for agents: do not add untested logic under collected paths without either covering it with a test or excluding it from collectCoverageFrom in jest.config.js. Dropping a chunk of untestable presentational or SDK code into a measured path lowers the percentages and fails the commit. Exclude such code in config rather than letting coverage slide.

Hard rules

A few non-negotiables appear in every guidance file. Agents that ignore them tend to corrupt local state or break the build.

  • Never run npm run build while a dev server is running, concurrent writes corrupt the .next directory.
  • Keep keys optional. New integration env vars go into the Zod schema as .optional() so the app boots without them.
  • Know the safe-to-ignore warning. libs/securityLogger.ts imports fs/pathand gets pulled into the Edge middleware bundle, producing a dev-only "1 Issue" warning. It is guarded by NEXT_RUNTIME==='edge' and does not throw at runtime, pre-existing and safe to ignore.

The agent commands block

For convenience, here is the canonical commands block from AGENTS.md that agents lean on:

npm run dev          # dev server (http://localhost:3000)
npm run build        # production build (also runs ESLint)
npm run lint         # ESLint
npm run test         # unit tests (Jest)
npm run test:e2e     # Playwright end-to-end tests

MCP-aware agents: copy .mcp.json.example to .mcp.json to grant a filesystem server scoped to the project, then add more servers (GitHub, Postgres) as needed. See MCP setup.

Next steps