Monorepo for the Kilo Code cloud platform.
apps/web/ Next.js web application (Vercel)
apps/mobile/ React Native mobile app
services/ Cloudflare Worker services (kiloclaw, cloud-agent-next, etc.)
packages/ Shared libraries (db, trpc, worker-utils, etc.)
dev/ Local development tooling (tmux dashboard, env sync, docker-compose)
scripts/ CI and one-off scripts
- Package manager: pnpm (version pinned in
package.jsonpackageManagerfield) - Database schema:
packages/db/src/schema.ts - Migrations:
packages/db/src/migrations/ - tRPC routers:
apps/web/src/routers/ - Env vars:
.env.localat repo root (pulled viavercel env pull)
After making changes, verify your work. At minimum run typecheck; run the full suite when appropriate. Always run pnpm format before committing — CI will reject unformatted code.
| Command | What it checks |
|---|---|
pnpm typecheck |
TypeScript type checking across all packages |
pnpm lint |
Lint all source files |
pnpm test |
Jest test suite |
pnpm validate |
All three above in sequence |
pnpm format |
Auto-format with oxfmt |
Target a specific test file: pnpm test -- <path>. Run tests for a specific service: pnpm --filter <package> test.
Before running tests, ensure the test database is running. If there is no active Postgres instance, run pnpm test:db first — this starts the Postgres container and applies migrations. You can check whether Postgres is already running with docker compose -f dev/docker-compose.yml ps postgres.
- Prefer
typeoverinterface. - Avoid
ascasts and!non-null assertions — usesatisfiesor flow-sensitive typing. - Avoid mocks in tests; assert on results or check the database for side effects.
- Prefer clear names over comments. Only comment things not obvious in context.
- When the linter flags an unused variable, investigate the root cause — do not blindly prefix with
_. - Use existing dependencies before implementing custom solutions. Check
package.jsonfor what's available.
Schema is in packages/db/src/schema.ts. Migrations live in packages/db/src/migrations/ and are generated by drizzle-kit via pnpm drizzle generate.
- Never hand-write or hand-edit migration SQL, snapshots, or the journal. Always use
pnpm drizzle generateto produce migrations from the schema. - Backfill statements (UPDATE/INSERT) can be appended to a generated migration file after the generated DDL, using
--> statement-breakpointseparators. - After a rebase that conflicts on migration files: delete all migration files, snapshots, and journal entries that were added on the branch, then re-run
pnpm drizzle generateto regenerate a clean migration from the current schema diff. Re-append any backfill SQL afterward. - Prefer a single migration per feature branch when the code has not yet been deployed to production. If multiple migrations accumulated during development, squash them by deleting all branch-local migrations and regenerating.
When adding PII (email, name, IP address, etc.) to the database — whether as a new table or a new column — you must also update the GDPR soft-delete flow in softDeleteUser (apps/web/src/lib/user.ts) and add a corresponding test in apps/web/src/lib/user.test.ts.
Never log tokens, credentials, auth headers, cookies, or webhook secrets. Use redactSensitiveHeaders from @kilocode/worker-utils/redact-headers when headers must be stored or logged. Do not enable sendDefaultPii or attachRpcInput in Sentry config.
When writing implementation plans, always save them to the .plans/ directory at the repo root. This is the designated location for all planning documents — do not place them elsewhere in the repo.
- Never use
--force,--no-verify, or any other flag that bypasses git hooks or safety checks without explicit user approval. - If a hook or check fails, diagnose the issue and either fix it or ask the user how to proceed — do not silently skip it.
- Format:
type(scope): <description>(e.g.,feat(auth): add SSO login) - Common types:
feat,fix,refactor,docs,test,chore,ci,style,perf - Imperative mood, under 72 characters, no trailing period.
Follow the PR template in .github/pull_request_template.md. Every description must include four sections in order:
## Summary— What changed and why. Outcome-focused, call out architectural changes.## Verification— Checks you actually ran. Never fabricate verification steps.## Visual Changes— Before/after screenshots, orN/A.## Reviewer Notes— Risk areas, tricky logic, rollout notes, orN/A.
Do not leave HTML comments from the template. Review all commits on the branch when writing the summary.
- Create PRs as ready for review by default. Only use
--draftif explicitly requested. - When assigning PRs or issues, resolve the GitHub username with
gh api user --jq '.login'. Never guess usernames.
Business-rule specs live in .specs/. Before making any changes to a domain covered by a spec — including bug fixes, new features, refactors, or reviews — you must first read the relevant spec.
| Spec | Governs |
|---|---|
.specs/kiloclaw-billing.md |
KiloClaw billing, pricing, invoicing, usage metering, payment flows |
.specs/kiloclaw-datamodel.md |
KiloClaw data model — instance/subscription tables, invariants |
.specs/kiloclaw-controller.md |
KiloClaw controller/machine lifecycle, bootstrap, Docker image |
.specs/team-enterprise-seat-billing.md |
Team and Enterprise seat billing, subscription management |
.specs/impact-affiliate-tracking.md |
Impact.com affiliate conversion tracking |
When using subscriptionSchedules.create() with from_subscription, Stripe prohibits setting metadata in the same call (it copies metadata from the subscription automatically). Set custom metadata (e.g., origin tags) in the subsequent subscriptionSchedules.update() call instead.