create-pracht 0.7.0 → 0.7.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.
- package/README.md +2 -2
- package/package.json +1 -1
- package/skills/add-capabilities/SKILL.md +11 -8
- package/skills/audit-agent-surface/SKILL.md +2 -1
- package/skills/audit-bundles/SKILL.md +10 -0
- package/skills/pracht-debug/SKILL.md +17 -1
- package/skills/pre-deploy/SKILL.md +15 -1
- package/src/index.js +32 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ npm run dev
|
|
|
17
17
|
- Detects the active package manager from the current environment.
|
|
18
18
|
- Lets the user choose between the Node.js, Cloudflare, Vercel, Netlify, and static adapters.
|
|
19
19
|
- Optionally wires up Tailwind CSS (`tailwindcss` + `@tailwindcss/vite`, a global stylesheet, and the shell import).
|
|
20
|
-
- Scaffolds a minimal app with a route manifest or pages router, shell, home route, not-found page, a sample API route for serverful adapters, runnable project README, TypeScript typecheck script, and (with agent tooling enabled) agent instructions.
|
|
20
|
+
- Scaffolds a minimal app with a route manifest or pages router, shell, home route, not-found page, a sample API route for serverful adapters, runnable project README, TypeScript typecheck script, browser-aware package resolution that rejects server-only root exports in client code, and (with agent tooling enabled) agent instructions.
|
|
21
21
|
- Manifest scaffolds include a commented-out `constraints` example in `src/routes.ts`, ready for `pracht verify`.
|
|
22
22
|
- The generated `.gitignore` keeps `.pracht/app-graph.json` committable, and the README and agent instructions cover the `pracht verify` / `pracht plan` / `pracht report` loop.
|
|
23
23
|
- Every standalone pnpm scaffold includes a narrow lifecycle-script policy for
|
|
@@ -100,7 +100,7 @@ writing it would leave a config in the repo that nothing in the repo reads.
|
|
|
100
100
|
|
|
101
101
|
- `dev` -> `pracht dev`
|
|
102
102
|
- `build` -> `pracht build`
|
|
103
|
-
- `typecheck` ->
|
|
103
|
+
- `typecheck` -> checks the server-capable base TypeScript program, then the browser-conditioned routes, shells, and islands in `tsconfig.client.json`
|
|
104
104
|
|
|
105
105
|
Node starters also include:
|
|
106
106
|
|
package/package.json
CHANGED
|
@@ -176,11 +176,15 @@ export const CAPABILITIES = ["notes.search"];
|
|
|
176
176
|
|
|
177
177
|
`CAPABILITIES` must be an inline array of non-empty registered names and cannot
|
|
178
178
|
appear on `_app` or `404`. In a manifest, group capability lists are additive.
|
|
179
|
+
|
|
179
180
|
Unknown names, capabilities without `expose.webmcp`, and activation on
|
|
180
181
|
`hydration: "none"` routes are rejected. Initial hydration registers the matched
|
|
181
182
|
route's set; every committed client navigation replaces it, so never assume a
|
|
182
183
|
tool exposed on one page persists globally.
|
|
183
184
|
|
|
185
|
+
In dev, the tab's `pracht_page_tools` WebMCP tool lists the tools active on
|
|
186
|
+
the route and why a declared capability is not one.
|
|
187
|
+
|
|
184
188
|
Each `agents` sub-option is independent — add only what the app uses. Web Bot
|
|
185
189
|
Auth `policy: "require"` gates capability HTTP endpoints (not pages or API
|
|
186
190
|
routes) with `401 agent_required`; `agentPolicy: "require"` on a capability
|
|
@@ -381,14 +385,13 @@ same prepare/commit round trip as HTTP, with the token in the call's
|
|
|
381
385
|
`createCapabilityTestHost()` from `@pracht/core` covers the same pipeline in
|
|
382
386
|
unit tests, without a server.
|
|
383
387
|
|
|
384
|
-
WebMCP
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
shell `head()` recipe.
|
|
388
|
+
WebMCP: names must fit the draft's 1–128 ASCII `[a-zA-Z0-9_.-]` grammar;
|
|
389
|
+
Chrome advises 30 chars/name, 500/tool description, 150/parameter description,
|
|
390
|
+
and 1.5K/result. `pracht verify` checks static limits; bound outputs yourself.
|
|
391
|
+
Stable Chrome needs an origin-trial token. No mainstream production agent
|
|
392
|
+
consumes these tools yet, so retain HTTP or remote MCP. See the capabilities
|
|
393
|
+
site page for current compatibility. An effective `agentPolicy: "require"`
|
|
394
|
+
always 401s unsigned page-tool calls and is warned.
|
|
392
395
|
|
|
393
396
|
To audit what the whole agent surface exposes, run `/audit-agent-surface`.
|
|
394
397
|
|
|
@@ -89,7 +89,8 @@ For every exposed capability, ask whether the exposure is deliberate:
|
|
|
89
89
|
the tool is dead — every call 401s. Also check that capabilities returning
|
|
90
90
|
user-generated or third-party content set
|
|
91
91
|
`expose.webmcp: { untrustedContent: true }` so hosts treat the output as
|
|
92
|
-
untrusted.
|
|
92
|
+
untrusted. Flag `exposedTo` and outputs above 1.5K. Consequential work is
|
|
93
|
+
`destructive`, so never WebMCP.
|
|
93
94
|
- Private capabilities used as building blocks: `invokeCapability()` runs their
|
|
94
95
|
named middleware but **not** app-level `api.middleware`. Their named
|
|
95
96
|
middleware is the only authorization seam — flag private capabilities with an
|
|
@@ -181,6 +181,16 @@ pracht injects per route. Compare `<link rel="stylesheet">` tags in
|
|
|
181
181
|
that wants Preact inside its own chunks or places `frameworkChunkGroups()`
|
|
182
182
|
itself.
|
|
183
183
|
|
|
184
|
+
Pracht contributes one group to the **server** build too: each island becomes
|
|
185
|
+
its own chunk, so a route that does not fully hydrate can resolve its CSS from
|
|
186
|
+
that build without inheriting the server entry's merged stylesheet. An app that
|
|
187
|
+
configures SSR chunking composes with it the same way the client side does; a
|
|
188
|
+
group that pulls islands back into the entry shows up as extra `<link
|
|
189
|
+
rel="stylesheet">` tags on static pages, and the build warns.
|
|
190
|
+
|
|
191
|
+
`build.cssCodeSplit: false` is rejected outright — there is no `index.html` to
|
|
192
|
+
link the single stylesheet it produces, so every page would ship without one.
|
|
193
|
+
|
|
184
194
|
## Step 7: Report
|
|
185
195
|
|
|
186
196
|
Three sections:
|
|
@@ -36,6 +36,7 @@ Reach for these before deep manual inspection:
|
|
|
36
36
|
| `pracht inspect routes\|api\|build --json` | The resolved graph — never reconstruct it from source |
|
|
37
37
|
| `GET /_pracht` (JSON at `/_pracht.json`) | Same graph from a running dev server, no CLI needed |
|
|
38
38
|
| `Server-Timing` on dev SSR responses | `mw` / `loader` / `render` durations in ms — which phase is slow |
|
|
39
|
+
| `pracht_*` WebMCP page tools in the open tab | Tab-scoped view from an agent-driven browser: `pracht_route`, `pracht_loader_data`, `pracht_islands`, `pracht_last_error`, `pracht_page_tools` |
|
|
39
40
|
|
|
40
41
|
`pracht inspect` needs the pracht plugin in the vite config; `inspect build`
|
|
41
42
|
needs a prior `pracht build`. Under a Vite deploy base, prefix `/_pracht` with
|
|
@@ -44,6 +45,18 @@ pracht MCP server is registered (docs/MCP.md), prefer the
|
|
|
44
45
|
`inspect_routes`/`inspect_api`/`doctor`/`verify` MCP tools — same payloads,
|
|
45
46
|
structured results.
|
|
46
47
|
|
|
48
|
+
When you are driving a browser against `pracht dev` with a WebMCP-compatible
|
|
49
|
+
test harness or host, ask the tab before reading logs: every dev document registers read-only
|
|
50
|
+
`pracht_*` page tools with `document.modelContext`. `pracht_route` gives the
|
|
51
|
+
matched route, files, render/hydration mode, and middleware; `pracht_loader_data`
|
|
52
|
+
the data the page holds now (`{ path: "a.0.b" }` narrows it; islands/none routes
|
|
53
|
+
answer with the route-state request to make instead); `pracht_islands` each
|
|
54
|
+
island's file, strategy, props, and hydration status; `pracht_last_error` the
|
|
55
|
+
structured server error behind an overlay or `ErrorBoundary` plus recent client
|
|
56
|
+
errors; `pracht_page_tools` the app's active WebMCP tools on the route. All
|
|
57
|
+
resolve to `{ ok, data }` / `{ ok: false, error }` envelopes and follow client
|
|
58
|
+
navigation.
|
|
59
|
+
|
|
47
60
|
## Checklist
|
|
48
61
|
|
|
49
62
|
Work in order; stop at the root cause.
|
|
@@ -94,7 +107,10 @@ Work in order; stop at the root cause.
|
|
|
94
107
|
component (via Preact's `options.__m` hook). Compare server HTML against
|
|
95
108
|
client output. Usual causes: date/time differences, browser-only APIs during
|
|
96
109
|
SSR (`window`, `document`, `localStorage`), conditional rendering on client
|
|
97
|
-
state — or
|
|
110
|
+
state — or, on Preact 10, a suspending boundary resolving to zero or multiple
|
|
111
|
+
DOM nodes. Preact 11 supports those boundary shapes, so Pracht skips that
|
|
112
|
+
legacy diagnostic there. Another cause is two copies of `@pracht/core` in
|
|
113
|
+
the SSR module graph. The tell for
|
|
98
114
|
that last one: in the server-rendered HTML of *every* page, `useLocation()`
|
|
99
115
|
returns `/`, `useParams()` returns `{}`, and `useRouteData()` returns
|
|
100
116
|
`undefined`, while the hydrated client is correct — provider and hooks hold
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pre-deploy
|
|
3
|
-
version: 1.
|
|
3
|
+
version: 1.5.0
|
|
4
4
|
description: |
|
|
5
5
|
Adapter-aware pre-deployment checklist (Node, Cloudflare Workers, Vercel, static)
|
|
6
6
|
for the failures that only surface in production: missing env vars, Node-only
|
|
@@ -55,6 +55,20 @@ reports errors, stop here**; the remaining checks will be noisy false
|
|
|
55
55
|
positives. Stale generated typed-route files block deployment; if the app does
|
|
56
56
|
not use them yet, note that `typegen --check` is optional.
|
|
57
57
|
|
|
58
|
+
If `pracht inspect agents --json` reports any WebMCP exposure, run the live
|
|
59
|
+
browser boundary too. Point it at the already-started preview, or let it own the
|
|
60
|
+
server lifecycle:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pracht verify webmcp --start "pracht preview" --json
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
CI must install a pinned Chrome 150+ build and pass its executable with
|
|
67
|
+
`--browser`; the verifier never silently downloads one. Treat unsupported API,
|
|
68
|
+
startup/registration failure, and graph drift as deployment errors. Do not add
|
|
69
|
+
`--scenario` unless the repository already supplies an explicitly safe WebMCP
|
|
70
|
+
eval scenario.
|
|
71
|
+
|
|
58
72
|
For a PR deploy, `pracht report --base origin/main` produces a markdown summary
|
|
59
73
|
(graph diff + verify + budgets) worth attaching.
|
|
60
74
|
|
package/src/index.js
CHANGED
|
@@ -754,6 +754,7 @@ async function buildProjectFiles({
|
|
|
754
754
|
}),
|
|
755
755
|
"vite.config.ts": createViteConfig(adapter, router, tailwind),
|
|
756
756
|
"tsconfig.json": createBaseTSConfig(adapter),
|
|
757
|
+
"tsconfig.client.json": createClientTSConfig(router),
|
|
757
758
|
};
|
|
758
759
|
|
|
759
760
|
// A static export has no server, so an API route would be a hard build
|
|
@@ -871,7 +872,7 @@ function createPackageJson({ adapter, projectName, tailwind, versions }) {
|
|
|
871
872
|
const scripts = {
|
|
872
873
|
build: "pracht build",
|
|
873
874
|
dev: "pracht dev",
|
|
874
|
-
typecheck: "tsc --noEmit",
|
|
875
|
+
typecheck: "tsc --noEmit && tsc --noEmit --project tsconfig.client.json",
|
|
875
876
|
};
|
|
876
877
|
|
|
877
878
|
if (adapter.id === "node") {
|
|
@@ -1164,6 +1165,27 @@ function createBaseTSConfig(_adapter) {
|
|
|
1164
1165
|
return `${JSON.stringify(config, null, 2)}\n`;
|
|
1165
1166
|
}
|
|
1166
1167
|
|
|
1168
|
+
function createClientTSConfig(router) {
|
|
1169
|
+
const config = {
|
|
1170
|
+
extends: "./tsconfig.json",
|
|
1171
|
+
compilerOptions: {
|
|
1172
|
+
customConditions: ["browser"],
|
|
1173
|
+
},
|
|
1174
|
+
include:
|
|
1175
|
+
router === "pages"
|
|
1176
|
+
? ["src/pages/**/*", "src/islands/**/*"]
|
|
1177
|
+
: ["src/routes/**/*", "src/shells/**/*", "src/islands/**/*"],
|
|
1178
|
+
};
|
|
1179
|
+
|
|
1180
|
+
// Pages route modules are shared with the browser, but these two convention
|
|
1181
|
+
// files are server-only. The base program still checks the whole project.
|
|
1182
|
+
if (router === "pages") {
|
|
1183
|
+
config.exclude = ["src/pages/**/_app.config.*", "src/pages/**/_middleware.*"];
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
return `${JSON.stringify(config, null, 2)}\n`;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1167
1189
|
function createHealthRoute(adapter) {
|
|
1168
1190
|
return [
|
|
1169
1191
|
"export function GET() {",
|
|
@@ -1606,6 +1628,10 @@ function createAgentInstructions({
|
|
|
1606
1628
|
lines.push("- `src/api/` — API route handlers");
|
|
1607
1629
|
}
|
|
1608
1630
|
lines.push(`- \`vite.config.ts\` — Vite config with the ${adapter.label} adapter`);
|
|
1631
|
+
lines.push("- `tsconfig.json` — server-capable whole-project TypeScript checks");
|
|
1632
|
+
lines.push(
|
|
1633
|
+
"- `tsconfig.client.json` — browser-conditioned checks for routes, shells, islands, and their imports",
|
|
1634
|
+
);
|
|
1609
1635
|
|
|
1610
1636
|
if (tailwind) {
|
|
1611
1637
|
lines.push("- `src/styles/global.css` — Tailwind CSS entry stylesheet, imported by the shell");
|
|
@@ -1781,6 +1807,11 @@ function createReadme({
|
|
|
1781
1807
|
lines.push("- `src/api/health.ts` is a sample API route.");
|
|
1782
1808
|
}
|
|
1783
1809
|
|
|
1810
|
+
lines.push("- `tsconfig.json` — server-capable whole-project TypeScript checks.");
|
|
1811
|
+
lines.push(
|
|
1812
|
+
"- `tsconfig.client.json` — browser-conditioned checks for routes, shells, islands, and their imports.",
|
|
1813
|
+
);
|
|
1814
|
+
|
|
1784
1815
|
if (tailwind) {
|
|
1785
1816
|
lines.push("- `src/styles/global.css` is the Tailwind CSS entry, imported by the shell.");
|
|
1786
1817
|
}
|