@webjsdev/cli 0.10.40 → 0.10.41

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.
Files changed (69) hide show
  1. package/bin/webjs.js +4 -46
  2. package/lib/create.js +282 -479
  3. package/lib/doctor.js +1 -38
  4. package/package.json +5 -1
  5. package/templates/.agents/rules/workflow.md +61 -271
  6. package/templates/.agents/skills/webjs/SKILL.md +226 -0
  7. package/templates/.agents/skills/webjs/references/auth-and-sessions.md +220 -0
  8. package/templates/.agents/skills/webjs/references/built-ins.md +200 -0
  9. package/templates/.agents/skills/webjs/references/client-router-and-streaming.md +204 -0
  10. package/templates/.agents/skills/webjs/references/components.md +167 -0
  11. package/templates/.agents/skills/webjs/references/data-and-actions.md +187 -0
  12. package/templates/.agents/skills/webjs/references/muscle-memory-gotchas.md +170 -0
  13. package/templates/.agents/skills/webjs/references/optimistic-ui.md +128 -0
  14. package/templates/.agents/skills/webjs/references/routing-and-pages.md +158 -0
  15. package/templates/.agents/skills/webjs/references/runtime.md +80 -0
  16. package/templates/.agents/skills/webjs/references/service-worker.md +78 -0
  17. package/templates/.agents/skills/webjs/references/styling.md +123 -0
  18. package/templates/.agents/skills/webjs/references/testing.md +125 -0
  19. package/templates/.agents/skills/webjs/references/typescript.md +148 -0
  20. package/templates/.claude/hooks/check-server-imports.mjs +1 -1
  21. package/templates/.claude/hooks/require-tests-with-src.sh +1 -1
  22. package/templates/.claude/settings.json +0 -14
  23. package/templates/.cursorrules +21 -189
  24. package/templates/.github/copilot-instructions.md +7 -185
  25. package/templates/.github/pull_request_template.md +1 -1
  26. package/templates/AGENTS.md +59 -1494
  27. package/templates/CLAUDE.md +0 -1
  28. package/templates/CONVENTIONS.md +32 -1383
  29. package/templates/GEMINI.md +11 -0
  30. package/templates/gallery/app/apple-icon.ts +0 -1
  31. package/templates/gallery/app/examples/todo/page.ts +0 -1
  32. package/templates/gallery/app/features/async-render/page.ts +0 -1
  33. package/templates/gallery/app/features/boundaries/page.ts +0 -1
  34. package/templates/gallery/app/features/broadcast/page.ts +0 -1
  35. package/templates/gallery/app/features/caching/page.ts +0 -1
  36. package/templates/gallery/app/features/client-router/page.ts +0 -1
  37. package/templates/gallery/app/features/client-router/second/page.ts +0 -1
  38. package/templates/gallery/app/features/components/page.ts +0 -1
  39. package/templates/gallery/app/features/directives/page.ts +0 -1
  40. package/templates/gallery/app/features/env/page.ts +0 -1
  41. package/templates/gallery/app/features/file-storage/page.ts +0 -1
  42. package/templates/gallery/app/features/forms/page.ts +0 -1
  43. package/templates/gallery/app/features/metadata/page.ts +0 -1
  44. package/templates/gallery/app/features/optimistic-ui/page.ts +0 -1
  45. package/templates/gallery/app/features/rate-limit/page.ts +0 -1
  46. package/templates/gallery/app/features/route-handler/page.ts +0 -1
  47. package/templates/gallery/app/features/routing/page.ts +0 -1
  48. package/templates/gallery/app/features/server-actions/page.ts +0 -1
  49. package/templates/gallery/app/features/service-worker/page.ts +0 -1
  50. package/templates/gallery/app/features/sessions/page.ts +0 -1
  51. package/templates/gallery/app/features/websockets/page.ts +0 -1
  52. package/templates/gallery/app/global-error.ts +0 -1
  53. package/templates/gallery/app/global-not-found.ts +0 -1
  54. package/templates/gallery/app/icon.ts +0 -1
  55. package/templates/gallery/app/manifest.ts +0 -1
  56. package/templates/gallery/app/opengraph-image.ts +0 -1
  57. package/templates/gallery/app/robots.ts +0 -1
  58. package/templates/gallery/app/sitemap.ts +0 -1
  59. package/templates/gallery/app/twitter-image.ts +0 -1
  60. package/templates/public/favicon.svg +5 -0
  61. package/templates/public/sw.js +1 -1
  62. package/templates/scripts/clear-gallery.mjs +95 -0
  63. package/lib/clear-placeholders.js +0 -98
  64. package/lib/design-bar.js +0 -67
  65. package/templates/.claude/hooks/design-review-before-stop.sh +0 -36
  66. package/templates/.claude/hooks/route-skills.sh +0 -35
  67. package/templates/.claude/skills/webjs-design-review/SKILL.md +0 -84
  68. package/templates/LAYOUT-REFERENCE.md +0 -96
  69. package/templates/lib/utils/ui.ts +0 -83
@@ -1,189 +1,21 @@
1
- # Cursor Rules: webjs app
2
-
3
- You are working on a webjs app, an AI-first, no-build, web-components-first
4
- framework. Read AGENTS.md for the full API reference and CONVENTIONS.md for
5
- project-specific conventions before writing any code. When AGENTS.md doesn't
6
- cover what you need, the full hosted docs are at **https://docs.webjs.dev**.
7
-
8
- ## Persistence + scaffold rules (non-negotiable)
9
-
10
- - **Use Drizzle + SQLite for data, never JSON files.** It's already wired up
11
- (`db/schema.server.ts`, `db/connection.server.ts`, `npm run db:generate` + `npm run db:migrate`). For ANY
12
- data the app stores (todos, posts, messages, products, comments…),
13
- define a Drizzle table. NEVER create `data/*.json`, `db.json`, or any
14
- JSON file as a fake database. NEVER use module-scope arrays / Maps as
15
- a substitute. NEVER use localStorage for app data. These are project conventions in CONVENTIONS.md (a JSON file used as a
16
- database resets on reload and cannot scale).
17
- - **The scaffold is reference, not the final product.** Replace
18
- `app/page.ts`, the example `User` model, the example users module, etc.
19
- with the app the user actually asked for. Don't ship "Hello from
20
- <app-name>" as the deliverable.
21
- - **Study the gallery first, prune it second.** The full-stack and saas scaffolds ship
22
- single-feature demos under `app/features/` plus one whole example app under
23
- `app/examples/` (logic in `modules/`). It is your PRIMARY webjs reference:
24
- read every demo end to end (code AND comments) to learn the idioms BEFORE you
25
- write or delete anything. Only after internalising the patterns should you
26
- prune, keeping and adapting what you need and deleting the rest (the
27
- `app/features/<name>` or `app/examples/<name>` route AND its `modules/<name>`;
28
- for the todo app, also the `todos` table). Never delete blindly up front. Each
29
- route page has a `webjs-scaffold-placeholder` marker so `webjs check` fails
30
- until you resolve it. Delete now-empty directories after pruning. The `api`
31
- template ships a BACKEND-features showcase instead (endpoints under
32
- `app/api/features/`: the `route()` adapter + validation, rate limiting,
33
- streaming, file storage, WebSockets + broadcast, plus `env.ts` validation),
34
- listed in the root `app/route.ts` index; prune it the same way.
35
- - **Prune what the app does not use.** Keep the infrastructure the app
36
- USES and delete the rest (files AND folders). No persistence means
37
- delete `db/`, `drizzle.config.ts`, and the `db:*` scripts. No UI kit
38
- used means delete `components/ui/`, `components.json`, and
39
- `lib/utils/cn.ts`. No PWA means delete `public/sw.js` and
40
- `offline.html`. Always KEEP the durable knowledge (`AGENTS.md`,
41
- `CONVENTIONS.md`, the rule files, the MCP), never prune it, so removing
42
- example code never removes your context. Prune AFTER using the features
43
- and examples as reference, never blindly up front. A no-op for the `api` template
44
- (no UI kit, no PWA files).
45
- - **`app/` is routing-only.** Only routing files live in `app/` (page,
46
- layout, route, middleware, metadata routes). CSS, helpers, and constants
47
- do NOT: `globals.css` is at `styles/`, browser-safe helpers at
48
- `lib/utils/`, feature logic in `modules/`.
49
- - **Use a unique design, and redesign means more than recolor (UI apps).** Give
50
- the app a design of its own (palette, typography, LAYOUT, and chrome) chosen
51
- from what the app IS. `app/layout.ts` ships as a MINIMAL shell (theme, design
52
- tokens, and Tailwind infra, then `${children}` in a bare padded container) with
53
- NO header, nav, footer, or reading column: design the app's own chrome from
54
- scratch. Decide whether it needs a header at all, a nav (or none), a footer, a
55
- sidebar, a centered reading column, or a full-bleed canvas, from what fits the
56
- app. `LAYOUT-REFERENCE.md` at the project root is a complete worked layout to
57
- learn the patterns from, then build your own. Two `webjs-scaffold-placeholder`
58
- markers gate `webjs check`: the minimal shell ("design your layout from
59
- scratch") and the palette block ("own the colors"), so check fails until each
60
- is addressed. Keep the design TOKENS and theme wiring in `app/layout.ts`
61
- (infrastructure the ui kit reads) and set the token VALUES to your own palette;
62
- run `webjs check --clear-placeholders` to keep the starter palette
63
- deliberately. Style with Tailwind utilities wherever they reach, and use custom
64
- CSS only for what utilities cannot express (@theme tokens, @keyframes,
65
- scrollbar, complex color-mix or gradients). The `api` template has no UI, so
66
- this does not apply there.
67
- - **Render the app and LOOK before you call UI work done (every agent, not just one harness).**
68
- You write CSS blind, so a layout or design defect ships silently: `webjs check`
69
- and `webjs typecheck` pass even when a component collapses, grid cells are
70
- uneven, the layout resizes as it fills, or the app just kept the scaffold's
71
- colors. Static tools give no failure signal for this. The only thing that
72
- catches it is rendering the app and looking at the pixels. So for ANY page,
73
- layout, or component work: run it (`webjs dev`), open every route you changed in
74
- a real browser (drive it with your harness's browser tool or MCP if it has one,
75
- otherwise open it yourself and screenshot), and PLAY THROUGH every state (empty,
76
- filled, win, draw, reload, narrow and wide, light and dark). Confirm nothing
77
- collapses or reflows, that cells stay equal, that the design is the app's OWN,
78
- and that both themes read. Ship a real-browser test (`webjs test --browser`) for
79
- the mechanical floor (measure `getBoundingClientRect()` and assert cells stay
80
- equal across a move). Fix and re-render until it holds, then state in your final
81
- message what you rendered and confirmed. Claude Code additionally ENFORCES this
82
- via the `webjs-design-review` skill plus a Stop hook, but the discipline is
83
- harness-agnostic and this rule is the source of truth for every agent.
84
- - **Only three templates exist:** `webjs create <name>` (default
85
- full-stack), `--template api`, `--template saas`. The CLI rejects any
86
- other `--template` value. Pick:
87
- - Any product UI (todo, blog, dashboard, marketplace, social…) → default
88
- - HTTP/JSON API only, no UI → `--template api`
89
- - Auth / login / signup / SaaS → `--template saas`
90
-
91
- ## Before starting ANY work
92
-
93
- FIRST, before writing any code:
94
- 1. Run `git branch --show-current` to check the branch.
95
- - If on main/master: STOP. Ask the user which branch to use, or create
96
- one with `git checkout -b feature/<name>`.
97
- - If on a feature branch: verify it matches the current task. Ask if unsure.
98
- 2. Sync with parent: `git fetch origin && git log HEAD..origin/main --oneline`
99
- - If upstream has new commits: `git rebase origin/main` before starting.
100
- - Resolve any conflicts before proceeding with the task.
101
- 3. If more than one agent may work this repo at once, use a DEDICATED git
102
- worktree per task, not a shared checkout: `git worktree add -b <branch>
103
- ../<repo>-<slug> origin/main`, `cd` in, work there, `git worktree remove`
104
- after merge. Two agents in one directory collide (a `git checkout` in one
105
- moves HEAD under the other, so commits land on the wrong branch). A lone
106
- agent in a clean checkout may use a plain branch.
107
-
108
- ## Autonomous mode (sandbox / no-prompt)
109
-
110
- If running without interactive approval, auto-decide:
111
- - On main? Auto-create feature/<task-slug> branch
112
- - Parent has new commits? Auto-rebase before starting
113
- - Merge? Auto-merge in autonomous mode, delete feature branches after
114
- - Commit message? Auto-generate (meaningful, no AI attribution)
115
- - Tests failing? Fix them. Convention violations? Fix them.
116
- Quality bar stays the same, no blocking on questions.
117
-
118
- ## Mandatory workflow (never skip)
119
-
120
- Every code change must include:
121
- 1. Server tests in `test/<feature>/*.test.ts` (node:test).
122
- 2. Browser tests in `test/<feature>/browser/*.test.js` (WTR + Playwright, real Chromium).
123
- 3. Documentation updates. Walk every surface in the **Definition of done**
124
- section of CONVENTIONS.md (AGENTS.md, CONVENTIONS.md, README.md, docs/,
125
- website/, scaffold scripts) and either update it or write
126
- "N/A because <reason>" in the PR body. Docs land on the same PR as the
127
- code, never as a follow-up.
128
- 4. Convention check: `webjs check` must pass.
129
- 5. Pre-merge self-review loop. Before saying the PR is ready for merge, run
130
- fresh-context review rounds until one round finds zero issues. Cursor
131
- primitive: open a NEW composer tab and prompt the review there so the
132
- reviewer has no prior context on your decisions. Minimum two rounds;
133
- rotate focus each round. Skip the loop only for one-line trivial
134
- changes; skipping on a change that touches logic, public surface, build,
135
- security, or multiple files is the exact failure mode the loop exists
136
- to prevent. The full rule, prompt template, and reporting contract live
137
- in the **Pre-merge self-review loop** section of CONVENTIONS.md.
138
-
139
- The user should never have to ask for tests, documentation, or the
140
- self-review loop.
141
-
142
- ## Git rules
143
-
144
- - COMMIT AND PUSH PER LOGICAL UNIT, NOT AT THE END. One feature, one fix,
145
- one rename, one doc rewrite per commit. Always `git push` after
146
- committing. The user should never have to ask for a commit.
147
- - HARD LIMIT: if you have 5+ unstaged files spanning different concerns,
148
- commit before continuing. Cursor 1.7+ has its own
149
- `.cursor/hooks/nudge-uncommitted.sh` (afterFileEdit) firing at threshold
150
- 4; the same enforcement runs for Claude users via
151
- `.claude/hooks/nudge-uncommitted.sh`. On older Cursor versions without
152
- the hook, self-enforce the same rule. Batching multiple logical units
153
- into one commit is the failure mode this rule exists to prevent.
154
- - Write meaningful commit messages: what changed and why, not "update files"
155
- - NEVER add "Co-Authored-By", "Generated by", "AI-assisted" or similar
156
- attribution trailers to commits
157
- - NEVER use em-dashes (U+2014), a hyphen-as-pause (` - `), or a
158
- semicolon-as-pause (` ; `) in commit messages or anywhere else.
159
- Rewrite the sentence so no pause-punctuation crutch is needed.
160
- Use a period, comma, colon, parentheses, or a restructured phrasing.
161
- Plain hyphens stay fine in compound words, CLI flags, filenames,
162
- and ranges. Semicolons stay fine inside code
163
- - Work on feature branches, not main
164
- - NEVER push directly to main. Create a pull request instead.
165
- - NEVER merge any branch without explicit user permission. Always ask:
166
- "Ready to merge <branch> into <target>? Delete or keep <branch> after?"
167
- Wait for approval AND the delete/keep preference before proceeding.
168
- This applies to ALL merges, not just merges into main.
169
- - Run tests before every commit
170
-
171
- ## Framework rules
172
-
173
- - No build step: source files are served as ES modules
174
- - **Erasable TypeScript only.** The runtime strips types via `module.stripTypeScriptTypes` (Node's built-in, or `amaro` on Bun) (whitespace replacement, byte-exact position preservation, no sourcemap). The scaffold's tsconfig.json sets `erasableSyntaxOnly: true`, so the TS compiler rejects `enum`, `namespace` with values, constructor parameter properties, legacy decorators with `emitDecoratorMetadata`, and `import = require`. Use erasable equivalents: `const X = { ... } as const` plus a derived union type instead of `enum`; explicit fields plus constructor body assignments instead of parameter properties. If `erasableSyntaxOnly` is disabled and non-erasable syntax is used, the dev server fails at strip time and returns a 500 pointing at the `no-non-erasable-typescript` lint rule. webjs is buildless end-to-end and has no bundler fallback.
175
- - **Tailwind-first styling.** Tailwind utilities are the strong default for pages AND light-DOM components (the default DOM mode): layout, spacing, color (via `@theme` tokens), typography, borders, radius, shadows, interaction states. Light DOM does not scope, so utilities apply directly. The lit reflex to scope CSS (`static styles = css`) or write an inline `<style>` with semantic class names (`.hero`, `.card`) in a light-DOM component is wrong: the scoped block needs `static shadow = true`, and inline class names leak globally. When a utility bundle repeats, extract a `lib/utils/ui.ts` helper returning an `html` fragment, not a CSS class. Reserve raw CSS for the allowlist (design tokens / `@theme`, `@property` + `@keyframes`, `::-webkit-scrollbar`, `prefers-reduced-motion`, complex `color-mix()` / gradients); when unavoidable in a light-DOM component, prefix every class selector with the component tag.
176
- - **One theme, canonical tokens.** The app has a SINGLE theme, defined once in `app/layout.ts` using the standard `@webjsdev/ui` (shadcn-compatible) semantic tokens set to the brand palette. Use the canonical utility names everywhere, in the page chrome AND inside components: `bg-background`, `text-foreground`, `bg-card`, `bg-muted`, `text-muted-foreground`, `bg-primary`, `text-primary-foreground`, `bg-accent`, `text-accent-foreground`, `border-border`, `ring-ring`. These are exactly the tokens a component copied in by `webjs ui add <name>` reads, so a scaffolded page and a later-added ui component share one theme with no wiring. NEVER invent a parallel token vocabulary (`--fg`, `--bg`, `text-fg`, `bg-elev`, a separate `--brand`): it collides with the ui tokens (the accent once flipped to neutral on navigation for exactly this reason) and diverges from the shadcn conventions the kit and AI agents expect. Reach for opacity modifiers (`bg-primary/10`, `hover:bg-primary/90`) before adding a token; add one the canonical way (a `--x` var plus a `--color-x: var(--x)` line in `@theme inline`).
177
- - Shadow-DOM components opt in with `static shadow = true` and use `static styles = css` for scoped CSS, not inline styles. That is the right home for scoped CSS.
178
- - One function per server action file (*.server.ts)
179
- - **`.server.ts` vs `'use server'`, the decision.** Will the client call it? Add `'use server'` and the file becomes an RPC action (browser import rewritten to a typed stub). Is it server-only infra instead (a DB driver, secrets, node:*)? Use NO directive, and NEVER import it into a page/layout/component. Reach it from a `'use server'` action, `route.ts`, or `middleware.ts`. A `.server.ts` WITHOUT the directive is a server-only utility whose browser import throws at module load.
180
- - **Label every interactive control.** Give each control an accessible name, and make clickable text a `<label for="control-id">` (or the control itself) so a text click activates the control on BOTH the JS path and the no-JS form-submit path. Use `aria-label` and `aria-pressed` on icon-only controls. `assertNoA11yViolations(el)` in a browser test catches missing labels.
181
- - Components must call customElements.define('tag', Class)
182
- - **Never extend raw HTMLElement directly for app components.** Always subclass `WebComponent` (or the factory form `WebComponent({...})`) to hook into SSR, lifecycle, elision, and the reactive property system. Extend raw HTMLElement only for rare native-API edge cases (like form-associated `ElementInternals` or customized built-in elements), and add a `webjs-allow-htmlelement: <reason>` comment to acknowledge the exception.
183
- - Server-only code (the DB driver `pg`, node:*, anything that needs Node APIs) goes only in .server.{js,ts} files, route.ts handlers, or middleware.ts. Never in pages, layouts, or components. Wrap the access in a .server.{js,ts} file; the framework rewrites that import into an RPC stub for the browser. The DB lives in db/*.server.ts (db/connection.server.ts); lib/ holds other server-only infra and browser-safe utilities (lib/utils/cn.ts with cn); follow the same rule per file: if a lib/ file needs Node APIs, only import it from server-only files. A TYPE-ONLY `import type { Todo } from '#db/schema.server.ts'` is the exception, fine in a page or component because the stripper erases it before it reaches the browser.
184
- - Directives: webjs exports the lit directives with no clean native equivalent (`repeat`, `unsafeHTML`, `live`, `keyed`, `guard`, `cache`, `until`, `ref` / `createRef`, `templateContent`, `asyncAppend` / `asyncReplace`, `watch`). Lit's `classMap` / `styleMap` / `ifDefined` / `when` / `choose` are NOT exported. For those, use plain template-literal expressions (`class=${cond ? 'a' : 'b'}`, `${cond ? a : b}`) and lifecycle hooks (`this.query('#el')` in `firstUpdated`) instead.
185
- - **Progressive enhancement is the default.** Pages AND every web component are SSR'd to real HTML. Write components so the first paint is the right content. Read SSR-meaningful defaults in `constructor()`. `connectedCallback` is never called on the server, so anything there only runs after hydration. Initial data for components comes from the page function (server-side fetch plus pass as attribute/property) OR from an `async render()` in the component itself (`const u = await getUser(this.uid)`, which SSR awaits so the data is in the first paint), NOT from `fetch` calls in `connectedCallback`. Prefer the co-located `async render()` over prop-drilling; `renderFallback()` is the optional re-fetch loading state (never first paint), and a `Task` is for genuinely client-only data. For write-paths, prefer `<form action=...>` plus server action over `fetch` plus click handler. The framework upgrades plain forms to partial-swap submissions automatically.
186
- - **Default to optimistic UI for feasible mutations.** Use `optimistic()` from `@webjsdev/core` for create/toggle/like/reorder so the UI updates instantly and rolls back on failure (no hand-written try-catch or temp-id bookkeeping). Do NOT use it where it hurts: unpredictable or server-computed results, side-effectful or OAuth/payment mutations, and destructive irreversible actions (confirm-first instead).
187
- - **Client navigation is auto-magic.** Real `<a href>` and `<form action>` get partial-swap behavior with no opt-in. Because layouts persist across navigation, put shared chrome (sidenav, header) in `layout.ts` and page-specific content in `page.ts`. For validation errors, return 4xx HTML from a `route.ts` POST handler; the router renders it in place preserving the user's input. For non-layout swap regions, wrap in `<webjs-frame id="...">`. See "Client navigation patterns" in AGENTS.md.
188
- - **Keep pages and layouts as pure carriers** so their modules stay out of the network tab. A page/layout never hydrates; the framework drops its module from the browser as long as its only browser job is registering the components it imports. It starts shipping its own module (invisible in tests, an elision verdict) the moment its closure does any OTHER client work. Don't give a page/layout module-scope client work (a top-level call, a window/document/customElements access, a bare side-effect import, or a @webjsdev/core/client-router import: routing is automatic) or import a client-global-touching non-component util into it. Put client behaviour in a component, server-only code in .server.{js,ts}. Self-check: page.ts/layout.ts should not appear in the browser's network tab.
189
- - See AGENTS.md for the complete directive decision guide
1
+ # WebJs app rules (Cursor)
2
+
3
+ Cursor reads `AGENTS.md` natively; this file points you at it and the agent
4
+ skill, and carries the commit rule.
5
+
6
+ - **Read `AGENTS.md` first, then `.agents/skills/webjs/SKILL.md`** (the guide to
7
+ building a WebJs app; it routes to focused references under
8
+ `.agents/skills/webjs/references/` that you load only when a task needs them).
9
+ - **The scaffold ships a browsable feature gallery** (single-concept demos under
10
+ `app/features/` plus the `app/examples/todo` app, with logic in `modules/`).
11
+ It is reference to learn the idioms from, not part of your product.
12
+ - **Building a real app? Learn from the gallery FIRST, then clear it, then
13
+ build.** Skim the demos relevant to your task for the runnable idiom (the skill
14
+ teaches the same and SURVIVES the clear, so you never lose it), then run
15
+ `npm run gallery:clear` to shed the gallery and reset the home, then grow the
16
+ app in place: add routes under `app/`, components under `components/`, features
17
+ under `modules/<feature>/`, and keep server-only code behind `.server.ts`.
18
+ - **Use the wired-up database (Drizzle)** for persistence. Never a JSON file, an
19
+ in-memory array, or localStorage.
20
+ - **Commit per logical unit** as soon as it is complete, and never commit to
21
+ `main`.
@@ -1,187 +1,9 @@
1
- # GitHub Copilot Instructions: webjs app
1
+ # Copilot instructions
2
2
 
3
- You are working on a webjs app, an AI-first, no-build, web-components-first
4
- framework. Read AGENTS.md for the full API reference and CONVENTIONS.md for
5
- project-specific conventions. When AGENTS.md doesn't cover what you need,
6
- the full hosted docs are at **https://docs.webjs.dev**.
3
+ This is a thin bridge to the single source. GitHub Copilot always reads this
4
+ file; in VS Code it reads `AGENTS.md` directly only when `chat.useAgentsMdFile`
5
+ is enabled, so this bridge keeps Copilot pointed at the instructions regardless.
7
6
 
8
- ## Persistence + scaffold rules (non-negotiable)
9
-
10
- - **Use Drizzle + SQLite for data, never JSON files.** It's already wired up
11
- (`db/schema.server.ts`, `db/connection.server.ts`, `npm run db:generate` + `npm run db:migrate`). For ANY
12
- data the app stores (todos, posts, messages, products, comments…),
13
- define a Drizzle table. NEVER create `data/*.json`, `db.json`, or any
14
- JSON file as a fake database. NEVER use module-scope arrays / Maps as
15
- a substitute. NEVER use localStorage for app data. It resets on reload and cannot scale. This is a project convention
16
- (CONVENTIONS.md).
17
- - **The scaffold is reference, not the final product.** Replace
18
- `app/page.ts`, the example `User` model, the example users module, etc.
19
- with the app the user actually asked for. Don't ship "Hello from
20
- <app-name>" as the deliverable.
21
- - **Study the gallery first, prune it second.** The full-stack and saas scaffolds ship
22
- single-feature demos under `app/features/` plus one whole example app under
23
- `app/examples/` (logic in `modules/`). It is your PRIMARY webjs reference:
24
- read every demo end to end (code AND comments) to learn the idioms BEFORE you
25
- write or delete anything. Only after internalising the patterns should you
26
- prune, keeping and adapting what you need and deleting the rest (the
27
- `app/features/<name>` or `app/examples/<name>` route AND its `modules/<name>`;
28
- for the todo app, also the `todos` table). Never delete blindly up front. Each
29
- route page has a `webjs-scaffold-placeholder` marker so `webjs check` fails
30
- until you resolve it. Delete now-empty directories after pruning. The `api`
31
- template ships a BACKEND-features showcase instead (endpoints under
32
- `app/api/features/`: the `route()` adapter + validation, rate limiting,
33
- streaming, file storage, WebSockets + broadcast, plus `env.ts` validation),
34
- listed in the root `app/route.ts` index; prune it the same way.
35
- - **Prune what the app does not use.** Keep the infrastructure the app
36
- USES and delete the rest (files AND folders). No persistence means
37
- delete `db/`, `drizzle.config.ts`, and the `db:*` scripts. No UI kit
38
- used means delete `components/ui/`, `components.json`, and
39
- `lib/utils/cn.ts`. No PWA means delete `public/sw.js` and
40
- `offline.html`. Always KEEP the durable knowledge (`AGENTS.md`,
41
- `CONVENTIONS.md`, the rule files, the MCP), never prune it, so removing
42
- example code never removes your context. Prune AFTER using the features
43
- and examples as reference, never blindly up front. A no-op for the `api` template
44
- (no UI kit, no PWA files).
45
- - **`app/` is routing-only.** Only routing files live in `app/` (page,
46
- layout, route, middleware, metadata routes). CSS, helpers, and constants
47
- do NOT: `globals.css` is at `styles/`, browser-safe helpers at
48
- `lib/utils/`, feature logic in `modules/`.
49
- - **Use a unique design, and redesign means more than recolor (UI apps).** Give
50
- the app a design of its own (palette, typography, LAYOUT, and chrome) chosen
51
- from what the app IS. `app/layout.ts` ships as a MINIMAL shell (theme, design
52
- tokens, and Tailwind infra, then `${children}` in a bare padded container) with
53
- NO header, nav, footer, or reading column: design the app's own chrome from
54
- scratch. Decide whether it needs a header at all, a nav (or none), a footer, a
55
- sidebar, a centered reading column, or a full-bleed canvas, from what fits the
56
- app. `LAYOUT-REFERENCE.md` at the project root is a complete worked layout to
57
- learn the patterns from, then build your own. Two `webjs-scaffold-placeholder`
58
- markers gate `webjs check`: the minimal shell ("design your layout from
59
- scratch") and the palette block ("own the colors"), so check fails until each
60
- is addressed. Keep the design TOKENS and theme wiring in `app/layout.ts`
61
- (infrastructure the ui kit reads) and set the token VALUES to your own palette;
62
- run `webjs check --clear-placeholders` to keep the starter palette
63
- deliberately. Style with Tailwind utilities wherever they reach, and use custom
64
- CSS only for what utilities cannot express (@theme tokens, @keyframes,
65
- scrollbar, complex color-mix or gradients). The `api` template has no UI, so
66
- this does not apply there.
67
- - **Render the app and LOOK before you call UI work done (every agent, not just one harness).**
68
- You write CSS blind, so a layout or design defect ships silently: `webjs check`
69
- and `webjs typecheck` pass even when a component collapses, grid cells are
70
- uneven, the layout resizes as it fills, or the app just kept the scaffold's
71
- colors. Static tools give no failure signal for this. The only thing that
72
- catches it is rendering the app and looking at the pixels. So for ANY page,
73
- layout, or component work: run it (`webjs dev`), open every route you changed in
74
- a real browser (drive it with your harness's browser tool or MCP if it has one,
75
- otherwise open it yourself and screenshot), and PLAY THROUGH every state (empty,
76
- filled, win, draw, reload, narrow and wide, light and dark). Confirm nothing
77
- collapses or reflows, that cells stay equal, that the design is the app's OWN,
78
- and that both themes read. Ship a real-browser test (`webjs test --browser`) for
79
- the mechanical floor (measure `getBoundingClientRect()` and assert cells stay
80
- equal across a move). Fix and re-render until it holds, then state in your final
81
- message what you rendered and confirmed. Claude Code additionally ENFORCES this
82
- via the `webjs-design-review` skill plus a Stop hook, but the discipline is
83
- harness-agnostic and this rule is the source of truth for every agent.
84
- - **Only three templates exist:** `webjs create <name>` (default
85
- full-stack), `--template api`, `--template saas`. The CLI rejects any
86
- other `--template` value. Pick:
87
- - Any product UI (todo, blog, dashboard, marketplace, social…) → default
88
- - HTTP/JSON API only, no UI → `--template api`
89
- - Auth / login / signup / SaaS → `--template saas`
90
-
91
- ## Before starting ANY work
92
-
93
- FIRST, before writing any code:
94
- 1. Check `git branch --show-current`.
95
- - If on main/master: create a feature branch before editing.
96
- - If on a feature branch: verify it matches the task at hand.
97
- 2. Sync: `git fetch origin && git rebase origin/main` if behind.
98
- 3. If more than one agent may work this repo at once, use a DEDICATED git
99
- worktree per task (`git worktree add -b <branch> ../<repo>-<slug> origin/main`,
100
- `cd` in, work there, `git worktree remove` after merge), never a shared
101
- checkout. Two agents in one directory collide: a `git checkout` in one moves
102
- HEAD under the other, so commits land on the wrong branch. A lone agent in a
103
- clean checkout may use a plain branch.
104
-
105
- ## Autonomous mode (sandbox / no-prompt)
106
-
107
- If running without interactive approval (sandbox, auto-approve, etc.):
108
- - On main? Auto-create feature/<task-slug> branch
109
- - Parent behind? Auto-rebase. Merge? Auto-merge + delete feature branches.
110
- - Auto-generate meaningful commit messages. Fix tests and violations.
111
-
112
- Quality bar stays the same, no blocking on questions.
113
-
114
- ## Mandatory workflow (never skip)
115
-
116
- Every code change must include:
117
- 1. Server tests in `test/<feature>/*.test.ts` (node:test).
118
- 2. Browser tests in `test/<feature>/browser/*.test.js` (WTR + Playwright, real Chromium).
119
- 3. Documentation updates. Walk every surface in the **Definition of done**
120
- section of CONVENTIONS.md (AGENTS.md, CONVENTIONS.md, README.md, docs/,
121
- website/, scaffold scripts) and either update or write
122
- "N/A because <reason>" in the PR body. Docs land on the same PR as the
123
- code, never as a follow-up.
124
- 4. Convention check: `webjs check` must pass.
125
- 5. Pre-merge self-review loop. Before saying the PR is ready for merge,
126
- run fresh-context review rounds until one round finds zero issues.
127
- Copilot primitive: open a NEW chat session (reset the side panel) for
128
- each round so the reviewer has no prior context on the implementation
129
- decisions. Minimum two rounds; rotate focus each round. Skip the loop
130
- only for one-line trivial changes; skipping on a change that touches
131
- logic, public surface, build, security, or multiple files is the exact
132
- failure mode the loop exists to prevent. The full rule, prompt
133
- template, and reporting contract live in the **Pre-merge self-review
134
- loop** section of CONVENTIONS.md.
135
-
136
- The user should never have to ask for tests, documentation, or the
137
- self-review loop. The commit-per-logical-unit rule lives under "Git rules"
138
- below, not here, since it governs how work is grouped rather than what
139
- each change must include.
140
-
141
- ## Git rules
142
-
143
- - COMMIT AND PUSH PER LOGICAL UNIT, NOT AT THE END. One feature, one fix,
144
- one rename, one doc rewrite per commit. Always `git push` after
145
- committing. The user should never have to ask for a commit.
146
- - HARD LIMIT: if you have 5+ unstaged files spanning different concerns,
147
- commit before continuing. The Claude Code hook at
148
- `.claude/hooks/nudge-uncommitted.sh` enforces threshold 4 for Claude
149
- users. Copilot has no equivalent hook surface today; self-enforce the
150
- same rule. Batching multiple logical units into one commit is the
151
- failure mode this rule exists to prevent.
152
- - Meaningful commit messages: what changed and why
153
- - NEVER add Co-Authored-By or AI attribution trailers to commits
154
- - NEVER use em-dashes (U+2014), a hyphen-as-pause (` - `), or a
155
- semicolon-as-pause (` ; `) in commit messages or anywhere else.
156
- Rewrite the sentence so no pause-punctuation crutch is needed. Use a
157
- period, comma, colon, parentheses, or a restructured phrasing. Plain
158
- hyphens stay fine in compound words, CLI flags, filenames, and ranges.
159
- Semicolons stay fine inside code.
160
- - Work on feature branches, create PRs, never push directly to main
161
- - NEVER merge any branch without explicit user permission. Always ask:
162
- "Ready to merge <branch> into <target>? Delete or keep <branch> after?"
163
- Wait for approval AND the delete/keep preference. Applies to ALL merges.
164
- - Run `webjs test` before every commit
165
-
166
- ## Framework rules
167
-
168
- - No build step: source files are served as ES modules. Don't introduce
169
- build tools or bundlers in the critical path.
170
- - **Erasable TypeScript only.** The runtime strips types via `module.stripTypeScriptTypes` (Node's built-in, or `amaro` on Bun) (whitespace replacement, byte-exact position preservation, no sourcemap). The scaffold's tsconfig.json sets `erasableSyntaxOnly: true`, so the TS compiler rejects `enum`, `namespace` with values, constructor parameter properties, legacy decorators with `emitDecoratorMetadata`, and `import = require`. Use erasable equivalents: `const X = { ... } as const` plus a derived union type instead of `enum`; explicit fields plus constructor body assignments instead of parameter properties. If `erasableSyntaxOnly` is disabled and non-erasable syntax is used, the dev server fails at strip time and returns a 500 pointing at the `no-non-erasable-typescript` lint rule. WebJs is buildless end-to-end and has no bundler fallback.
171
- - Tagged template: html`<div>${value}</div>` with css`...` for styles.
172
- - **Tailwind-first styling.** Tailwind utilities are the strong default for pages AND light-DOM components (the default DOM mode): layout, spacing, color (via `@theme` tokens), typography, borders, radius, shadows, interaction states. Light DOM does not scope, so utilities apply directly. The lit reflex to scope CSS (`static styles = css\`...\``) or write an inline `<style>` with semantic class names (`.hero`, `.card`) in a light-DOM component is wrong: the scoped block needs `static shadow = true`, and inline class names leak globally. When a utility bundle repeats, extract a `lib/utils/ui.ts` helper returning an `html` fragment, not a CSS class. Reserve raw CSS for the allowlist (design tokens / `@theme`, `@property` + `@keyframes`, `::-webkit-scrollbar`, `prefers-reduced-motion`, complex `color-mix()` / gradients); when unavoidable in a light-DOM component, prefix every class selector with the component tag. Shadow-DOM components (`static shadow = true`) legitimately author `static styles = css\`...\`` for scoped CSS; don't use inline `style="..."` there.
173
- - **One theme, canonical tokens.** The app has a SINGLE theme, defined once in `app/layout.ts` using the standard `@webjsdev/ui` (shadcn-compatible) semantic tokens set to the brand palette. Use the canonical utility names everywhere, in the page chrome AND inside components: `bg-background`, `text-foreground`, `bg-card`, `bg-muted`, `text-muted-foreground`, `bg-primary`, `text-primary-foreground`, `bg-accent`, `text-accent-foreground`, `border-border`, `ring-ring`. These are exactly the tokens a component copied in by `webjs ui add <name>` reads, so a scaffolded page and a later-added ui component share one theme with no wiring. NEVER invent a parallel token vocabulary (`--fg`, `--bg`, `text-fg`, `bg-elev`, a separate `--brand`): it collides with the ui tokens (the accent once flipped to neutral on navigation for exactly this reason) and diverges from the shadcn conventions the kit and AI agents expect. Reach for opacity modifiers (`bg-primary/10`, `hover:bg-primary/90`) before adding a token; add one the canonical way (a `--x` var plus a `--color-x: var(--x)` line in `@theme inline`).
174
- - Components: extend the `WebComponent({ ... })` factory to declare reactive properties (e.g. `extends WebComponent({ count: Number })`; per-prop options via `prop(Number, { reflect: true })`), add `static styles` for shadow-DOM components, call `Class.register('tag-name')` at the bottom of the file. The tag name is the argument to `.register()`, not a static field. A hand-written `static properties` throws at construction (`no-static-properties`); set defaults in the constructor, never a class-field initializer (`reactive-props-no-class-field`). Declare an array-typed prop with the `Array` constructor, not `Object` (`items: prop<Tag[]>(Array)`), flagged by `array-prop-uses-array-type`. **Never extend raw HTMLElement directly for app components.** Always subclass `WebComponent` (or the factory form `WebComponent({...})`) to hook into SSR, lifecycle, elision, and the reactive property system. Extend raw HTMLElement only for rare native-API edge cases (like form-associated `ElementInternals` or customized built-in elements), and add a `webjs-allow-htmlelement: <reason>` comment to acknowledge the exception.
175
- - Async data in a component: prefer an `async render()` (`const u = await getUser(this.uid)`), which SSR awaits so the data is in the first paint, over prop-drilling from the page or fetching in `connectedCallback`. `renderFallback()` is the optional re-fetch loading state (never first paint); error isolation is automatic (`renderError()` customizes it); a `Task` is for genuinely client-only data.
176
- - **Default to optimistic UI for feasible mutations.** Use `optimistic()` from `@webjsdev/core` for create/toggle/like/reorder so the UI updates instantly and rolls back on failure (no hand-written try-catch or temp-id bookkeeping). Do NOT use it where it hurts: unpredictable or server-computed results, side-effectful or OAuth/payment mutations, and destructive irreversible actions (confirm-first instead).
177
- - Server actions: *.server.ts files with one exported async function each.
178
- - **`.server.ts` vs `'use server'`, the decision.** Will the client call it? Add `'use server'` and the file becomes an RPC action (browser import rewritten to a typed stub). Is it server-only infra instead (a DB driver, secrets, node:*)? Use NO directive, and NEVER import it into a page/layout/component. Reach it from a `'use server'` action, route.ts, or middleware.ts. A `.server.ts` WITHOUT the directive is a server-only utility whose browser import throws at module load.
179
- - **Label every interactive control.** Give each control an accessible name, and make clickable text a `<label for="control-id">` (or the control itself) so a text click activates the control on BOTH the JS path and the no-JS form-submit path. Use `aria-label` and `aria-pressed` on icon-only controls. `assertNoA11yViolations(el)` in a browser test catches missing labels.
180
- - Server-only code (a DB driver like pg, node:*, anything needing Node APIs) goes only in .server.{js,ts} files, route.ts handlers, or middleware.ts. Never in pages, layouts, or components. Wrap in a .server.{js,ts} file; the framework rewrites that import to an RPC stub for the browser. The DB lives in db/*.server.ts; lib/ holds other server-only infra and browser-safe utilities (lib/utils/cn.ts with cn); apply the same rule per file. A TYPE-ONLY `import type { Todo } from '#db/schema.server.ts'` is the exception, fine in a page or component because the stripper erases it before it reaches the browser.
181
- - Directives: webjs exports the lit directives with no clean native equivalent (`repeat`, `unsafeHTML`, `live`, `keyed`, `guard`, `cache`, `until`, `ref` / `createRef`, `templateContent`, `asyncAppend` / `asyncReplace`, `watch`). Lit's `classMap` / `styleMap` / `ifDefined` / `when` / `choose` are NOT exported; use plain template-literal expressions instead.
182
- - Context: import { createContext, ContextProvider, ContextConsumer } from '@webjsdev/core/context'
183
- - Task: import { Task, TaskStatus } from '@webjsdev/core/task'
184
- - Routing: file-based under app/ (page.ts, layout.ts, route.ts, middleware.ts).
185
- - Keep pages and layouts as pure carriers so their modules stay out of the network tab. A page/layout never hydrates; the framework drops its module from the browser as long as its only browser job is registering the components it imports. It starts shipping its own module (invisible in tests, an elision verdict) the moment its closure does any OTHER client work. Don't give a page/layout module-scope client work (a top-level call, a window/document/customElements access, a bare side-effect import, or a @webjsdev/core/client-router import: routing is automatic) or import a client-global-touching non-component util into it. Put client behaviour in a component, server-only code in .server.{js,ts}. Self-check: page.ts/layout.ts should not appear in the network tab.
186
- - Component state lives in signals from @webjsdev/core. Module-scope signals share state across components; instance signals (created in the constructor) carry component-local state. Reactive properties (declared via the `WebComponent({ ... })` factory) are for HTML attributes and .prop=${...} hydration.
187
- - Don't skip tests or documentation updates.
7
+ The instructions for this app live in `AGENTS.md` (the cross-agent source) and
8
+ the skill at `.agents/skills/webjs/SKILL.md`. Read `AGENTS.md` first, then the
9
+ skill (it routes to focused references on demand).
@@ -23,7 +23,7 @@ in [`CONVENTIONS.md`](../CONVENTIONS.md) for the full guidance.
23
23
  - [ ] **Every markdown file in the project** that describes the
24
24
  changed surface. Common cases (non-exhaustive): `AGENTS.md` (root
25
25
  + nested), `CONVENTIONS.md`, `README.md` (root + nested),
26
- `CHANGELOG.md`, `docs/**/*.md`, `agent-docs/**/*.md`,
26
+ `CHANGELOG.md`, `docs/**/*.md`, `.agents/skills/webjs/**/*.md`,
27
27
  `.github/*.md`. The rule is generative: if a markdown file in
28
28
  this project mentions a thing this PR changed, it gets touched
29
29
  on this PR.