name: add-feature description: "Use when the user asks to add a feature, implement functionality, or build something spanning database, API, and frontend. ALWAYS use for multi-layer feature work."
Add Feature End-to-End
Implement feature described in $ARGUMENTS across the full stack.
Step 0: Decide Scope
Before writing code, answer:
- CE, EE, or both? If both → use
hooksFactory(CE default + EE override inapp.ts) - Need a plan flag? → Add to
PlatformPlanentity +LicenseKeyEntitytype + plan constants in shared - Need a Permission? → Add to
Permissionenum in shared - Affects billing/quotas? → Add enforcement in controller
- Must work embedded? → Check
EmbeddingStatein frontend - Project-scoped or platform-scoped? → Filter queries accordingly
Step 1: Shared Types (packages/shared)
- Define Zod schemas +
z.infertypes insrc/lib/{domain}/ - Export from
src/index.tsbarrel - Bump version in
package.json(patch for fix, minor for new export)
Step 2: Server (packages/server/api)
Read .agents/features/<module-name>.md first (e.g. .agents/features/tables.md for the tables module).
- Entity: Use
EntitySchema+BaseColumnSchemaPart+ApIdSchema. Seetables/table/table.entity.ts. - Register entity: Add to
getEntities()indatabase-connection.ts(REQUIRED — TypeORM doesn't auto-discover) - Migration: Read playbook → create class → import in
postgres-connection.ts→ add togetMigrations()→ handle PGlite - Service: Factory
(log: FastifyBaseLogger) => ({...})if logging needed, plain object otherwise. Seetables/table/table.service.ts. - Controller:
FastifyPluginAsyncZod. Route configs AFTER controller.securityAccessrequired. Seetables/table/table.controller.ts. - Project ownership: Add
entitiesMustBeOwnedByCurrentProjecthook if returning project-scoped data - Module: Register in
app.ts(CE or EE section) - EE-only: Put in
src/app/ee/, gate withplatformMustHaveFeatureEnabled((p) => p.plan.myFlag) - Side effects: Separate
*-side-effects.tsfile if mutations trigger events/webhooks
Step 3: Worker (if queued work needed)
- Add to
SystemJobNameorWorkerJobTypeenum in shared - Add handler, register in
app.tsviasystemJobHandlers.registerJobHandler()
Step 4: Frontend (packages/web)
- Feature folder:
src/features/{feature}/api/,hooks/,components/ - API client: See
features/tables/api/tables-api.ts - Hooks: See
features/tables/hooks/table-hooks.ts - Route:
React.lazy()+ProjectRouterWrapper()+RoutePermissionGuard+SuspenseWrapper - Translations:
packages/web/public/locales/en/translation.jsononly - Feature flags:
flagsHooks.useFlag()or<FlagGuard>
Step 5: Tests
- API test:
packages/server/api/test/integration/ce/{feature}.test.ts - Use
setupTestEnvironment()+createTestContext(app)
Step 6: Verify
npm run lint-dev
npm run test-api