create-pracht 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,146 @@
1
+ ---
2
+ name: pracht-debug
3
+ version: 1.3.0
4
+ description: |
5
+ Pracht framework-aware debugging. Systematically investigates route matching,
6
+ loader/API route errors, rendering issues, middleware, API routes, HMR, and build
7
+ problems. Uses pracht's architecture knowledge to find root causes fast.
8
+ Use when asked to "debug this", "fix this bug", "why is this broken",
9
+ "blank page", "hydration mismatch", or "404 on my route".
10
+ Proactively suggest when the user reports errors or unexpected behavior
11
+ in a pracht application.
12
+ allowed-tools:
13
+ - Bash
14
+ - Read
15
+ - Write
16
+ - Edit
17
+ - Grep
18
+ - Glob
19
+ - AskUserQuestion
20
+ ---
21
+
22
+ # Pracht Debug
23
+
24
+ Framework-aware debugging for pracht applications — a full-stack Preact framework built on Vite.
25
+
26
+ The user will describe a symptom (error, unexpected behavior, blank page, etc.). Investigate systematically using the checklist below, stopping when you find the root cause.
27
+
28
+ Before deep manual inspection, prefer running `pracht verify` (add `--changed` to scope the checks to git-changed files) for a fast agent loop or `pracht doctor` when the problem could be caused by broader broken app wiring or missing files.
29
+ When another agent/tool needs the framework's resolved graph, prefer `pracht inspect routes --json`, `pracht inspect api --json`, or `pracht inspect build --json` over reconstructing it from source files. Prerequisites: `pracht inspect` needs the pracht plugin registered in the project's vite config, and `pracht inspect build` needs a prior `pracht build`.
30
+ If the pracht MCP server is registered (docs/MCP.md), prefer the `inspect_routes`/`inspect_api`/`doctor`/`verify` MCP tools over shelling out — same payloads, structured results.
31
+ While the dev server is running, `GET /_pracht` serves a devtools page with the same resolved route/API graph (raw JSON at `/_pracht.json`) — useful when you have a browser or `curl` handy but no CLI access. Dev SSR responses also carry a `Server-Timing` header (`mw`, `loader`, `render` durations in ms) — check it in the browser Network panel or with `curl -sI` to see which phase makes a route slow.
32
+
33
+ ## Iron Law
34
+
35
+ **NO FIXES WITHOUT ROOT CAUSE INVESTIGATION FIRST.**
36
+
37
+ ## Debugging Checklist
38
+
39
+ Work through these in order, stopping when you find the root cause:
40
+
41
+ ### 1. Route matching
42
+
43
+ - Run `pracht verify --changed` first if you want a cheap changed-file confidence check.
44
+ - Run `pracht doctor` if the route might be missing, miswired, or pointing at a missing module across the project.
45
+ - For machine-readable route wiring, run `pracht inspect routes --json`. With a running dev server, `curl http://localhost:5173/_pracht.json` returns the same graph.
46
+ - Read `src/routes.ts` — is the route defined? Is the path correct?
47
+ - Check for typos in file paths (the manifest uses relative paths like `"./routes/home.tsx"`).
48
+ - For dynamic segments, verify bracket syntax: `route("/users/:id", ...)` in manifest, `[id].ts` in filenames.
49
+ - Grep for the route path across the manifest and check `matchAppRoute()` logic if needed.
50
+
51
+ ### 2. Typed route/link issues
52
+
53
+ - If `<Link route="...">`, `href("...")`, or route-object `useNavigate()` fails to typecheck, run `pracht typegen --check` to detect stale generated files.
54
+ - Run `pracht inspect routes --json` and confirm the route id exists. If it is a fallback id, remember path changes can rename it.
55
+ - Check generated `src/pracht.d.ts` for inferred params. `:id`, `*`, and `:path*` params are required; extra params should fail at typecheck time.
56
+ - If runtime navigation throws `Unknown pracht route id "..."`, in dev the error includes a `Did you mean "..."?` suggestion and the list of registered route ids (production builds tree-shake this and throw the bare error) — check for a typo first, then ensure `pracht typegen` was run and the component is rendered inside the pracht route tree.
57
+ - For unexpected URLs, reproduce with `href(routeId, options)` and compare against the route's resolved path and params.
58
+
59
+ ### 3. Loader / API route errors
60
+
61
+ - For slow pages, read the dev `Server-Timing` response header (`mw`/`loader`/`render` in ms) to see which phase dominates before reading code.
62
+ - Read the route module's `loader` function or the matching API route handler.
63
+ - Check that `loader` returns serializable data (no functions, no circular refs).
64
+ - Check that API route handlers return `Response` objects and branch on `request.method` when using a default export.
65
+ - Look for unhandled promise rejections or thrown errors.
66
+ - Verify `LoaderArgs` destructuring matches what the framework provides: `{ request, params, context, signal, url, route }`.
67
+
68
+ ### 4. Rendering issues
69
+
70
+ - **Blank page**: Check if the route has `render: "spa"` (no SSR content expected) vs `"ssr"`.
71
+ - **Hydration mismatch**: In dev, pracht surfaces a fixed-position red banner at the top of the page listing each mismatched component (via Preact's `options.__m` hook). Compare server-rendered HTML vs client component output. Common causes:
72
+ - Date/time rendering differences
73
+ - Browser-only APIs used during SSR (`window`, `document`, `localStorage`)
74
+ - Conditional rendering based on client state
75
+ - **Missing shell**: Referencing an unregistered shell name throws at manifest resolution — `Unknown shell "..." for route "...". Did you mean "..."? Registered shells: ...` — and shows up in the dev error overlay as soon as the server loads the manifest. Verify the shell is registered in `defineApp({ shells: { ... } })` and assigned to the route/group.
76
+ - **404 page**: Route not matched — check manifest wiring (step 1). In `pracht dev`, unmatched navigations render a dev-only 404 page listing every registered route with its render mode; compare the requested path against that table. The route table is also printed on dev-server startup and available via `pracht inspect routes`. Apps that declare `defineApp({ notFound })` render their own 404 page instead (in dev and production alike), so the route table is not shown — check `pracht inspect routes` directly. A 404 on a URL you *do* expect to work usually means the loader threw `notFound()`, not that matching failed.
77
+
78
+ ### 5. Middleware issues
79
+
80
+ - Verify middleware is registered in `defineApp({ middleware: { ... } })`. An
81
+ unregistered name (on a route, group, or `api.middleware`) throws at manifest
82
+ resolution — `Unknown middleware "..." for route "...". Did you mean "..."? Registered middleware: ...`
83
+ - Verify middleware is applied to the route/group: `middleware: ["name"]`.
84
+ - Middleware is wrap-around: it must always return a `Response`, either by
85
+ calling `await next()` (to continue down the chain) or short-circuiting.
86
+ - Common bugs:
87
+ - Forgetting `return next()` → `Middleware "..." did not return a Response`
88
+ - Calling `next()` twice → `Middleware "..." called next() multiple times`
89
+ - Mutating a non-object `context` → mutations don't propagate; always pass
90
+ an object as the request context.
91
+ - Middleware runs server-side only, wrapping loaders and API handlers.
92
+
93
+ ### 6. API route issues
94
+
95
+ - API routes live in `src/api/` and are auto-discovered (no manifest entry needed).
96
+ - For machine-readable API inventory, run `pracht inspect api --json`.
97
+ - File path maps to URL: `src/api/health.ts` → `/api/health`, `src/api/users/[id].ts` → `/api/users/:id`.
98
+ - Each file exports named HTTP method handlers (`GET`, `POST`, etc.) or one default handler.
99
+ - Missing method handler → 405 response when there is no default handler.
100
+ - Default handlers receive the same route args and can branch on `request.method`.
101
+ - Handlers must return `Response` objects.
102
+
103
+ ### 7. Vite plugin / HMR issues
104
+
105
+ - Check `vite.config.ts` — is `pracht()` plugin included?
106
+ - Virtual modules: `virtual:pracht/client` (hydration), `virtual:pracht/server` (SSR), `virtual:pracht/islands-client` (islands hydration).
107
+ - HMR: changes to `src/routes.ts` restart the dev server (`server.restart()`, not a browser-side full reload); changes to route/shell/middleware/API/server/islands files invalidate the server module.
108
+ - If HMR seems broken, check that the file is in one of the watched directories (`src/routes/`, `src/shells/`, `src/middleware/`, `src/api/`, `src/server/`, `src/islands/`).
109
+
110
+ ### 8. Build / deployment issues
111
+
112
+ - `pracht build` runs client + server builds, then prerenders SSG/ISG routes.
113
+ - `pracht preview` builds and serves the production output locally (Node runs `dist/server/server.js`, Cloudflare delegates to `wrangler dev`).
114
+ - `pracht inspect build --json` reports the resolved adapter target plus client/CSS/JS manifests from the latest build output (requires a prior `pracht build`).
115
+ - Check `dist/client/` for client assets and `dist/server/` for server bundle.
116
+ - ISG manifest: `dist/server/isg-manifest.json`. On Cloudflare the build also copies it to `dist/client/_pracht/isg.json` for the worker runtime to read via the assets binding.
117
+ - Adapter mismatch: ensure `pracht({ adapter: nodeAdapter() })` or `cloudflareAdapter()` matches deployment target.
118
+
119
+ ## Key Files
120
+
121
+ | File | Purpose |
122
+ | --------------------- | ----------------------------------------------------- |
123
+ | `src/routes.ts` | App manifest — all route/shell/middleware definitions |
124
+ | `vite.config.ts` | Vite config with `pracht()` plugin |
125
+ | `src/routes/*.tsx` | Route modules (loader, Component) |
126
+ | `src/shells/*.tsx` | Shell layout components |
127
+ | `src/middleware/*.ts` | Server-side middleware |
128
+ | `src/api/*.ts` | API route handlers |
129
+
130
+ ## Framework Internals
131
+
132
+ - `handlePrachtRequest()` dispatches: API routes → middleware → loader → render → HTML assembly
133
+ - Route state JSON: returned when `x-pracht-route-state-request` header is present (client-side navigation)
134
+ - Hydration state: injected as `window.__PRACHT_STATE__` in the HTML
135
+ - Client router: `initClientRouter()` intercepts link clicks and fetches route state JSON
136
+
137
+ ## Rules
138
+
139
+ 1. Always read the relevant source files before diagnosing.
140
+ 2. Start with the most likely cause based on the symptom, not a full audit.
141
+ 3. When you find the root cause, explain _why_ it breaks and fix it.
142
+ 4. If wiring looks suspicious, run `pracht verify` first, then `pracht doctor` if you need the full-project view. If running the dev server or tests would help, do so (`pracht dev`, `pnpm test`, `pnpm e2e`).
143
+ 5. After fixing, verify the fix works (run relevant test or check dev server output).
144
+ 6. Never say "this should fix it." Verify and prove it.
145
+
146
+ $ARGUMENTS
@@ -0,0 +1,208 @@
1
+ ---
2
+ name: pracht-deploy
3
+ version: 1.1.0
4
+ description: |
5
+ Pracht deployment guide. Walks through adapter configuration, building, and
6
+ deploying to Node.js, Cloudflare Workers, or Vercel. Handles wrangler config,
7
+ Docker and production checklist.
8
+ Use when asked to "deploy", "set up deployment", "configure adapter",
9
+ "deploy to cloudflare", "deploy to vercel", or "production build".
10
+ allowed-tools:
11
+ - Bash
12
+ - Read
13
+ - Write
14
+ - Edit
15
+ - Grep
16
+ - Glob
17
+ - AskUserQuestion
18
+ ---
19
+
20
+ # Pracht Deploy
21
+
22
+ Guided adapter setup and deployment for pracht applications.
23
+
24
+ ## Step 1: Determine the target
25
+
26
+ Read `vite.config.ts` and `package.json` first — don't assume the current adapter.
27
+ Ask the user where they want to deploy if not already clear from their message.
28
+
29
+ If the pracht MCP server is registered (docs/MCP.md), prefer the `inspect_build`/`doctor`/`verify` MCP tools over shelling out. Note: `inspect_build` (like `pracht inspect build`) needs a prior `pracht build`, and `pracht inspect` requires the pracht plugin registered in the vite config.
30
+
31
+ ## Supported Adapters
32
+
33
+ | Adapter | Package | Status |
34
+ | ------------------ | ---------------------------- | ------ |
35
+ | Node.js | `@pracht/adapter-node` | Stable |
36
+ | Cloudflare Workers | `@pracht/adapter-cloudflare` | Stable |
37
+ | Vercel | `@pracht/adapter-vercel` | Stable |
38
+
39
+ ---
40
+
41
+ ## Node.js Deployment
42
+
43
+ ### Setup
44
+
45
+ 1. Ensure `@pracht/adapter-node` is installed.
46
+ 2. In `vite.config.ts`:
47
+ ```ts
48
+ import { pracht } from "@pracht/vite-plugin";
49
+ import { nodeAdapter } from "@pracht/adapter-node";
50
+ export default { plugins: [pracht({ adapter: nodeAdapter() })] };
51
+ ```
52
+
53
+ ### Build
54
+
55
+ ```bash
56
+ pracht build
57
+ ```
58
+
59
+ Produces:
60
+
61
+ - `dist/client/` — static assets (JS, CSS, prerendered HTML)
62
+ - `dist/server/server.js` — Node server entry
63
+ - `dist/server/isg-manifest.json` — ISG revalidation config (if ISG routes exist)
64
+ - `dist/client/.vite/manifest.json` — asset manifest for script/style injection
65
+
66
+ ### Run
67
+
68
+ ```bash
69
+ node dist/server/server.js
70
+ ```
71
+
72
+ Port 3000 by default. For a local production smoke test, `pracht preview` builds and runs the server in one step (`--port <n>`, `--skip-build` to reuse an existing build). For production: reverse proxy (nginx, Caddy), process manager (PM2, systemd), `NODE_ENV=production`.
73
+
74
+ ### Docker
75
+
76
+ ```dockerfile
77
+ FROM node:22-alpine
78
+ WORKDIR /app
79
+ COPY dist/ dist/
80
+ COPY package.json .
81
+ EXPOSE 3000
82
+ CMD ["node", "dist/server/server.js"]
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Cloudflare Workers Deployment
88
+
89
+ ### Setup
90
+
91
+ 1. Ensure `@pracht/adapter-cloudflare` is installed.
92
+ 2. In `vite.config.ts`:
93
+ ```ts
94
+ import { pracht } from "@pracht/vite-plugin";
95
+ import { cloudflareAdapter } from "@pracht/adapter-cloudflare";
96
+ export default { plugins: [pracht({ adapter: cloudflareAdapter() })] };
97
+ ```
98
+
99
+ ### Build & Deploy
100
+
101
+ ```bash
102
+ pracht build
103
+ npx wrangler deploy
104
+ ```
105
+
106
+ To smoke-test the built worker locally first, run `pracht preview` — it builds and then delegates to `wrangler dev`, which serves the wrangler config's `main` entry, `dist/server/worker.js`.
107
+
108
+ ### Wrangler Configuration
109
+
110
+ ```jsonc
111
+ // wrangler.jsonc
112
+ {
113
+ "name": "my-pracht-app",
114
+ "main": "dist/server/worker.js",
115
+ "compatibility_date": "2024-01-01",
116
+ "assets": {
117
+ "binding": "ASSETS",
118
+ "directory": "dist/client",
119
+ "run_worker_first": true,
120
+ },
121
+ }
122
+ ```
123
+
124
+ `"binding": "ASSETS"` and `"run_worker_first": true` are required. Without the binding, the worker's `env.ASSETS` resolves to nothing and the runtime silently falls back to `null` — headers and ISG manifests load empty, so SSG serving, ISG revalidation, and per-route headers all silently no-op. The canonical config lives at `examples/cloudflare/wrangler.jsonc`. If you rename the binding with `assetsBinding` (below), the wrangler `binding` value must match.
125
+
126
+ ### Bindings (KV, D1, R2)
127
+
128
+ ```ts
129
+ export async function loader({ context }: LoaderArgs) {
130
+ const value = await context.env.MY_KV.get("key");
131
+ return { value };
132
+ }
133
+ ```
134
+
135
+ ### Custom Assets Binding
136
+
137
+ ```ts
138
+ pracht({ adapter: cloudflareAdapter({ assetsBinding: "STATIC" }) });
139
+ ```
140
+
141
+ ### ISG via Workers Caching
142
+
143
+ ISG works out of the box: without any cache option, the default worker-managed path serves the build-time snapshot, detects staleness, and regenerates pages in the background via the Workers Cache API — per colo — and `POST /__pracht/revalidate` triggers on-demand regeneration. Enabling `cache: true` moves ISG from that per-colo worker-managed path to edge-tier Workers Caching, on both sides:
144
+
145
+ ```ts
146
+ pracht({ adapter: cloudflareAdapter({ cache: true }) });
147
+ ```
148
+
149
+ ```jsonc
150
+ // wrangler.jsonc
151
+ { "cache": { "enabled": true } }
152
+ ```
153
+
154
+ Before enabling it, audit ISG URLs for unbounded query strings. Workers Caching
155
+ keys the exact path and query string, including parameter order and trailing
156
+ slashes; use a bounded query allowlist/canonical redirect or an uncached gateway
157
+ with a pathname-only `cf.cacheKey`, and normalize `Accept` there for routes that
158
+ export markdown. See `docs/ADAPTERS.md#cache-key-cardinality`.
159
+
160
+ Time-revalidated ISG pages then render on demand, are cached at the edge for
161
+ their `revalidate` window (stale pages served instantly while the Worker
162
+ re-renders in the background), and can be purged early with `purgeCache()` from
163
+ `@pracht/adapter-cloudflare/cache`. Webhook-only ISG routes keep their
164
+ build-time snapshots and the worker-managed path either way.
165
+
166
+ ---
167
+
168
+ ## Vercel Deployment
169
+
170
+ ### Setup
171
+
172
+ 1. Ensure `@pracht/adapter-vercel` is installed.
173
+ 2. In `vite.config.ts`:
174
+ ```ts
175
+ import { pracht } from "@pracht/vite-plugin";
176
+ import { vercelAdapter } from "@pracht/adapter-vercel";
177
+ export default { plugins: [pracht({ adapter: vercelAdapter() })] };
178
+ ```
179
+
180
+ ### Build & Deploy
181
+
182
+ ```bash
183
+ pracht build
184
+ npx vercel deploy --prebuilt
185
+ ```
186
+
187
+ Produces: `.vercel/output/config.json`, `.vercel/output/static/`, `.vercel/output/functions/render.func/server.js`
188
+
189
+ ---
190
+
191
+ ## Deployment Checklist
192
+
193
+ 1. **Build**: Run `pracht build` and verify `dist/` output.
194
+ 2. **Environment variables**: Ensure secrets/config needed by loaders are available at runtime.
195
+ 3. **Static assets**: Verify `dist/client/` contains prerendered HTML for SSG routes (and ISG routes — except time-revalidated ISG routes on Cloudflare with Workers Caching enabled, which render on demand; webhook-only ISG routes keep their build-time snapshots).
196
+ 4. **ISG routes**: Confirm the ISG manifest (`dist/server/isg-manifest.json`; on Cloudflare also `dist/client/_pracht/isg.json`) exists if using incremental static generation.
197
+ 5. **API routes**: Test API endpoints work in the production runtime. For Node.js, run `pracht preview` (or `node dist/server/server.js`).
198
+ 6. **Middleware**: Verify auth/redirect middleware behaves correctly in production.
199
+
200
+ ## Rules
201
+
202
+ 1. Read `vite.config.ts` and `package.json` before giving advice.
203
+ 2. Run `pracht build` to verify the build succeeds before deploying.
204
+ 3. Smoke-test the production runtime before pushing to production. For Node.js and Cloudflare, run `pracht preview`.
205
+ 4. If the user needs an adapter that isn't installed, help them add it (`pnpm add @pracht/adapter-*`).
206
+ 5. Don't push to production without the user's explicit confirmation.
207
+
208
+ $ARGUMENTS
@@ -0,0 +1,191 @@
1
+ ---
2
+ name: pracht-scaffold
3
+ version: 1.1.0
4
+ description: |
5
+ Pracht code scaffolding. Prefer the framework-native CLI generators
6
+ (`pracht generate route|shell|middleware|api`) and only fall back to manual
7
+ edits when the CLI flags cannot express the requested shape. Knows pracht
8
+ conventions (Preact idioms, render modes, route manifest).
9
+ Use when asked to "scaffold", "generate a route", "create a new page",
10
+ "add middleware", "add an API route", or "create a shell".
11
+ allowed-tools:
12
+ - Bash
13
+ - Read
14
+ - Write
15
+ - Edit
16
+ - Grep
17
+ - Glob
18
+ - AskUserQuestion
19
+ ---
20
+
21
+ # Pracht Scaffold
22
+
23
+ Generate pracht framework modules with correct types, exports, and manifest wiring.
24
+
25
+ ## First Choice
26
+
27
+ Use the CLI first:
28
+
29
+ ```bash
30
+ pracht generate route --path /dashboard --render ssr
31
+ pracht generate shell --name app
32
+ pracht generate middleware --name auth
33
+ pracht generate api --path /health --methods GET,POST
34
+ ```
35
+
36
+ `pracht generate route` supports the full flag matrix below — do not fall back to manual edits for shapes it already covers:
37
+
38
+ | Flag | Meaning |
39
+ | ------------------ | ------------------------------------------------------------------------------------ |
40
+ | `--path` (required) | Route path, e.g. `/dashboard` or `/blog/:slug` |
41
+ | `--render` | Render mode: `ssr` (default), `spa`, `ssg`, or `isg` |
42
+ | `--shell` | Registered shell name (manifest apps only) |
43
+ | `--middleware` | Registered middleware names, comma-separated (manifest apps only) |
44
+ | `--loader` | Include a `loader` export |
45
+ | `--error-boundary` | Include an `ErrorBoundary` export |
46
+ | `--static-paths` | Include `getStaticPaths` (added automatically for dynamic `ssg`/`isg` paths) |
47
+ | `--title` | Page title used in the `head()` export |
48
+ | `--revalidate` | ISG revalidation window in seconds (`isg` only, default 3600) |
49
+ | `--json` | Machine-readable output |
50
+
51
+ `generate shell` and `generate middleware` take `--name`; `generate api` takes `--path` and `--methods` (comma-separated). All subcommands accept `--json`.
52
+
53
+ - `--shell`/`--middleware` names must already be registered in the app manifest — the CLI errors otherwise. Generate the shell/middleware first, then the route that references it.
54
+ - If the pracht MCP server is registered (docs/MCP.md), call the `generate_route`/`generate_shell`/`generate_middleware`/`generate_api` MCP tools instead of Bash — same behavior, structured results.
55
+ - Add `--json` when another agent/tool needs machine-readable output.
56
+ - `generate route` also emits a Playwright smoke test in `e2e/` when the app has a Playwright setup (`playwright.config.*` or an `e2e/` directory). Pass `--no-test` to skip it, `--test` to force it. Keep the generated test — it is the output-level proof the route works.
57
+ - Use `pracht inspect routes --json` or `pracht inspect api --json` to confirm current wiring before manual edits when the existing graph matters. `pracht inspect` requires the pracht plugin registered in the project's vite config.
58
+ - If the app has typed routes (`src/pracht-routes.ts` / `.d.ts`) or the user asks for typed links, run `pracht typegen` after adding or renaming routes.
59
+ - If the app commits `.pracht/app-graph.json`, run `pracht plan --write` after changing routes and include the refreshed snapshot — `pracht verify` fails when it is stale.
60
+ - If `src/routes.ts` declares `constraints:`, respect them (e.g. put new `/app/**` routes behind the required middleware). Never delete or weaken a constraint to make `pracht verify` pass — that is a policy change the user must approve.
61
+ - If the CLI can express the request, do not reimplement the scaffold by hand.
62
+ - Only edit files manually when the CLI cannot cover the requested shape.
63
+
64
+ The user will describe what they want to create. Parse their request and generate the appropriate module(s). Always ask if anything is ambiguous (e.g. render mode, shell assignment).
65
+
66
+ ## What You Can Scaffold
67
+
68
+ | Kind | Directory | Key exports | Example |
69
+ | ---------- | ----------------- | -------------------------------------------------------------------- | ------------------------------ |
70
+ | Route | `src/routes/` | `loader`, `head`, `Component`, `ErrorBoundary`, `getStaticPaths` | `src/routes/blog.tsx` |
71
+ | Shell | `src/shells/` | `Shell`, `head` | `src/shells/marketing.tsx` |
72
+ | Middleware | `src/middleware/` | `middleware` | `src/middleware/rate-limit.ts` |
73
+ | API route | `src/api/` | Named HTTP method handlers (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`) or one default method dispatcher | `src/api/users/[id].ts` |
74
+
75
+ ## Templates (manual fallback)
76
+
77
+ Use these only when the CLI cannot express the requested shape.
78
+
79
+ ### Route
80
+
81
+ ```tsx
82
+ export function head() {
83
+ return { title: "Page Title" };
84
+ }
85
+
86
+ export function Component() {
87
+ return <section>{/* route UI */}</section>;
88
+ }
89
+ ```
90
+
91
+ - Include a `loader` only when the route needs server data (matches the CLI, which omits it unless `--loader` is passed):
92
+
93
+ ```tsx
94
+ import type { LoaderArgs, RouteComponentProps } from "@pracht/core";
95
+
96
+ export async function loader(_args: LoaderArgs) {
97
+ return {
98
+ /* loader data */
99
+ };
100
+ }
101
+
102
+ export function Component({ data }: RouteComponentProps<typeof loader>) {
103
+ return <section>{/* route UI */}</section>;
104
+ }
105
+ ```
106
+
107
+ - Include `ErrorBoundary` only if requested.
108
+ - Include `getStaticPaths` only for SSG/ISG routes with dynamic segments.
109
+ - Use `RouteComponentProps<typeof loader>` for typed `data` prop.
110
+
111
+ ### Shell
112
+
113
+ ```tsx
114
+ import type { ShellProps } from "@pracht/core";
115
+
116
+ export function Shell({ children }: ShellProps) {
117
+ return (
118
+ <div class="shell-name">
119
+ <nav>{/* navigation */}</nav>
120
+ <main>{children}</main>
121
+ </div>
122
+ );
123
+ }
124
+
125
+ export function head() {
126
+ return { title: "Shell Title" };
127
+ }
128
+ ```
129
+
130
+ ### Middleware
131
+
132
+ Middleware wraps the rest of the request via `next()`:
133
+
134
+ ```ts
135
+ import { redirect, type MiddlewareFn } from "@pracht/core";
136
+
137
+ export const middleware: MiddlewareFn = async ({ context, request }, next) => {
138
+ // Mutate context, validate auth, etc.
139
+ // - Call `return next()` to continue
140
+ // - Return `redirect("/path", { request })` to short-circuit with a redirect
141
+ // - Return any `Response` to short-circuit
142
+ // - Wrap `await next()` in try/catch/finally for tracing/logging
143
+ return next();
144
+ };
145
+ ```
146
+
147
+ ### API Route
148
+
149
+ ```ts
150
+ import type { ApiRouteArgs } from "@pracht/core";
151
+
152
+ export function GET({ params, url }: ApiRouteArgs) {
153
+ return Response.json({
154
+ /* response data */
155
+ });
156
+ }
157
+ ```
158
+
159
+ - Only include the HTTP methods the user needs.
160
+ - Use a default export only when the user wants to branch on `request.method` manually.
161
+ - Use `request.json()`, `request.formData()`, etc. for body parsing.
162
+ - Always return `Response` objects (typically `Response.json()`).
163
+ - Dynamic segments use bracket syntax in filenames: `[id].ts`, `[...slug].ts`.
164
+
165
+ ## Wiring Into the Manifest (manual fallback only)
166
+
167
+ The CLI generators wire the manifest themselves: `pracht generate route` inserts the `route(...)` call into `src/routes.ts` (adding `route`/`timeRevalidate` imports as needed), and `generate shell`/`generate middleware` upsert their registry entries. **Do not re-edit the manifest after a successful `pracht generate` run.**
168
+
169
+ Only when you created module files by hand, update `src/routes.ts` to register the new module:
170
+
171
+ - **Routes**: Add a `route("/path", () => import("./routes/filename.tsx"), { id: "name", render: "ssr" })` call inside the appropriate group or at the top level. Plain strings like `"./routes/filename.tsx"` also work.
172
+ - **Shells**: Add to the `shells` record: `shellName: () => import("./shells/filename.tsx")` (or `"./shells/filename.tsx"`).
173
+ - **Middleware**: Add to the `middleware` record: `mwName: () => import("./middleware/filename.ts")` (or `"./middleware/filename.ts"`).
174
+ - **API routes**: No manifest change needed — auto-discovered from `src/api/` by the Vite plugin.
175
+
176
+ Available render modes: `"ssr"` (default), `"ssg"` (static at build), `"isg"` (incremental static with `revalidate: timeRevalidate(seconds)`), `"spa"` (client-only).
177
+
178
+ Import `timeRevalidate` from `"@pracht/core"` when using ISG.
179
+
180
+ ## Rules
181
+
182
+ 1. Prefer `pracht generate ...` over manual edits.
183
+ 2. Read the project's existing `src/routes.ts` to determine current shells, middleware, and route structure before adding when the CLI cannot finish the job on its own.
184
+ 3. Place files in the conventional directories (`src/routes/`, `src/shells/`, `src/middleware/`, `src/api/`).
185
+ 4. Keep generated code minimal — only include exports the user actually needs.
186
+ 5. Use Preact idioms: `class` not `className`, functional components, `import type` for type-only imports.
187
+ 6. When route ids/paths change in a typed-routes app, run `pracht typegen` and include the generated route files.
188
+ 7. Finish with `pracht verify` (and `pracht plan --write` when the app commits an app-graph snapshot).
189
+ 8. After scaffolding, summarize what was created and how it was wired.
190
+
191
+ $ARGUMENTS