This is the canonical contributor and coding-agent guide for Cheatcode. It follows the agents.md convention so the same instructions work across supported tools.
These instructions apply to the entire repository. If a subdirectory later
adds its own AGENTS.md, the nearest file takes precedence for that subtree.
Explicit instructions in an issue, pull request, or user request take
precedence over repository guidance.
Cheatcode is a source-available generalist AI agent platform. Users describe an outcome, and agents can build applications, create documents and media, conduct research, and operate browser workflows.
The application is TypeScript-first and runs across Cloudflare and Vercel. Users bring their own keys for paid AI and tool providers. The live source, deployment configuration, database migrations, and package READMEs define the current architecture.
| Layer | Choice |
|---|---|
| Backend | Cloudflare Workers, Durable Objects, and Workflows |
| Frontend | Next.js 16.2.11, React 19.2.7, Tailwind CSS 4.3.2, AI Elements, and Streamdown on Vercel |
| Agent framework | Mastra 1.51.0 on Vercel AI SDK 6.0.205 |
| Sandbox | Per-user Daytona sandboxes through a bounded REST-over-fetch client |
| Browser runtime | Stagehand 3.7.0 in local mode inside the Daytona sandbox |
| Database | Supabase Postgres only, through Cloudflare Hyperdrive and Drizzle ORM 0.45.2 |
| Authentication | Clerk 7.5.19 |
| Billing | Polar 0.48.1 |
| Connected apps | Bounded Composio v3.1 REST client |
| Storage | Cloudflare R2 for generated outputs; no Supabase Storage |
| Observability | Workers Logs, Workers Tracing, and Workers Analytics Engine |
| Tooling | pnpm 11.15.0, Turborepo 2.10.5, TypeScript, and Biome 2.5.2 |
apps/
web/ Next.js application deployed on Vercel
gateway-worker/ Public Hono API, Clerk auth, and request admission
agent-worker/ Agent loop, Workflows, Durable Objects, and Daytona adapter
preview-proxy/ Authenticated proxy for Daytona previews
webhooks-worker/ Clerk, Polar, Composio, and Daytona webhook processing
packages/
agent-core/ Mastra agents, workflows, contexts, and tool registry
auth/ Clerk verification and signed-capability protocols
billing/ Polar integration, plan catalog, and entitlement policy
byok/ Vault-backed provider-key access and validation
composio/ Bounded Composio REST client
db/ Drizzle schema, migrations, client, and scoped queries
durable-storage/ Durable Object SQLite schema validation
env/ Zod-validated environment contracts
observability/ Structured logging, redaction, errors, and analytics
preview-bridge/ Shared code-server parent-frame integration
sandbox-contracts/ Provider-neutral sandbox ports and validators
skills/ Build-time skill loader and generated bundle
tsconfig/ Shared TypeScript configurations
types/ Branded IDs, Zod schemas, API contracts, and UI types
skills/ 19 curated skill sources; _shared/office is vendored
infra/ Local-development and Daytona container definitions
scripts/ Build, development, migration, and safety tooling
.github/ CI, deployment workflows, and community templates
Read the README in the app or package you are changing before moving an architectural boundary. Package READMEs document public exports, checks, and environment inputs.
Use the exact Node.js version from the root engines field and the pnpm
version from packageManager. Do not substitute npm or Yarn.
pnpm install
pnpm dev
pnpm dev:down
pnpm turbo skills:build
pnpm db:generate
pnpm db:migrate -- --dry-runpnpm dev starts the application stack through Docker Compose and requires a
complete, git-ignored .env.local. Administrative migration credentials
belong only in git-ignored .env.migrate; application processes must not load
them.
Before editing:
- Inspect
git statusand preserve unrelated work. - Confirm dependency versions in
pnpm-workspace.yamlandpnpm-lock.yaml. - Read the relevant app or package README.
- Inspect the schema, migration, or deployment source of truth before changing that boundary.
Never commit .env.local, .env.migrate, provider keys, tokens, database
credentials, or sanitized-looking copies of real secrets.
This repository intentionally has no unit-test suite. Code changes are verified with static analysis, production builds, architectural checks, and real-flow browser QA where behavior changes.
Run the complete gate chain before requesting review:
pnpm lint
pnpm typecheck
pnpm turbo build --force
pnpm deadcode
pnpm architecture:check
pnpm turbo skills:buildThese gates cover Biome, TypeScript, production builds, Knip, dependency boundaries, and the generated skill bundle. Markdown is not a substitute for running applicable code gates.
For user-visible or integration behavior, exercise the real flow directly with
agent-browser --auto-connect --session cheatcode-debug. Capture relevant
screenshots and inspect the browser console, network activity, and running
application logs. Direct browser operation is the repository's acceptance
workflow; routine contributions should not add a separate scripted product,
browser, authentication, accessibility, load, or end-to-end harness.
Every pull request should include verification notes listing the commands run, the real flows exercised, and any omitted check with its reason.
CI enforces these rules:
- Do not use explicit
any; useunknownwith narrowing or schema validation. - Do not use
console.log; use the structured logger frompackages/observability. - Application code does not read
process.envdirectly; usepackages/env. Framework configuration and operational scripts may use their documented boundary-specific loaders. - Use branded IDs for entities such as
UserId,ProjectId,ThreadId, andAgentRunId. - Use named exports. Default exports are reserved for framework-required entrypoints such as Next.js routes, Worker entries, configuration files, and Mastra definitions.
- Await, explicitly void, or chain every promise.
- Zod-validate HTTP input, environment data, webhooks, provider responses, LLM output, and other trust boundaries.
- Keep files at or below 800 lines, functions at or below 50 lines, and cognitive complexity at or below 15.
- Add JSDoc to Mastra tool definitions; tool descriptions are production prompt behavior.
Files use kebab-case, React components use PascalCase, and type-only
modules end in *.types.ts. Types and interfaces use PascalCase without
an I prefix. Functions and variables use camelCase; booleans start with
is, has, can, or should. Reserve
UPPER_SNAKE_CASE for true module-level constants.
- Access provider keys only through
packages/byok. - Decrypt a key only inside the active
withUserContext()transaction and pass the plaintext downward as a request-scoped value. - Never log plaintext keys or place them in error details, analytics, or verification notes.
- Never cache plaintext in module scope or persist it in KV, Durable Object storage, Postgres, R2, or generated artifacts.
- Provider-key rows store Vault references and non-secret fingerprints, not plaintext.
Every runtime Worker connects with one of three separate, least-privilege Postgres roles:
app_gatewayfor authenticated product reads and writes;app_agentfor run, artifact, skill, and sandbox persistence; andapp_webhooksfor provider reconciliation and lifecycle jobs.
Runtime Workers never use service_role or an administrative migration
login. Tenant-owned tables use forced row-level security and a role-specific,
signed transaction context established through withUserDb or
withUserContext. Do not bypass those transaction helpers or pass their
branded context outside its transaction.
Postgres stores product metadata and durable workflow state. User workspace files remain in Daytona, generated artifacts remain in R2, provider secrets remain in Supabase Vault, and live stream state remains in Durable Objects.
Schema modules live in packages/db/src/schema; SQL migrations and their
journal live in packages/db/drizzle.
- Migrations are append-only. Never rewrite, reorder, or remove an applied migration or its journal entry.
- Use
generate -> review -> migrate: runpnpm db:generate, inspect the generated SQL and snapshot, runpnpm db:migrate -- --dry-run, then apply withpnpm db:migrate -- --applyonly against the approved target. - Never use
drizzle-kit pushin production. - Never run migrations with a runtime Worker role. The guarded runner requires
an administrative URL plus pinned host, database, role, and Postgres system
identity from
.env.migrate. - Preserve the migration runner's advisory lock, ledger integrity checks, session timeouts, and post-migration schema assertions.
- Update
packages/db/README.mdand affected package documentation when a database boundary changes.
Function prefixes are behavioral contracts:
fetch*performs third-party network I/O.load*assembles a composite from the database.read*reads bytes or Durable Object storage.get*performs a cheap keyed lookup without a fallback chain.resolve*selects through an explicit fallback chain.assert*synchronously checks an invariant and throws.require*asynchronously fetches a prerequisite and throws when absent.ensure*idempotently creates or establishes a prerequisite.guard*wraps an operation with policy.verify*performs cryptographic verification.validate*applies business rules.
Data and lifecycle nouns also have fixed meanings:
Rowis a private raw persistence shape and keeps sourcesnake_case.Recordis an exported, mapped persistence shape incamelCase.Optionsare caller-selected, per-call inputs.Configis validated, longer-lived configuration.Contextcarries request-scoped dependencies and identity.Stateis mutable or persisted lifecycle data.
Wire and schema grammar:
- Generic wire or storage union tags use
type. - Generic in-process-only union tags use
kind. - Domain fields such as
status,provider, and literalokkeep their names. - Persisted message parts are wire data and use
type. - Schemas owned by this repository use
z.strictObject(...). - Third-party payload projections use
z.object(...).strip()intentionally. - Use
z.looseObject(...)only when the contract must preserve unknown keys. - Error codes use
<category>_<subject>_<condition>. - Event names use
<subsystem>_<object>_<past-participle>.
Architectural suffixes:
- Shared implementation modules end in
-support, not-utilsor-helpers. - UI role modules use
-controller,-view, or-model. - Worker route collections use
-routes; transport-only collections use-http-routes. - Hooks keep a
use*function name, but controller filenames omit a redundantuse-prefix.
| Need | Location |
|---|---|
| Add a web route | apps/web/src/app/<route>/page.tsx; authenticated routes use the existing (app) group |
| Add a Worker route | apps/<worker>/src/<domain>-routes.ts or <domain>-http-routes.ts, registered from that Worker's src/index.ts |
| Add an agent tool | packages/agent-core/src/tools/<domain>/<tool>.ts; expose cross-package domains through a dedicated export subpath |
| Add a Mastra agent | packages/agent-core/src/mastra/agents/<name>.ts |
| Add a Mastra workflow | packages/agent-core/src/mastra/workflows/<name>.ts |
| Add a database table | packages/db/src/schema/<domain>.ts, then generate and review a forward migration |
| Add database queries | A focused module in packages/db/src/, exported through the package's public surface |
| Add shared API types or IDs | packages/types/src/ |
| Add an environment contract | The matching app module in packages/env/src/ |
| Add a curated skill | skills/<name>/SKILL.md, with optional references/ or assets/ |
| Change skill bundling | scripts/build-skills.ts and packages/skills/ |
| Change sandbox contracts | packages/sandbox-contracts/; Daytona implementation details stay behind packages/agent-core/src/tools/code/ |
| Change Durable Object SQLite state | packages/durable-storage/ and the owning Durable Object module |
| Change preview-frame integration | packages/preview-bridge/ |
| Change deployment topology | The relevant wrangler.jsonc, apps/web/vercel.json, and CI workflow |
Curated skills are bundled into packages/skills/src/generated.ts at build
time because Workers have no runtime filesystem. The source of truth is the set
of non-underscore directories under skills/;
scripts/build-skills.ts intentionally skips _-prefixed directories.
- Do not use Node.js
fsat runtime in Workers; bundle required files at build time. - Do not use Supabase Realtime; Durable Objects own streaming.
- Do not store file bytes in Postgres; use R2 and the generated-output index.
- Do not introduce Inngest, Sentry, Langfuse, or Axiom. Use Cloudflare-native workflow and observability primitives.
- Do not use
service_rolefrom Workers. - Do not use
postgres.js; usepgfor Hyperdrive compatibility. - Do not bypass
packages/byokfor provider-key access. - Do not expose Cheatcode as an MCP server; Cheatcode consumes MCP tools.
- Do not add hard step, token, or cost ceilings to agent loops. Semantic completion controls the loop; cancellation and timeouts remain operational safeguards.
- Do not introduce a new vendor without an explicit architecture decision and matching documentation and configuration changes.
When an architectural boundary moves, update the source, deployment
configuration, migration or schema, package README, and public exports in the
same change. Check pnpm-workspace.yaml and pnpm-lock.yaml for dependency
truth; packages/db/src/schema and packages/db/drizzle for database truth;
and Worker wrangler.jsonc, apps/web/vercel.json, and .github/workflows
for deployment truth.
Commit messages and pull request titles use Conventional Commits:
feat(agent): add wide research workflow
fix(sandbox): handle snapshot restore on cold start
refactor(db): split provider keys by domain
docs(architecture): document a package boundary
Keep the subject on one line and below 72 characters. Pull requests should explain the reason for the change, call out architecture or migration effects, and include the verification notes described above.
- Architecture or ownership: start with the relevant app or package README and its public exports.
- Dependency or version question: inspect
pnpm-workspace.yamlandpnpm-lock.yaml. - Database or RLS question: read
packages/db/README.md,packages/db/src/schema,packages/db/drizzle, andscripts/database-operation-safety.ts. - Sandbox lifecycle issue: start with
apps/agent-worker/src/durable-objects/project-sandbox-lifecycle.ts,project-sandbox-runtime-handle.ts, andpackages/agent-core/src/tools/code/daytona-client.ts. - Authentication issue: inspect
packages/auth/and the gateway's authentication middleware. - Agent-run streaming issue: inspect
apps/agent-worker/src/durable-objects/agent-run-stream-driver.ts,agent-run-mastra-stream.ts, andapps/agent-worker/src/streaming/ui-message-stream.ts. - Skill activation issue: inspect the skill frontmatter
description, thenscripts/build-skills.tsandpackages/skills/. - Strict TypeScript surprise: check
exactOptionalPropertyTypesandnoUncheckedIndexedAccessbefore weakening a type. - Worker module resolution issue: verify package exports and the Worker's
nodejs_compatsetting inwrangler.jsonc.
Contributions to repository-owned code are made under the root LICENSE.
First-party excluded assets and third-party materials have separate terms
described in NOTICE; do not assume the repository license applies to them.