diff --git a/.agents/skills/tanstack-promptable-fullstack-app-template/SKILL.md b/.agents/skills/tanstack-promptable-fullstack-app-template/SKILL.md index 6cccc5d..cbf5dad 100644 --- a/.agents/skills/tanstack-promptable-fullstack-app-template/SKILL.md +++ b/.agents/skills/tanstack-promptable-fullstack-app-template/SKILL.md @@ -8,84 +8,111 @@ description: 'Use when scaffolding a new TanStack Start project, adding domain instead of training data. Project: TanStack AI-Promptable Full-Stack Template. Triggers on "fullstack template", "TanStack Start project", "repository pattern", "interface-first", "new app scaffold", "nested routes", "layout - route", "beforeLoad", "tanstack cli".' + route", "beforeLoad", "tanstack cli", "tanstack intent", "package skills".' --- > This file is generated from `skills/src/*.skill.yaml`. Do not edit manually. # TanStack Fullstack Pattern -An interface-first fullstack architecture built on TanStack Start. The pattern defines clear interface boundaries between layers -- interfaces are rigid, implementations are swappable. - -> **Companion documentation:** In repositories built from this template, [AGENTS.md](https://github.com/carlosvin/tanstack-fullstack-ai-template/blob/main/AGENTS.md) holds the project handbook -- file structure, Mantine styling, auth snippets, Biome, testing/E2E commands, and the full validation checklist. This skill focuses on the architectural contract; refer to AGENTS.md for operational detail. - -## Pattern Overview - -- Zero-config development with seed implementations and swappable service layers -- AI promptability by exposing every repository method (reads and writes) as tools -- End-to-end type safety from schema-first definitions with explicit layer boundaries - -## Rigid Rules (Must Follow) - -1. Interface-first services: every external service (database, AI, observability) is accessed through an interface. -2. End-to-end type safety via schemas: every boundary uses a Zod/ArkType schema as the type source (`z.infer<>`). Never hand-write `type` for wire data. Service interfaces (`ReadRepository`, `AIAdapterService`, etc.) are hand-written contracts -- they define behaviour, not wire shapes. -3. Three schema layers: repository (DB-shaped), server-function / AI-tool (API-shaped, shared), router search-param (URL-shaped). Mappers translate between layers. -4. Repository contracts use repository-layer schemas only. -5. Server functions and AI tools share the same tools-layer schemas (`.inputValidator(Schema)` / `toolDefinition({ inputSchema })`). Both parse with `Schema.parse(args)`. -6. AI and UI interact only with tools-layer schemas; they must not depend directly on repository schemas. -7. Loaders-first data fetching: fetch route data in loaders through server functions. -8. URL-as-state: filters, tabs, selections live in URL search params via `validateSearch` (Zod/ArkType). Use `loaderDeps` to feed validated search into loaders. -9. Middleware chain: auth is global middleware, invalidation runs on mutation server functions. -10. Mutation pattern: POST server functions chain `.middleware([requireAuthMiddleware, invalidateMiddleware])`, return domain data or throw `HttpError`. Callers normalize errors: UI via `processResponse()`, AI tools via `safeToolHandler()` / `createSafeServerTool()`. -11. Query pattern: GET server functions throw on failure for centralized error handling. When possible, use static server functions for performance improvements. -12. Maximize AI tool coverage: expose **every** repository method (reads and writes) via `createSafeServerTool()` so failures return `{ error, code }`. If a server function exists, it gets a tool. -13. Router capabilities as AI client tools: expose `router.navigate()` and `router.invalidate()` as client tools via `toolDefinition()`. -14. AI system prompt context: `buildSystemPrompt()` injects three context blocks into every AI chat request. (a) **Current User** — name, email, role from the auth middleware context (server-side; no client round-trip needed). (b) **Browser Context** — timezone, locale, and current date/time from `browserContext` sent by `ChatDrawer` (client-side). (c) **Current Location** — pathname, search params, and full URL from `browserContext`; route patterns (e.g. `/tasks/$taskId`) are matched to resolve dynamic segments. The navigation manifest mirrors `routeTree.gen.ts` and lists each route's search params. When adding routes, update `navigationManifest.ts` and add pattern-matching in `buildSystemPrompt()` for new dynamic segments. -15. JSDoc on exports: every exported function, interface, type, and constant gets a JSDoc comment stating *what* and *why*. -16. AI chat drawer: render the AI chat in a Mantine `Drawer` (`position="right"`, `size="lg"`) mounted at the root layout level so messages persist across navigation. `useDisclosure` controls open/close. See AGENTS.md section 8 for rendering details. -17. AI renders rich markdown: use `react-markdown` + `remark-gfm` for assistant messages. Tables/code styled via `.markdown` CSS Module with Mantine CSS variables. Internal links render as TanStack Router `Link` with `preload="intent"` (client-side navigation without losing chat). External links open in a new tab. The AI uses markdown links in replies (e.g. `[Pending tasks](/tasks?status=pending)`). -18. Thin routes: route files contain only route config (`createFileRoute`, `validateSearch`, `loaderDeps`, `loader`, `component`). Page UI lives in `src/components/PageName/PageName.tsx`. -19. Promptable by default: check AI availability at the root loader level via `getAIAvailability()`. Only render the chat trigger and drawer when AI is configured — no disabled-state fallback. -20. Icon library: use `lucide-react` as the default icon library. Keep one icon library per project for visual consistency. -21. Structured logging: use `pino` for all server-side logging instead of `console.*`. Configure `@sentry/pino-transport` so error-level logs are automatically forwarded to Sentry when `VITE_SENTRY_DSN` is set. The logger singleton lives in `src/services/logger.ts`. -22. Build-time app version: extract the semver version from `package.json` at build time via Vite `define` and expose it as `__APP_VERSION__`. Inject it into Sentry (`release`), the pino logger (`version` field), and any other observability tool. -23. Latest dependencies: install and keep dependencies at latest compatible versions. Never pin exact versions unless a known incompatibility exists. Use `pnpm add ` (no version suffix); run `pnpm outdated` and `pnpm update` to align the lockfile. -24. Ask for LLM provider: when scaffolding a new project or when the user's LLM preference is unclear, ask which provider they want before writing the adapter. Install only the chosen `@tanstack/ai-*` adapter package and configure matching env vars. Default is `@tanstack/ai-openai`; do not assume OpenAI without asking. See AGENTS.md section 8 for the full provider table. -25. Generate the system prompt: when scaffolding a new app, ask the user about their domain — entities, capabilities, and permissions — then generate a tailored `BASE_SYSTEM_PROMPT` in `src/routes/api/chat.ts` with six sections (Capabilities, Data Model, Links and navigation, Mutations and data refresh, Permissions and errors, Guidelines). Do not reuse the template's task-management prompt. `buildSystemPrompt()` composes this base with dynamic context (rule 14) and the navigation manifest. `chat()` from `@tanstack/ai` receives it via `systemPrompts: string[]`. See AGENTS.md section 8 "System Prompt Generation" for the full template. -26. Repository-resolved authorization: `authMiddleware` extracts JWT claims **and** calls a repository method (e.g. `getReadRepository().getUserAccess(email)`) to enrich `AuthContext` with application-defined access data — roles, group memberships, owned scopes, superuser flags. Downstream guards (`requireAuth`, `requireGroup`, any app-specific `requireOwnerOf`) and AI tools read this enriched context so UI and AI see the same permission signals. Authorization checks live **inside** server-function handlers (not only in UI components), so permissions are enforced regardless of whether the caller is the UI, the AI, or a direct HTTP client. -27. Write attribution via traceability context: `WritableRepository` methods accept an optional `TraceabilityContext` (`createdBy`, `createdDate`, `lastModifiedBy`, `lastModifiedDate`) built from the authenticated identity. Mutation server-function handlers construct it from `ctx.context.user.email` (available after `requireAuthMiddleware`) and pass it to the repository. Seed and production implementations apply it consistently. This gives UI and AI callers the same audit trail without duplicating logic at each call site. -29. Explicit agent loop depth: configure `agentLoopStrategy: maxIterations(N)` explicitly on the `chat()` call (default N=10). This caps the number of consecutive tool-calling iterations the AI can run before returning a final answer, which bounds latency, cost, and infinite-loop risk. Tune N only after measuring; do not rely on the framework default. -30. Public runtime config bridge: expose non-secret runtime config (Sentry DSN, environment name, feature flags) via a GET server function `getPublicEnv()` and inline the result as `window.__ENV__` in the root `RootDocument` using a small `