Skip to content

Latest commit

 

History

History
111 lines (75 loc) · 6.7 KB

File metadata and controls

111 lines (75 loc) · 6.7 KB

AGENTS.md

Repo Structure

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.json packageManager field)
  • Database schema: packages/db/src/schema.ts
  • Migrations: packages/db/src/migrations/
  • tRPC routers: apps/web/src/routers/
  • Env vars: .env.local at repo root (pulled via vercel env pull)

Verification

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.

Coding Standards

  • Prefer type over interface.
  • Avoid as casts and ! non-null assertions — use satisfies or 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.json for what's available.

Database Migrations

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 generate to produce migrations from the schema.
  • Backfill statements (UPDATE/INSERT) can be appended to a generated migration file after the generated DDL, using --> statement-breakpoint separators.
  • 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 generate to 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.

GDPR & PII

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.

Logging & Sensitive Data

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.

Plans

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.

Git Safety

  • 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.

Pull Requests

Titles

  • 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.

Descriptions

Follow the PR template in .github/pull_request_template.md. Every description must include four sections in order:

  1. ## Summary — What changed and why. Outcome-focused, call out architectural changes.
  2. ## Verification — Checks you actually ran. Never fabricate verification steps.
  3. ## Visual Changes — Before/after screenshots, or N/A.
  4. ## Reviewer Notes — Risk areas, tricky logic, rollout notes, or N/A.

Do not leave HTML comments from the template. Review all commits on the branch when writing the summary.

Workflow

  • Create PRs as ready for review by default. Only use --draft if explicitly requested.
  • When assigning PRs or issues, resolve the GitHub username with gh api user --jq '.login'. Never guess usernames.

Specs

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

Stripe Subscription Schedules

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.