@webjsdev/cli 0.9.1 → 0.10.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 +1 -1
- package/bin/webjs.js +241 -1
- package/lib/create.js +15 -4
- package/package.json +2 -2
- package/templates/.agents/rules/workflow.md +149 -0
- package/templates/.claude/hooks/require-tests-with-src.sh +83 -0
- package/templates/.claude/settings.json +9 -0
- package/templates/.cursorrules +36 -22
- package/templates/.github/copilot-instructions.md +53 -26
- package/templates/.github/pull_request_template.md +27 -3
- package/templates/.hooks/pre-commit +26 -1
- package/templates/AGENTS.md +121 -8
- package/templates/CONVENTIONS.md +150 -12
- package/templates/test/hello/hello.test.ts +2 -1
- package/templates/.windsurfrules +0 -91
package/templates/.cursorrules
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Cursor Rules
|
|
1
|
+
# Cursor Rules: webjs app
|
|
2
2
|
|
|
3
|
-
You are working on a webjs app
|
|
3
|
+
You are working on a webjs app, an AI-first, no-build, web-components-first
|
|
4
4
|
framework. Read AGENTS.md for the full API reference and CONVENTIONS.md for
|
|
5
5
|
project-specific conventions before writing any code. When AGENTS.md doesn't
|
|
6
6
|
cover what you need, the full hosted docs are at **https://docs.webjs.com**.
|
|
@@ -36,7 +36,7 @@ FIRST, before writing any code:
|
|
|
36
36
|
- If upstream has new commits: `git rebase origin/main` before starting.
|
|
37
37
|
- Resolve any conflicts before proceeding with the task.
|
|
38
38
|
|
|
39
|
-
## Autonomous mode (sandbox / no-prompt
|
|
39
|
+
## Autonomous mode (sandbox / no-prompt)
|
|
40
40
|
|
|
41
41
|
If running without interactive approval, auto-decide:
|
|
42
42
|
- On main? Auto-create feature/<task-slug> branch
|
|
@@ -44,29 +44,44 @@ If running without interactive approval, auto-decide:
|
|
|
44
44
|
- Merge? Auto-merge in autonomous mode, delete feature branches after
|
|
45
45
|
- Commit message? Auto-generate (meaningful, no AI attribution)
|
|
46
46
|
- Tests failing? Fix them. Convention violations? Fix them.
|
|
47
|
-
Quality bar stays the same
|
|
47
|
+
Quality bar stays the same, no blocking on questions.
|
|
48
48
|
|
|
49
49
|
## Mandatory workflow (never skip)
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
Every code change must include:
|
|
52
|
+
1. Server tests in `test/<feature>/*.test.ts` (node:test).
|
|
53
|
+
2. Browser tests in `test/<feature>/browser/*.test.js` (WTR + Playwright, real Chromium).
|
|
54
|
+
3. Documentation updates. Walk every surface in the **Definition of done**
|
|
55
|
+
section of CONVENTIONS.md (AGENTS.md, CONVENTIONS.md, README.md, docs/,
|
|
56
|
+
website/, scaffold scripts) and either update it or write
|
|
57
|
+
"N/A because <reason>" in the PR body. Docs land on the same PR as the
|
|
58
|
+
code, never as a follow-up.
|
|
59
|
+
4. Convention check: `webjs check` must pass.
|
|
60
|
+
5. Pre-merge self-review loop. Before saying the PR is ready for merge, run
|
|
61
|
+
fresh-context review rounds until one round finds zero issues. Cursor
|
|
62
|
+
primitive: open a NEW composer tab and prompt the review there so the
|
|
63
|
+
reviewer has no prior context on your decisions. Minimum two rounds;
|
|
64
|
+
rotate focus each round. Skip the loop only for one-line trivial
|
|
65
|
+
changes; skipping on a change that touches logic, public surface, build,
|
|
66
|
+
security, or multiple files is the exact failure mode the loop exists
|
|
67
|
+
to prevent. The full rule, prompt template, and reporting contract live
|
|
68
|
+
in the **Pre-merge self-review loop** section of CONVENTIONS.md.
|
|
54
69
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
3. CONVENTIONS: Run `webjs check` and fix violations before committing.
|
|
70
|
+
The user should never have to ask for tests, documentation, or the
|
|
71
|
+
self-review loop.
|
|
59
72
|
|
|
60
73
|
## Git rules
|
|
61
74
|
|
|
62
75
|
- COMMIT AND PUSH PER LOGICAL UNIT, NOT AT THE END. One feature, one fix,
|
|
63
76
|
one rename, one doc rewrite per commit. Always `git push` after
|
|
64
|
-
committing.
|
|
77
|
+
committing. The user should never have to ask for a commit.
|
|
65
78
|
- HARD LIMIT: if you have 5+ unstaged files spanning different concerns,
|
|
66
|
-
commit before continuing.
|
|
67
|
-
`.
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
commit before continuing. Cursor 1.7+ has its own
|
|
80
|
+
`.cursor/hooks/nudge-uncommitted.sh` (afterFileEdit) firing at threshold
|
|
81
|
+
4; the same enforcement runs for Claude users via
|
|
82
|
+
`.claude/hooks/nudge-uncommitted.sh`. On older Cursor versions without
|
|
83
|
+
the hook, self-enforce the same rule. Batching multiple logical units
|
|
84
|
+
into one commit is the failure mode this rule exists to prevent.
|
|
70
85
|
- Write meaningful commit messages: what changed and why, not "update files"
|
|
71
86
|
- NEVER add "Co-Authored-By", "Generated by", "AI-assisted" or similar
|
|
72
87
|
attribution trailers to commits
|
|
@@ -77,23 +92,22 @@ Quality bar stays the same - just no blocking on questions.
|
|
|
77
92
|
Plain hyphens stay fine in compound words, CLI flags, filenames,
|
|
78
93
|
and ranges. Semicolons stay fine inside code
|
|
79
94
|
- Work on feature branches, not main
|
|
80
|
-
- NEVER push directly to main
|
|
95
|
+
- NEVER push directly to main. Create a pull request instead.
|
|
81
96
|
- NEVER merge any branch without explicit user permission. Always ask:
|
|
82
97
|
"Ready to merge <branch> into <target>? Delete or keep <branch> after?"
|
|
83
98
|
Wait for approval AND the delete/keep preference before proceeding.
|
|
84
99
|
This applies to ALL merges, not just merges into main.
|
|
85
100
|
- Run tests before every commit
|
|
86
|
-
- Keep commits small and focused
|
|
87
101
|
|
|
88
102
|
## Framework rules
|
|
89
103
|
|
|
90
104
|
- No build step: source files are served as ES modules
|
|
91
|
-
- **Erasable TypeScript only.** Node 24+ strips types via `module.stripTypeScriptTypes` (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
|
|
105
|
+
- **Erasable TypeScript only.** Node 24+ strips types via `module.stripTypeScriptTypes` (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.
|
|
92
106
|
- Web components with shadow DOM: use `static styles = css` not inline styles
|
|
93
107
|
- One function per server action file (*.server.ts)
|
|
94
108
|
- Components must call customElements.define('tag', Class)
|
|
95
109
|
- Server-only code (@prisma/client, 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. lib/ holds both server-only infra (lib/prisma.server.ts) 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.
|
|
96
|
-
- Directives are deliberately minimal: only `unsafeHTML`, `live`, and `repeat` ship. Lit's `classMap` / `styleMap` / `ref` / `when` / `choose` / `guard` are NOT exported
|
|
97
|
-
- **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
|
|
98
|
-
- **Client navigation is auto-magic.** Real `<a href>` and `<form action>` get partial-swap behavior with no opt-in.
|
|
110
|
+
- Directives are deliberately minimal: only `unsafeHTML`, `live`, and `repeat` ship. Lit's `classMap` / `styleMap` / `ref` / `when` / `choose` / `guard` are NOT exported. Use plain template-literal expressions (`class=${cond ? 'a' : 'b'}`, `${cond ? a : b}`) and lifecycle hooks (`this.query('#el')` in `firstUpdated`) instead.
|
|
111
|
+
- **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), NOT from `fetch` calls in `connectedCallback`. For write-paths, prefer `<form action=...>` plus server action over `fetch` plus click handler. The framework upgrades plain forms to partial-swap submissions automatically.
|
|
112
|
+
- **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.
|
|
99
113
|
- See AGENTS.md for the complete directive decision guide
|
|
@@ -33,53 +33,80 @@ FIRST, before writing any code:
|
|
|
33
33
|
- If on a feature branch: verify it matches the task at hand.
|
|
34
34
|
2. Sync: `git fetch origin && git rebase origin/main` if behind.
|
|
35
35
|
|
|
36
|
-
## Autonomous mode
|
|
36
|
+
## Autonomous mode (sandbox / no-prompt)
|
|
37
37
|
|
|
38
38
|
If running without interactive approval (sandbox, auto-approve, etc.):
|
|
39
39
|
- On main? Auto-create feature/<task-slug> branch
|
|
40
40
|
- Parent behind? Auto-rebase. Merge? Auto-merge + delete feature branches.
|
|
41
41
|
- Auto-generate meaningful commit messages. Fix tests and violations.
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
Quality bar stays the same, no blocking on questions.
|
|
44
|
+
|
|
45
|
+
## Mandatory workflow (never skip)
|
|
44
46
|
|
|
45
47
|
Every code change must include:
|
|
46
|
-
1.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
1. Server tests in `test/<feature>/*.test.ts` (node:test).
|
|
49
|
+
2. Browser tests in `test/<feature>/browser/*.test.js` (WTR + Playwright, real Chromium).
|
|
50
|
+
3. Documentation updates. Walk every surface in the **Definition of done**
|
|
51
|
+
section of CONVENTIONS.md (AGENTS.md, CONVENTIONS.md, README.md, docs/,
|
|
52
|
+
website/, scaffold scripts) and either update or write
|
|
53
|
+
"N/A because <reason>" in the PR body. Docs land on the same PR as the
|
|
54
|
+
code, never as a follow-up.
|
|
55
|
+
4. Convention check: `webjs check` must pass.
|
|
56
|
+
5. Pre-merge self-review loop. Before saying the PR is ready for merge,
|
|
57
|
+
run fresh-context review rounds until one round finds zero issues.
|
|
58
|
+
Copilot primitive: open a NEW chat session (reset the side panel) for
|
|
59
|
+
each round so the reviewer has no prior context on the implementation
|
|
60
|
+
decisions. Minimum two rounds; rotate focus each round. Skip the loop
|
|
61
|
+
only for one-line trivial changes; skipping on a change that touches
|
|
62
|
+
logic, public surface, build, security, or multiple files is the exact
|
|
63
|
+
failure mode the loop exists to prevent. The full rule, prompt
|
|
64
|
+
template, and reporting contract live in the **Pre-merge self-review
|
|
65
|
+
loop** section of CONVENTIONS.md.
|
|
66
|
+
|
|
67
|
+
The user should never have to ask for tests, documentation, or the
|
|
68
|
+
self-review loop. The commit-per-logical-unit rule lives under "Git rules"
|
|
69
|
+
below, not here, since it governs how work is grouped rather than what
|
|
70
|
+
each change must include.
|
|
56
71
|
|
|
57
72
|
## Git rules
|
|
58
73
|
|
|
59
|
-
-
|
|
74
|
+
- COMMIT AND PUSH PER LOGICAL UNIT, NOT AT THE END. One feature, one fix,
|
|
75
|
+
one rename, one doc rewrite per commit. Always `git push` after
|
|
76
|
+
committing. The user should never have to ask for a commit.
|
|
77
|
+
- HARD LIMIT: if you have 5+ unstaged files spanning different concerns,
|
|
78
|
+
commit before continuing. The Claude Code hook at
|
|
79
|
+
`.claude/hooks/nudge-uncommitted.sh` enforces threshold 4 for Claude
|
|
80
|
+
users. Copilot has no equivalent hook surface today; self-enforce the
|
|
81
|
+
same rule. Batching multiple logical units into one commit is the
|
|
82
|
+
failure mode this rule exists to prevent.
|
|
60
83
|
- Meaningful commit messages: what changed and why
|
|
61
84
|
- NEVER add Co-Authored-By or AI attribution trailers to commits
|
|
85
|
+
- NEVER use em-dashes (U+2014), a hyphen-as-pause (` - `), or a
|
|
86
|
+
semicolon-as-pause (` ; `) in commit messages or anywhere else.
|
|
87
|
+
Rewrite the sentence so no pause-punctuation crutch is needed. Use a
|
|
88
|
+
period, comma, colon, parentheses, or a restructured phrasing. Plain
|
|
89
|
+
hyphens stay fine in compound words, CLI flags, filenames, and ranges.
|
|
90
|
+
Semicolons stay fine inside code.
|
|
62
91
|
- Work on feature branches, create PRs, never push directly to main
|
|
63
92
|
- NEVER merge any branch without explicit user permission. Always ask:
|
|
64
93
|
"Ready to merge <branch> into <target>? Delete or keep <branch> after?"
|
|
65
94
|
Wait for approval AND the delete/keep preference. Applies to ALL merges.
|
|
66
95
|
- Run `webjs test` before every commit
|
|
67
96
|
|
|
68
|
-
##
|
|
97
|
+
## Framework rules
|
|
69
98
|
|
|
70
|
-
-
|
|
71
|
-
|
|
99
|
+
- No build step: source files are served as ES modules. Don't introduce
|
|
100
|
+
build tools or bundlers in the critical path.
|
|
101
|
+
- **Erasable TypeScript only.** Node 24+ strips types via `module.stripTypeScriptTypes` (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.
|
|
102
|
+
- Tagged template: html`<div>${value}</div>` with css`...` for styles.
|
|
103
|
+
Don't use inline `style="..."` on components (use `static styles = css\`...\``).
|
|
72
104
|
- Components: extend WebComponent, declare `static properties` (and `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.
|
|
73
|
-
- Server actions: *.server.ts files with one exported async function each
|
|
74
|
-
-
|
|
105
|
+
- Server actions: *.server.ts files with one exported async function each.
|
|
106
|
+
- Server-only code (@prisma/client, 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. lib/ holds both server-only infra (lib/prisma.server.ts) and browser-safe utilities (lib/utils/cn.ts with cn); apply the same rule per file.
|
|
107
|
+
- Directives: webjs ships only `unsafeHTML`, `live`, and `repeat`. Lit's `classMap` / `styleMap` / `ref` / `when` / `choose` / `guard` are NOT exported. Use plain template-literal expressions and lifecycle hooks instead.
|
|
75
108
|
- Context: import { createContext, ContextProvider, ContextConsumer } from '@webjsdev/core/context'
|
|
76
109
|
- Task: import { Task, TaskStatus } from '@webjsdev/core/task'
|
|
77
|
-
- Routing: file-based under app/ (page.ts, layout.ts, route.ts, middleware.ts)
|
|
78
|
-
|
|
79
|
-
## What NOT to do
|
|
80
|
-
|
|
81
|
-
- Don't introduce build tools or bundlers in the critical path
|
|
82
|
-
- Server-only code (@prisma/client, 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. lib/ holds both server-only infra (lib/prisma.server.ts) and browser-safe utilities (lib/utils/cn.ts with cn); apply the same rule per file.
|
|
83
|
-
- Don't use inline style="..." on components (use static styles = css`...`)
|
|
110
|
+
- Routing: file-based under app/ (page.ts, layout.ts, route.ts, middleware.ts).
|
|
84
111
|
- 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 (static properties + declare) are for HTML attributes and .prop=${...} hydration.
|
|
85
|
-
- Don't skip tests or documentation updates
|
|
112
|
+
- Don't skip tests or documentation updates.
|
|
@@ -8,7 +8,31 @@
|
|
|
8
8
|
- [ ] E2E tests added/updated for user-facing changes (`webjs test --e2e` passes)
|
|
9
9
|
- [ ] `webjs check` passes (no convention violations)
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Definition of done
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
Documentation MUST land on the same PR as the code change. Drift is how
|
|
14
|
+
a codebase rots. Walk every markdown file in the project (`git ls-files
|
|
15
|
+
'*.md'`) and ask whether this PR changed behaviour, surface, or
|
|
16
|
+
invariants it describes. For each row below, write `Updated <path>` or
|
|
17
|
+
`N/A because <reason>`. Reviewers should reject the PR if this section
|
|
18
|
+
is left as the template default. See the **Definition of done** section
|
|
19
|
+
in [`CONVENTIONS.md`](../CONVENTIONS.md) for the full guidance.
|
|
20
|
+
|
|
21
|
+
- [ ] **Tests.** Unit coverage for logic. Real-browser coverage for
|
|
22
|
+
user-facing behaviour.
|
|
23
|
+
- [ ] **Every markdown file in the project** that describes the
|
|
24
|
+
changed surface. Common cases (non-exhaustive): `AGENTS.md` (root
|
|
25
|
+
+ nested), `CONVENTIONS.md`, `README.md` (root + nested),
|
|
26
|
+
`CHANGELOG.md`, `docs/**/*.md`, `agent-docs/**/*.md`,
|
|
27
|
+
`.github/*.md`. The rule is generative: if a markdown file in
|
|
28
|
+
this project mentions a thing this PR changed, it gets touched
|
|
29
|
+
on this PR.
|
|
30
|
+
- [ ] **`website/`** (if the project has one). Marketing copy on the
|
|
31
|
+
landing or pricing page when the change touches a claim made
|
|
32
|
+
there.
|
|
33
|
+
- [ ] **Scaffold scripts / codegen** (if the project has any). Updated
|
|
34
|
+
when the change affects what new instances generate.
|
|
35
|
+
- [ ] **Pre-merge self-review loop.** Ran N rounds; last round clean.
|
|
36
|
+
Skip only for one-line trivial changes. See the **Pre-merge
|
|
37
|
+
self-review loop** section in [`CONVENTIONS.md`](../CONVENTIONS.md)
|
|
38
|
+
for the prompt template and reporting contract.
|
|
@@ -21,8 +21,33 @@ if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
|
|
|
21
21
|
exit 1
|
|
22
22
|
fi
|
|
23
23
|
|
|
24
|
+
# Require a test to accompany app-code changes. Tool-agnostic floor: a
|
|
25
|
+
# commit that stages app code (app/, modules/, components/, lib/) with no
|
|
26
|
+
# test (test/** or *.test.* / *.spec.*) is blocked. The full suite below
|
|
27
|
+
# proves tests PASS; this proves a test was WRITTEN. Bypass for a genuine
|
|
28
|
+
# non-code commit with WEBJS_NO_TEST_GATE=1, or --no-verify in emergencies.
|
|
29
|
+
if [ "${WEBJS_NO_TEST_GATE:-}" != "1" ]; then
|
|
30
|
+
STAGED=$(git diff --cached --name-only 2>/dev/null)
|
|
31
|
+
APP_CODE=$(printf '%s\n' "$STAGED" | grep -E '^(app|modules|components|lib)/.*\.([mc]?[jt]sx?)$' || true)
|
|
32
|
+
if [ -n "$APP_CODE" ]; then
|
|
33
|
+
TESTS=$(printf '%s\n' "$STAGED" | grep -E '(^|/)test/|\.test\.[mc]?[jt]sx?$|\.spec\.[mc]?[jt]sx?$' || true)
|
|
34
|
+
if [ -z "$TESTS" ]; then
|
|
35
|
+
echo ""
|
|
36
|
+
echo "ERROR: app code changed but no test is staged."
|
|
37
|
+
echo "Every change ships with a test. Add or update the test that"
|
|
38
|
+
echo "proves the new behaviour (a browser/e2e test for interactive"
|
|
39
|
+
echo "or component code), then git add it."
|
|
40
|
+
echo ""
|
|
41
|
+
echo "Genuine non-code commit? WEBJS_NO_TEST_GATE=1 git commit ..."
|
|
42
|
+
echo "Emergency bypass: git commit --no-verify"
|
|
43
|
+
echo ""
|
|
44
|
+
exit 1
|
|
45
|
+
fi
|
|
46
|
+
fi
|
|
47
|
+
fi
|
|
48
|
+
|
|
24
49
|
# webjs test + webjs check on every commit. Tool-agnostic enforcement:
|
|
25
|
-
# fires regardless of which agent (Claude, Cursor,
|
|
50
|
+
# fires regardless of which agent (Claude, Cursor, Antigravity, Copilot,
|
|
26
51
|
# human) is making the commit. Skipped if the CLI is not yet installed
|
|
27
52
|
# (fresh clone before npm install).
|
|
28
53
|
if command -v webjs >/dev/null 2>&1 || [ -x "node_modules/.bin/webjs" ]; then
|
package/templates/AGENTS.md
CHANGED
|
@@ -301,6 +301,20 @@ In Docker / Railway, prefer `npm start` (or `node node_modules/.bin/npm
|
|
|
301
301
|
start`) as the CMD over `node ... webjs.js start ...`. The npm form
|
|
302
302
|
fires `prestart`; the direct binary form skips it.
|
|
303
303
|
|
|
304
|
+
**Health and readiness probes.** Every webjs server answers two endpoints:
|
|
305
|
+
`/__webjs/health` (liveness, 200 once the process is listening) and
|
|
306
|
+
`/__webjs/ready` (readiness, 503 until the instance is fully warm, then 200).
|
|
307
|
+
Fully warm means the deterministic analysis AND the first vendor attempt have
|
|
308
|
+
both completed, so the importmap and its build id are settled. Point your
|
|
309
|
+
platform's readiness check at `/__webjs/ready` so it holds traffic off a
|
|
310
|
+
not-yet-warmed instance instead of routing the first user request into the cold
|
|
311
|
+
analysis or the brief window where the importmap is still resolving. On
|
|
312
|
+
Railway, set `"healthcheckPath": "/__webjs/ready"` under `deploy` in
|
|
313
|
+
`railway.json`. For dependency-aware
|
|
314
|
+
readiness (gate on a live DB ping), add an optional `readiness.{js,ts}` at the
|
|
315
|
+
app root that default-exports an async check; `/__webjs/ready` runs it once warm
|
|
316
|
+
and reports 503 if it returns `false` or throws.
|
|
317
|
+
|
|
304
318
|
Scripts:
|
|
305
319
|
|
|
306
320
|
- `npm run db:migrate`: `prisma migrate dev` (dev-time schema changes + migration + generate)
|
|
@@ -320,6 +334,87 @@ const users = await prisma.user.findMany();
|
|
|
320
334
|
To switch to Postgres or MySQL: change `provider` in `prisma/schema.prisma`
|
|
321
335
|
and the `DATABASE_URL` in `.env`.
|
|
322
336
|
|
|
337
|
+
## NPM packages (vendor pipeline)
|
|
338
|
+
|
|
339
|
+
Adding a third-party npm package follows the same `npm install` flow
|
|
340
|
+
as any Node project, with one webjs-specific concern: how the BROWSER
|
|
341
|
+
fetches that package.
|
|
342
|
+
|
|
343
|
+
```sh
|
|
344
|
+
npm install dayjs # standard npm install
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Now write `import dayjs from 'dayjs'` in any component or page. The
|
|
348
|
+
import works in dev immediately. webjs's scanner discovers bare
|
|
349
|
+
imports on the first request (memoized for the process) and asks
|
|
350
|
+
`api.jspm.io` to resolve them to CDN URLs (jspm.io serves pre-bundled
|
|
351
|
+
ESM for every npm package). The browser fetches the bundle directly
|
|
352
|
+
from `https://ga.jspm.io`.
|
|
353
|
+
|
|
354
|
+
**For production deploys**, run `webjs vendor pin` once and commit
|
|
355
|
+
the result:
|
|
356
|
+
|
|
357
|
+
```sh
|
|
358
|
+
webjs vendor pin # writes .webjs/vendor/importmap.json
|
|
359
|
+
git add .webjs/vendor/
|
|
360
|
+
git commit -m "vendor dayjs"
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
The pin file holds the resolved jspm.io URLs. Server reads it from
|
|
364
|
+
disk on the first request (memoized); no `api.jspm.io` call needed in
|
|
365
|
+
production. Deterministic across deploys.
|
|
366
|
+
|
|
367
|
+
**For offline-capable / strict-CSP production**, use `--download`:
|
|
368
|
+
|
|
369
|
+
```sh
|
|
370
|
+
webjs vendor pin --download # also vendors bundle bytes locally
|
|
371
|
+
git add .webjs/vendor/
|
|
372
|
+
git commit -m "vendor + download dayjs"
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Bundle files land in `.webjs/vendor/<pkg>@<version>.js`. importmap
|
|
376
|
+
points at local `/__webjs/vendor/` paths. Browser fetches from your
|
|
377
|
+
own origin. Suitable for `script-src 'self'` CSP, air-gapped deploys,
|
|
378
|
+
or compliance environments. See [docs.webjs.com Deployment → CSP](https://docs.webjs.com/docs/deployment#csp).
|
|
379
|
+
|
|
380
|
+
**Other CLI commands:**
|
|
381
|
+
|
|
382
|
+
```sh
|
|
383
|
+
webjs vendor list # show pinned packages with versions
|
|
384
|
+
webjs vendor unpin <pkg> # remove one entry from pin file
|
|
385
|
+
webjs vendor audit # npm security advisories against pinned versions
|
|
386
|
+
webjs vendor outdated # list pinned packages with newer versions on npm
|
|
387
|
+
webjs vendor update # re-pin every outdated package to its latest
|
|
388
|
+
|
|
389
|
+
# Switch CDN at pin time (default: jspm.io). Resolver options:
|
|
390
|
+
# jspm, jsdelivr, unpkg, skypack. Useful for jspm.io incident response.
|
|
391
|
+
webjs vendor pin --from jsdelivr
|
|
392
|
+
webjs vendor update --from jsdelivr
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Same posture as Rails 7 + importmap-rails: explicit pin command,
|
|
396
|
+
committed manifest, optional `--download` for full offline capability,
|
|
397
|
+
and a `--from` knob to swap the resolver CDN if jspm.io has an
|
|
398
|
+
incident.
|
|
399
|
+
|
|
400
|
+
**Don't auto-run `webjs vendor pin` in `predev` / `prestart`.** Auto-pin
|
|
401
|
+
would silently churn the committed importmap.json as jspm.io resolves
|
|
402
|
+
URLs or transitive deps drift. Pin is a deliberate developer action,
|
|
403
|
+
like `npm install` itself.
|
|
404
|
+
|
|
405
|
+
**Do NOT modify the `.webjs/` lines in `.gitignore` / `.dockerignore`.**
|
|
406
|
+
The scaffolded pattern is three lines (`.webjs/*` + `!.webjs/vendor/`
|
|
407
|
+
+ `!.webjs/vendor/**`) and is structurally load-bearing. Collapsing it
|
|
408
|
+
to a single `.webjs/` excludes the parent directory; once the parent
|
|
409
|
+
is excluded, git cannot re-include `.webjs/vendor/` via a child
|
|
410
|
+
negation (gitignore semantics: parent exclusion blocks child
|
|
411
|
+
negations). The breakage is invisible: `webjs vendor pin` runs, writes
|
|
412
|
+
files, and git silently ignores them. Production then has no
|
|
413
|
+
importmap.json and the server falls back to calling api.jspm.io on
|
|
414
|
+
every cold start. The `gitignore-vendor-not-ignored` lint rule
|
|
415
|
+
(`webjs check`) verifies the pattern with `git check-ignore` and will
|
|
416
|
+
fail CI if it regresses.
|
|
417
|
+
|
|
323
418
|
## Imports
|
|
324
419
|
|
|
325
420
|
```ts
|
|
@@ -770,9 +865,9 @@ composition, so a nested shell ends up dropped by the HTML parser.
|
|
|
770
865
|
```
|
|
771
866
|
|
|
772
867
|
If you turn `erasableSyntaxOnly` off and use non-erasable syntax,
|
|
773
|
-
the dev server
|
|
774
|
-
|
|
775
|
-
|
|
868
|
+
the dev server fails at strip time and returns a 500 naming the
|
|
869
|
+
file and pointing at the `no-non-erasable-typescript` lint rule.
|
|
870
|
+
webjs is buildless end-to-end and has no bundler fallback. The
|
|
776
871
|
`erasable-typescript-only` convention check warns when the flag
|
|
777
872
|
is missing or set to false.
|
|
778
873
|
9. **No em-dashes (U+2014) anywhere, and no hyphen or semicolon used
|
|
@@ -788,8 +883,15 @@ composition, so a nested shell ends up dropped by the HTML parser.
|
|
|
788
883
|
## Workflow expectations for AI agents
|
|
789
884
|
|
|
790
885
|
1. Branch before editing. Never push to `main` directly.
|
|
791
|
-
2. Every code change comes with
|
|
792
|
-
|
|
886
|
+
2. Every code change comes with a test, AGENTS.md / docs updates if the
|
|
887
|
+
feature surface changed, `webjs check` passing. A unit test is not
|
|
888
|
+
always enough: a component, hydration, the client router, or a server
|
|
889
|
+
action called from the client needs a browser test
|
|
890
|
+
(`webjs test --browser`) asserting the behaviour in a real browser. A
|
|
891
|
+
commit that stages app code (`app/`, `modules/`, `components/`, `lib/`)
|
|
892
|
+
with no test is blocked by `.claude/hooks/require-tests-with-src.sh`
|
|
893
|
+
and the `.hooks/pre-commit` floor (bypass a genuine non-code commit
|
|
894
|
+
with `WEBJS_NO_TEST_GATE=1`).
|
|
793
895
|
3. Commit and push **per logical unit**, not at the end. A logical unit is one
|
|
794
896
|
feature, one fix, one rename, one doc rewrite. If you have 5+ unstaged files
|
|
795
897
|
spanning different concerns, commit the current group before continuing.
|
|
@@ -802,14 +904,25 @@ composition, so a nested shell ends up dropped by the HTML parser.
|
|
|
802
904
|
| Gemini CLI | `.gemini/hooks/nudge-uncommitted.sh` (`AfterTool`) | `.gemini/settings.json` |
|
|
803
905
|
| Cursor 1.7+ | `.cursor/hooks/nudge-uncommitted.sh` (`afterFileEdit`) | `.cursor/hooks.json` |
|
|
804
906
|
| OpenCode | `.opencode/plugins/nudge-uncommitted.ts` (`tool.execute.after`) | `.opencode/plugins/` |
|
|
805
|
-
|
|
|
907
|
+
| Antigravity (Google) | text rule only (post-write hooks not yet exposed) | `.agents/rules/workflow.md` |
|
|
806
908
|
| GitHub Copilot | text rule only (no hooks API) | `.github/copilot-instructions.md` |
|
|
807
|
-
| Google Antigravity | text rule only (no hooks API) | `AGENTS.md` |
|
|
808
909
|
|
|
809
910
|
Tool-agnostic fallback: `.hooks/pre-commit` runs `webjs test` + `webjs check`
|
|
810
911
|
on every commit, regardless of which agent (or human) made it. No AI
|
|
811
912
|
attribution trailers in commit messages.
|
|
812
|
-
4.
|
|
913
|
+
4. Run the **pre-merge self-review loop** before signaling the PR is
|
|
914
|
+
ready. After committing the work, trigger a fresh-context review
|
|
915
|
+
pass (a new chat / composer tab / subagent / Cascade thread
|
|
916
|
+
depending on your tool) and iterate fix-then-review rounds until
|
|
917
|
+
one round finds zero issues. Minimum two rounds; rotate focus each
|
|
918
|
+
round so the reviewer does not rediscover the same surface twice.
|
|
919
|
+
Skip the loop only for one-line trivial changes; skipping on a
|
|
920
|
+
change that touches logic, public surface, build, security, or
|
|
921
|
+
multiple files is the exact failure mode the loop exists to
|
|
922
|
+
prevent. The full rule, prompt template, and reporting contract
|
|
923
|
+
live in the **Pre-merge self-review loop** section of
|
|
924
|
+
`CONVENTIONS.md`.
|
|
925
|
+
5. When unsure how a framework feature works, `grep` or `cat` the
|
|
813
926
|
relevant `node_modules/@webjsdev/*/src/` file before asking the user.
|
|
814
927
|
|
|
815
928
|
Project-specific conventions and overrides live in
|