@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
package/lib/doctor.js CHANGED
@@ -841,43 +841,7 @@ async function checkElisionCarriers(appDir) {
841
841
  message:
842
842
  `${report.shipped.length} page/layout module(s) ship to the browser instead of being elided:\n` +
843
843
  lines.map((l) => ` ${l}`).join('\n'),
844
- fix: 'Move the client work out of the page/layout closure (into a component, or a .server module reached through an action) so the carrier can be elided, or accept that it ships. See agent-docs/components.md.',
845
- };
846
- }
847
-
848
- /**
849
- * ADVISORY: the delivered app still rides the scaffold shell. AGENTS.md /
850
- * CONVENTIONS.md item 6 asks a UI app to own its design (layout, palette,
851
- * typography, chrome); the scaffold is a teaching artifact, not a starting
852
- * design. WARN-level and never a hard fail: a reading column or a theme toggle
853
- * CAN be a legitimate choice, so this nudges, it does not gate. The signal is
854
- * objective (distinctive scaffold-authored chrome strings still present in the
855
- * root layout), not a judgment of taste. Two or more tells is the threshold.
856
- * @param {string} appDir
857
- * @returns {Promise<DoctorResult>}
858
- */
859
- async function checkScaffoldDesign(appDir) {
860
- const name = 'App design (own design, not the scaffold shell)';
861
- let layoutSrc = '';
862
- for (const ext of ['ts', 'js', 'mts', 'mjs']) {
863
- const p = join(appDir, 'app', `layout.${ext}`);
864
- if (existsSync(p)) { layoutSrc = await readFile(p, 'utf8').catch(() => ''); break; }
865
- }
866
- if (!layoutSrc) {
867
- return { name, status: 'pass', message: 'no app/layout to analyse' };
868
- }
869
- const { scaffoldShellTells } = await import('./design-bar.js');
870
- const tells = scaffoldShellTells(layoutSrc);
871
- if (tells.length < 2) {
872
- return { name, status: 'pass', message: 'app/layout does not look like the unmodified scaffold shell' };
873
- }
874
- return {
875
- name,
876
- status: 'warn',
877
- message:
878
- `app/layout still carries ${tells.length} scaffold design signal(s): ${tells.join(', ')}. ` +
879
- 'A delivered UI app should own its design (layout AND palette), not adapt the scaffold.',
880
- fix: 'Design the app\'s own layout, palette, typography, and chrome from what the app IS (a centered board, a full-bleed dashboard, ...), not the scaffold\'s exact 760px reading column, its "Built with webjs" attribution footer, or the unmodified starter palette values (the theme-toggle and --header-h are keep-infrastructure). Recoloring the scaffold is not a redesign. Render the app and look at it. See AGENTS.md / CONVENTIONS.md item 6.',
844
+ fix: 'Move the client work out of the page/layout closure (into a component, or a .server module reached through an action) so the carrier can be elided, or accept that it ships. See references/components.md in the skill.',
881
845
  };
882
846
  }
883
847
 
@@ -1054,7 +1018,6 @@ export async function runDoctorChecks(appDir, opts = {}) {
1054
1018
  checkImportmapCoherence(appDir, opts),
1055
1019
  Promise.resolve(checkGitHook(appDir)),
1056
1020
  checkElisionCarriers(appDir),
1057
- checkScaffoldDesign(appDir),
1058
1021
  checkStaticAssetFreshness(appDir),
1059
1022
  ]);
1060
1023
  return results;
package/package.json CHANGED
@@ -1,11 +1,15 @@
1
1
  {
2
2
  "name": "@webjsdev/cli",
3
- "version": "0.10.40",
3
+ "version": "0.10.41",
4
4
  "type": "module",
5
5
  "description": "webjs CLI - dev, start, create, db",
6
6
  "bin": {
7
7
  "webjs": "bin/webjs.js"
8
8
  },
9
+ "scripts": {
10
+ "prepack": "node ../../scripts/sync-scaffold-skill.mjs",
11
+ "postpack": "node ../../scripts/sync-scaffold-skill.mjs --clean"
12
+ },
9
13
  "files": [
10
14
  "bin",
11
15
  "lib",
@@ -1,287 +1,77 @@
1
- # Antigravity Workspace Rules: webjs app
1
+ # Workspace workflow rules: WebJs app
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 before writing any code. When AGENTS.md does not
6
- cover what you need, the full hosted docs are at **https://docs.webjs.dev**.
3
+ You are working on a WebJs app (AI-first, no-build, web-components-first). This
4
+ file is the WORKFLOW contract (git, tests, review). For HOW to build (routing,
5
+ components, actions, styling, the framework API), read
6
+ `.agents/skills/webjs/SKILL.md`, which routes to focused references on demand.
7
+ Read `AGENTS.md` first. Full hosted docs are at https://docs.webjs.dev.
7
8
 
8
- ## Persistence + scaffold rules (non-negotiable)
9
+ ## Grow the app in place (non-negotiable)
9
10
 
10
- - **Use Drizzle + SQLite for data, never JSON files.** It is already wired up
11
- (`db/schema.server.ts`, `db/connection.server.ts`, `npm run db:generate` + `npm run db:migrate`). For
12
- ANY data the app stores (todos, posts, messages, products, comments), define
13
- a Drizzle table. NEVER create `data/*.json`, `db.json`, or any JSON file as a
14
- fake database. NEVER use module-scope arrays / Maps as a substitute. NEVER
15
- use localStorage for app data. These are project conventions in
16
- CONVENTIONS.md (a JSON file used as a database resets on reload and
17
- cannot scale).
18
- - **The scaffold is reference, not the final product.** Replace `app/page.ts`,
19
- the example `User` model, the example users module, etc. with the app the
20
- user actually asked for. Do not ship "Hello from <app-name>" as the
21
- deliverable.
22
- - **Study the gallery first, prune it second.** The full-stack and saas scaffolds
23
- ship single-feature demos under `app/features/` plus one whole example app under
24
- `app/examples/` (logic in `modules/`). It is your PRIMARY webjs reference: read
25
- every demo end to end (code AND comments) to learn the idioms BEFORE you write
26
- or delete anything. Only after internalising the patterns should you prune,
27
- keeping and adapting what you need and deleting the rest (the
28
- `app/features/<name>` or `app/examples/<name>` route AND its `modules/<name>`;
29
- for the todo app, also the `todos` table). Never delete blindly up front. Each
30
- route page has a `webjs-scaffold-placeholder` marker so `webjs check` fails
31
- until you resolve it. Delete now-empty directories after pruning. The `api`
32
- template ships a BACKEND-features showcase instead (endpoints under
33
- `app/api/features/`: the `route()` adapter + validation, rate limiting,
34
- streaming, file storage, WebSockets + broadcast, plus `env.ts` validation),
35
- listed in the root `app/route.ts` index; prune it the same way.
36
- - **Prune what the app does not use.** Keep the infrastructure the app USES
37
- and delete the rest (files AND folders). No persistence means delete `db/`,
38
- `drizzle.config.ts`, and the `db:*` scripts. No UI kit used means delete
39
- `components/ui/`, `components.json`, and `lib/utils/cn.ts`. No PWA means
40
- delete `public/sw.js` and `offline.html`. Always KEEP the durable knowledge
41
- (`AGENTS.md`, `CONVENTIONS.md`, the rule files, the MCP), never prune it, so
42
- removing example code never removes your context. Prune AFTER using the
43
- features and examples as reference, never blindly up front. A no-op for the `api` template
44
- (no UI kit, no PWA files).
11
+ - **The scaffold is a starting point with a browsable feature gallery.** It ships
12
+ a gallery index home, a root layout, a database wired up, and single-concept
13
+ demos under `app/features/` plus the `app/examples/todo` app (logic in
14
+ `modules/`). The gallery is reference, not part of your product. **Building a
15
+ real app? Learn from the gallery FIRST, then clear it, then build:** (1) skim
16
+ the demos relevant to your task under `app/features/<x>` for the runnable idiom
17
+ (the skill teaches the same and SURVIVES the clear, so you never lose it);
18
+ (2) run `npm run gallery:clear` to shed the whole gallery in one step (it keeps
19
+ the agent skill, the layout, and the database wiring, and resets the home);
20
+ (3) regenerate the database and grow the app in place under `app/`,
21
+ `components/`, and `modules/<feature>/`. Keep the gallery only while exploring,
22
+ never ship it.
23
+ - **Use the wired-up database (Drizzle), never JSON files.** For any data the app
24
+ stores, define a Drizzle table in `db/schema.server.ts`, then
25
+ `npm run db:generate` and `npm run db:migrate`. Never use a JSON file, a
26
+ module-scope array or Map, or localStorage as a database.
45
27
  - **`app/` is routing-only.** Only routing files live in `app/` (page, layout,
46
- route, middleware, metadata routes). CSS, helpers, and constants do NOT:
47
- `globals.css` is at `styles/`, browser-safe helpers at `lib/utils/`, feature
48
- 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 full-stack),
85
- `--template api`, `--template saas`. The CLI rejects any other `--template`
86
- value. Pick:
87
- - Any product UI (todo, blog, dashboard, marketplace, social) goes through
88
- the default template.
89
- - HTTP/JSON API only, no UI, uses `--template api`.
90
- - Auth / login / signup / SaaS uses `--template saas`.
28
+ route, middleware, metadata routes). Browser-safe helpers go in `lib/utils/`,
29
+ feature logic in `modules/`, server-only code behind `.server.ts`.
30
+ - **Give a UI app its own design.** Set the design-token values in `app/layout.ts`
31
+ to a palette that fits the app. Render the app and LOOK before calling UI work
32
+ done: `webjs check` and `webjs typecheck` pass even when a layout collapses, so
33
+ open every route you changed in a real browser and play through its states.
91
34
 
92
35
  ## Before starting ANY work
93
36
 
94
- FIRST, before writing any code:
95
- 1. Check `git branch --show-current`.
96
- - If on main/master: create a feature branch before editing.
97
- - If on a feature branch: verify it matches the current task.
98
- 2. Sync: `git fetch origin && git rebase origin/main` if behind.
99
- 3. If more than one agent may work this repo at once, use a DEDICATED git
100
- worktree per task (`git worktree add -b <branch> ../<repo>-<slug> origin/main`,
101
- `cd` in, work there, `git worktree remove` after merge), never a shared
102
- checkout. Two agents in one directory collide: a `git checkout` in one moves
103
- HEAD under the other, so commits land on the wrong branch. Git enforces
104
- one-branch-per-worktree, so worktrees prevent it. A lone agent in a clean
105
- checkout may use a plain branch.
37
+ 1. Check `git branch --show-current`. If on main or master, create a feature
38
+ branch before editing. If on a feature branch, verify it matches the task.
39
+ 2. Sync: `git fetch origin` and `git rebase origin/main` if behind.
40
+ 3. If more than one agent may work this repo at once, use a DEDICATED git worktree
41
+ per task (`git worktree add -b <branch> ../<repo>-<slug> origin/main`, work
42
+ there, `git worktree remove` after merge), never a shared checkout. Two agents
43
+ in one directory collide: a `git checkout` in one moves HEAD under the other,
44
+ so commits land on the wrong branch.
106
45
 
107
- ## Autonomous mode (sandbox / no-prompt)
108
-
109
- If running without interactive approval, auto-decide:
110
- - On main? Auto-create feature/<task-slug> branch.
111
- - Parent behind? Auto-rebase. Merge? Auto-merge + delete feature branches.
112
- - Auto-generate commit messages. Fix failing tests and violations.
113
- Quality bar stays the same, no blocking on questions.
114
-
115
- ## Mandatory workflow (never skip)
46
+ ## Every code change
116
47
 
117
- Every code change must include:
118
48
  1. Server tests in `test/<feature>/*.test.ts` (node:test).
119
- 2. Browser tests in `test/<feature>/browser/*.test.js` (WTR + Playwright, real Chromium).
120
- 3. Documentation updates. Walk every surface in the **Definition of done**
121
- section of CONVENTIONS.md (AGENTS.md, CONVENTIONS.md, README.md, docs/,
122
- website/, scaffold scripts) and either update it or write
123
- "N/A because <reason>" in the PR body. Docs land on the same PR as the
124
- code, never as a follow-up.
125
- 4. Convention check: `webjs check` must pass.
126
- 5. Pre-merge self-review loop. Before saying the PR is ready for merge, run
127
- fresh-context review rounds until one round finds zero issues. Antigravity
128
- primitive: open a new Cascade thread or a fresh side-panel session for
129
- each round so the reviewer has no prior context on the implementation
130
- decisions. Minimum two rounds; rotate focus each round. Skip the loop
131
- only for one-line trivial changes; skipping on a change that touches
132
- logic, public surface, build, security, or multiple files is the exact
133
- failure mode the loop exists to prevent. The full rule, prompt template,
134
- and reporting contract live in the **Pre-merge self-review loop** section
135
- of CONVENTIONS.md.
136
-
137
- The user should never have to ask for tests, documentation, or the
138
- self-review loop.
49
+ 2. Browser tests in `test/<feature>/browser/*.test.js` for hydration, DOM, slots,
50
+ and the client router.
51
+ 3. Documentation stays in sync on the SAME PR as the code, never a follow-up.
52
+ 4. `webjs check` must pass.
53
+ 5. Pre-merge self-review: before saying a PR is ready, run fresh-context review
54
+ rounds until one round finds zero issues (minimum two rounds, rotate focus).
55
+ Skip only for a one-line trivial change.
139
56
 
140
57
  ## Git rules
141
58
 
142
- - COMMIT AND PUSH PER LOGICAL UNIT, NOT AT THE END. One feature, one fix, one
143
- rename, one doc rewrite per commit. Always `git push` after committing.
144
- The user should never have to ask for a commit.
145
- - HARD LIMIT: if you have 5+ unstaged files spanning different concerns,
146
- commit before continuing. The Claude Code hook at
147
- `.claude/hooks/nudge-uncommitted.sh` fires at threshold 4. Antigravity
148
- users should self-enforce the same rule. Batching multiple logical units
149
- into one commit is the failure mode this rule exists to prevent.
59
+ - Commit and push per logical unit (one feature, one fix, one rename, one doc
60
+ rewrite), not at the end. Push after every commit.
61
+ - If you have 5 or more unstaged files spanning different concerns, commit before
62
+ continuing.
150
63
  - Meaningful commit messages: what changed and why.
151
- - NEVER add Co-Authored-By or AI attribution trailers to commits.
152
- - NEVER use em-dashes (U+2014), a hyphen-as-pause (` - `), or a
153
- semicolon-as-pause (` ; `) in commit messages or anywhere else. Rewrite the
154
- sentence so no pause-punctuation crutch is needed. Use a period, comma,
155
- colon, parentheses, or a restructured phrasing. Plain hyphens stay fine in
156
- compound words, CLI flags, filenames, and ranges. Semicolons stay fine
157
- inside code.
158
- - Work on feature branches, never push directly to main.
159
- - Create pull requests for review.
160
- - NEVER merge any branch without explicit user permission. Always ask:
161
- "Ready to merge <branch> into <target>? Delete or keep <branch> after?"
162
- Wait for approval AND the delete/keep preference. Applies to ALL merges.
163
- - Run `webjs test` before every commit.
64
+ - Never add Co-Authored-By or AI-attribution trailers.
65
+ - Never use an em-dash (U+2014), a space-surrounded hyphen as a pause, or a
66
+ space-surrounded semicolon as a pause, in commit messages or anywhere. Use a
67
+ period, comma, colon, parentheses, or a restructured phrasing.
68
+ - Work on feature branches, never push directly to main. Open pull requests for
69
+ review. Never merge without explicit user permission (ask which target, and
70
+ whether to delete or keep the branch, then wait for both answers).
164
71
 
165
- ## Framework rules
72
+ ## Autonomous mode (sandbox / no-prompt)
166
73
 
167
- - No build step: ES modules served directly.
168
- - **Erasable TypeScript only.** The runtime (Node 24+ or Bun) strips types via
169
- `module.stripTypeScriptTypes` (whitespace replacement, byte-exact position
170
- preservation, no sourcemap). The scaffold's `tsconfig.json` sets
171
- `erasableSyntaxOnly: true`, so the TS compiler rejects `enum`, `namespace`
172
- with values, constructor parameter properties, legacy decorators with
173
- `emitDecoratorMetadata`, and `import = require`. Use erasable equivalents:
174
- `const X = { ... } as const` plus a derived union type instead of `enum`;
175
- explicit fields plus constructor body assignments instead of parameter
176
- properties. If `erasableSyntaxOnly` is disabled and non-erasable syntax is
177
- used, the dev server fails at strip time and returns a 500 pointing at the
178
- `no-non-erasable-typescript` lint rule. WebJs is buildless end-to-end and
179
- has no bundler fallback.
180
- - Web components render into light DOM by default (so Tailwind / global CSS
181
- apply directly). Opt in to shadow DOM per component with
182
- `static shadow = true` when you need scoped styles (via
183
- `static styles = css\`...\``) or third-party-embed isolation. `<slot>`
184
- projection works identically in both modes (named slots, fallback content,
185
- `assignedNodes` / `slotchange`, first-wins resolution).
186
- - **Tailwind-first styling.** Tailwind utilities are the strong default for
187
- pages AND light-DOM components: layout, spacing, color (via `@theme`
188
- tokens), typography, borders, radius, shadows, interaction states. Light
189
- DOM does not scope, so utilities apply directly. The lit reflex to scope
190
- CSS (`static styles = css\`...\``) or write an inline `<style>` with
191
- semantic class names (`.hero`, `.card`) in a light-DOM component is wrong:
192
- the scoped block needs `static shadow = true`, and inline class names leak
193
- globally. When a utility bundle repeats, extract a `lib/utils/ui.ts` helper
194
- returning an `` html`...` `` fragment, not a CSS class. Reserve raw CSS for
195
- the allowlist (design tokens / `@theme`, `@property` + `@keyframes`,
196
- `::-webkit-scrollbar`, `prefers-reduced-motion`, complex `color-mix()` /
197
- gradients); when unavoidable in a light-DOM component, prefix every class
198
- selector with the component tag. Shadow-DOM components legitimately use
199
- `static styles = css\`...\`` for scoped CSS.
200
- - **One theme, canonical tokens.** The app has a SINGLE theme, defined once in
201
- `app/layout.ts` using the standard `@webjsdev/ui` (shadcn-compatible)
202
- semantic tokens set to the brand palette. Use the canonical utility names
203
- everywhere, in the page chrome AND inside components: `bg-background`,
204
- `text-foreground`, `bg-card`, `bg-muted`, `text-muted-foreground`,
205
- `bg-primary`, `text-primary-foreground`, `bg-accent`, `text-accent-foreground`,
206
- `border-border`, `ring-ring`. These are exactly the tokens a component copied
207
- in by `webjs ui add <name>` reads, so a scaffolded page and a later-added ui
208
- component share one theme with no wiring. NEVER invent a parallel token
209
- vocabulary (`--fg`, `--bg`, `text-fg`, `bg-elev`, a separate `--brand`): it
210
- collides with the ui tokens (the accent once flipped to neutral on navigation
211
- for exactly this reason) and diverges from the shadcn conventions the kit and
212
- AI agents expect. Reach for opacity modifiers (`bg-primary/10`,
213
- `hover:bg-primary/90`) before adding a token; add one the canonical way (a
214
- `--x` var plus a `--color-x: var(--x)` line in `@theme inline`).
215
- - Custom-element tag names are passed to `.register('tag-name')`. They are NOT
216
- a static field on the class.
217
- - **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.
218
- - One function per server action file (`*.server.ts`).
219
- - **`.server.ts` vs `'use server'`, the decision.** Will the client call it?
220
- Add `'use server'` and the file becomes an RPC action (browser import
221
- rewritten to a typed stub). Is it server-only infra instead (a DB driver,
222
- secrets, `node:*`)? Use NO directive, and NEVER import it into a
223
- page/layout/component. Reach it from a `'use server'` action, `route.ts`, or
224
- `middleware.ts`. A `.server.ts` WITHOUT the directive is a server-only utility
225
- whose browser import throws at module load.
226
- - **Label every interactive control.** Give each control an accessible name, and
227
- make clickable text a `<label for="control-id">` (or the control itself) so a
228
- text click activates the control on BOTH the JS path and the no-JS
229
- form-submit path. Use `aria-label` and `aria-pressed` on icon-only controls.
230
- `assertNoA11yViolations(el)` in a browser test catches missing labels.
231
- - Server-only code (a DB driver like `pg`, `node:*`, anything that needs Node APIs)
232
- goes only in `.server.{js,ts}` files, `route.ts` handlers, or
233
- `middleware.ts`. Never in pages, layouts, or components. Wrap the access in
234
- a `.server.{js,ts}` file; the framework rewrites that import into an RPC
235
- stub for the browser. `lib/` holds both server-only infra
236
- (the DB in `db/*.server.ts`) and browser-safe utilities (`lib/utils/cn.ts` with
237
- `cn`); follow the same rule per file. A TYPE-ONLY `import type { Todo } from
238
- '#db/schema.server.ts'` is the exception, fine in a page or component because
239
- the stripper erases it before it reaches the browser.
240
- - Keep pages and layouts as pure carriers so their modules stay out of the
241
- network tab. A page/layout never hydrates; the framework drops its module
242
- from the browser as long as its only browser job is registering the
243
- components it imports. It starts shipping its own module (invisible in tests,
244
- an elision verdict) the moment its closure does any OTHER client work. So do
245
- not give a page/layout module-scope client work (a top-level call, a
246
- `window` / `document` / `customElements` access, a bare side-effect import,
247
- or a `@webjsdev/core/client-router` import: routing is automatic), and do not
248
- import a client-global-touching non-component util into it. Put client
249
- behaviour in a component, server-only code in `.server.{js,ts}`. Self-check:
250
- `page.ts` / `layout.ts` should not appear in the browser's network tab.
251
- - Directives: webjs exports the lit directives with no clean native equivalent
252
- (`repeat`, `unsafeHTML`, `live`, `keyed`, `guard`, `cache`, `until`, `ref` /
253
- `createRef`, `templateContent`, `asyncAppend` / `asyncReplace`, `watch`).
254
- `classMap` / `styleMap` / `ifDefined` / `when` / `choose` are NOT exported.
255
- For those, use plain template-literal expressions
256
- (`class=${active ? 'btn active' : 'btn'}`, `style=${'color:' + color}`,
257
- `${cond ? a : b}`) and lifecycle hooks (`this.query('#el')` in
258
- `firstUpdated`) instead of Lit's `classMap` / `styleMap` / `ref` / `when` /
259
- `choose` / `guard`.
260
- - Use Context for cross-component data. For async data in a component, prefer
261
- an `async render()` (`const u = await getUser(this.uid)`, awaited at SSR so
262
- the data is in the first paint); keep `Task` for genuinely client-only data.
263
- - **Progressive enhancement is the default.** Pages AND every web component
264
- are SSR'd to real HTML. Write components so the first paint is the right
265
- content. Read SSR-meaningful defaults in `constructor()`. `connectedCallback`
266
- is never called on the server, so anything there only runs after
267
- hydration. Initial data for components comes from the page function
268
- (server-side fetch plus pass as attribute/property) OR from an `async
269
- render()` in the component itself (preferred over prop-drilling;
270
- `renderFallback()` is the optional re-fetch loading state, never first
271
- paint), NOT from `fetch` calls in `connectedCallback`. For write-paths, prefer `<form action=...>` plus
272
- server action over `fetch` plus click handler. The framework upgrades plain
273
- forms to partial-swap submissions automatically.
274
- - **Default to optimistic UI for feasible mutations.** Use `optimistic()` from
275
- `@webjsdev/core` for create/toggle/like/reorder so the UI updates instantly
276
- and rolls back on failure (no hand-written try-catch or temp-id bookkeeping).
277
- Do NOT use it where it hurts: unpredictable or server-computed results,
278
- side-effectful or OAuth/payment mutations, and destructive irreversible
279
- actions (confirm-first instead).
280
- - **Client navigation is auto-magic.** Real `<a href>` and `<form action>`
281
- get partial-swap behavior with no opt-in. Because layouts persist across
282
- navigation, put shared chrome (sidenav, header) in `layout.ts` and
283
- page-specific content in `page.ts`. For validation errors, return 4xx HTML
284
- from a `route.ts` POST handler; the router renders it in place preserving
285
- the user's input. For non-layout swap regions, wrap in `<webjs-frame id="...">`. See "Client
286
- navigation patterns" in AGENTS.md.
287
- - Full API reference in AGENTS.md.
74
+ If running without interactive approval, auto-decide: on main, auto-create a
75
+ `feature/<task-slug>` branch; auto-rebase if the parent moved; auto-generate
76
+ commit messages; fix failing tests and check violations rather than asking. The
77
+ quality bar stays the same. Only merging into main is gated on user permission.