@webjsdev/cli 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,39 @@
1
+ #!/bin/bash
2
+ #
3
+ # guard-branch-context.sh - Claude Code PreToolUse hook
4
+ #
5
+ # Rules:
6
+ # - On main/master → ask (agent should create a feature branch first)
7
+ # - On any other branch → allow (feature branches are free to edit)
8
+ # - Bypass mode → allow everything
9
+
10
+ INPUT=$(cat /dev/stdin)
11
+
12
+ # Bypass mode - full autonomy
13
+ SETTINGS="$HOME/.claude/settings.json"
14
+ if [ -f "$SETTINGS" ]; then
15
+ BYPASS=$(jq -r '.skipDangerousModePermissionPrompt // false' "$SETTINGS" 2>/dev/null)
16
+ if [ "$BYPASS" = "true" ]; then
17
+ exit 0
18
+ fi
19
+ fi
20
+
21
+ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
22
+ exit 0
23
+ fi
24
+
25
+ BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
26
+ [ -z "$BRANCH" ] && exit 0
27
+
28
+ if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
29
+ jq -n --arg reason "You are on '$BRANCH'. Create a feature branch first (git checkout -b feature/<name>), or approve to edit on '$BRANCH'." '{
30
+ hookSpecificOutput: {
31
+ hookEventName: "PreToolUse",
32
+ permissionDecision: "ask",
33
+ permissionDecisionReason: $reason
34
+ }
35
+ }'
36
+ exit 0
37
+ fi
38
+
39
+ exit 0
@@ -0,0 +1,46 @@
1
+ #!/bin/bash
2
+ #
3
+ # Claude Code PostToolUse hook.
4
+ #
5
+ # After each Edit, Write, MultiEdit, or NotebookEdit, counts
6
+ # uncommitted changes in the working tree. When the count
7
+ # crosses a threshold (default 4, override with the
8
+ # WEBJS_COMMIT_NUDGE_THRESHOLD env var), injects a reminder
9
+ # into the model's context via hookSpecificOutput.
10
+ #
11
+ # Soft nudge. Does NOT block the edit. The goal is to keep
12
+ # the agent honest about the "commit per logical unit" rule,
13
+ # not to interrupt valid work.
14
+ #
15
+ # Skipped on main/master and outside a git work tree.
16
+
17
+ set -e
18
+
19
+ THRESHOLD="${WEBJS_COMMIT_NUDGE_THRESHOLD:-4}"
20
+
21
+ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
22
+ exit 0
23
+ fi
24
+
25
+ BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
26
+ if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
27
+ exit 0
28
+ fi
29
+
30
+ # Read stdin so we don't break Claude Code's hook contract.
31
+ cat /dev/stdin >/dev/null 2>&1 || true
32
+
33
+ CHANGED=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
34
+
35
+ if [ -z "$CHANGED" ] || [ "$CHANGED" -lt "$THRESHOLD" ]; then
36
+ exit 0
37
+ fi
38
+
39
+ REASON="You have ${CHANGED} uncommitted changes on '${BRANCH}'. The webjs convention is small, focused commits per logical unit (one feature, one fix, one rename, one doc rewrite). Before continuing with more edits, group the current changes into a meaningful commit. See AGENTS.md \"Git workflow\" for the rule and the rationale. To raise the threshold for this hook in long-running tasks, set WEBJS_COMMIT_NUDGE_THRESHOLD."
40
+
41
+ jq -n --arg ctx "$REASON" '{
42
+ hookSpecificOutput: {
43
+ hookEventName: "PostToolUse",
44
+ additionalContext: $ctx
45
+ }
46
+ }'
@@ -0,0 +1,35 @@
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit|Bash",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": ".claude/hooks/block-prose-punctuation.sh"
10
+ }
11
+ ]
12
+ },
13
+ {
14
+ "matcher": "Edit|Write",
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": ".claude/hooks/guard-branch-context.sh"
19
+ }
20
+ ]
21
+ }
22
+ ],
23
+ "PostToolUse": [
24
+ {
25
+ "matcher": "Write|Edit|MultiEdit|NotebookEdit",
26
+ "hooks": [
27
+ {
28
+ "type": "command",
29
+ "command": ".claude/hooks/nudge-uncommitted.sh"
30
+ }
31
+ ]
32
+ }
33
+ ]
34
+ }
35
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "mcpServers": {
3
+ "playwright": {
4
+ "type": "stdio",
5
+ "command": "npx",
6
+ "args": ["@playwright/mcp@latest"]
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,38 @@
1
+ #!/bin/bash
2
+ #
3
+ # Cursor afterFileEdit hook.
4
+ #
5
+ # Counterpart of .claude/hooks/nudge-uncommitted.sh. After each
6
+ # file edit, counts uncommitted changes in the working tree.
7
+ # When the count crosses a threshold (default 4, override with
8
+ # WEBJS_COMMIT_NUDGE_THRESHOLD), injects a reminder via the
9
+ # top-level additional_context field (snake_case, unlike Claude
10
+ # Code's nested hookSpecificOutput.additionalContext).
11
+ #
12
+ # Soft nudge. Exit 0 always. Skipped on main/master and outside
13
+ # a git work tree.
14
+
15
+ set -e
16
+
17
+ THRESHOLD="${WEBJS_COMMIT_NUDGE_THRESHOLD:-4}"
18
+
19
+ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
20
+ exit 0
21
+ fi
22
+
23
+ BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
24
+ if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
25
+ exit 0
26
+ fi
27
+
28
+ cat /dev/stdin >/dev/null 2>&1 || true
29
+
30
+ CHANGED=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
31
+
32
+ if [ -z "$CHANGED" ] || [ "$CHANGED" -lt "$THRESHOLD" ]; then
33
+ exit 0
34
+ fi
35
+
36
+ REASON="You have ${CHANGED} uncommitted changes on '${BRANCH}'. The webjs convention is small, focused commits per logical unit (one feature, one fix, one rename, one doc rewrite). Before continuing with more edits, group the current changes into a meaningful commit. See AGENTS.md \"Git workflow\" for the rule and the rationale. To raise the threshold for this hook in long-running tasks, set WEBJS_COMMIT_NUDGE_THRESHOLD."
37
+
38
+ jq -n --arg ctx "$REASON" '{ additional_context: $ctx }'
@@ -0,0 +1,8 @@
1
+ {
2
+ "version": 1,
3
+ "hooks": {
4
+ "afterFileEdit": [
5
+ { "command": ".cursor/hooks/nudge-uncommitted.sh" }
6
+ ]
7
+ }
8
+ }
@@ -0,0 +1,99 @@
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.com**.
7
+
8
+ ## Persistence + scaffold rules (non-negotiable)
9
+
10
+ - **Use Prisma + SQLite for data, never JSON files.** It's already wired up
11
+ (`prisma/schema.prisma`, `lib/prisma.server.ts`, `npm run db:migrate`). For ANY
12
+ data the app stores (todos, posts, messages, products, comments…),
13
+ define a Prisma model. 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. `webjs check`'s
16
+ `no-json-data-files` rule will fail the build if you do.
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
+ - **Only three templates exist:** `webjs create <name>` (default
22
+ full-stack), `--template api`, `--template saas`. The CLI rejects any
23
+ other `--template` value. Pick:
24
+ - Any product UI (todo, blog, dashboard, marketplace, social…) → default
25
+ - HTTP/JSON API only, no UI → `--template api`
26
+ - Auth / login / signup / SaaS → `--template saas`
27
+
28
+ ## Before starting ANY work
29
+
30
+ FIRST, before writing any code:
31
+ 1. Run `git branch --show-current` to check the branch.
32
+ - If on main/master: STOP. Ask the user which branch to use, or create
33
+ one with `git checkout -b feature/<name>`.
34
+ - If on a feature branch: verify it matches the current task. Ask if unsure.
35
+ 2. Sync with parent: `git fetch origin && git log HEAD..origin/main --oneline`
36
+ - If upstream has new commits: `git rebase origin/main` before starting.
37
+ - Resolve any conflicts before proceeding with the task.
38
+
39
+ ## Autonomous mode (sandbox / no-prompt mode)
40
+
41
+ If running without interactive approval, auto-decide:
42
+ - On main? Auto-create feature/<task-slug> branch
43
+ - Parent has new commits? Auto-rebase before starting
44
+ - Merge? Auto-merge in autonomous mode, delete feature branches after
45
+ - Commit message? Auto-generate (meaningful, no AI attribution)
46
+ - Tests failing? Fix them. Convention violations? Fix them.
47
+ Quality bar stays the same - just no blocking on questions.
48
+
49
+ ## Mandatory workflow (never skip)
50
+
51
+ 1. TESTS: Server tests in test/<feature>/ (node:test), browser tests in
52
+ test/<feature>/browser/ (WTR + Playwright, real Chromium). Run `webjs test`
53
+ after every change. Never deliver code without passing tests.
54
+
55
+ 2. DOCS: Update AGENTS.md for API changes. Update docs/ and website/ if
56
+ they exist. The user should never have to ask for tests or docs.
57
+
58
+ 3. CONVENTIONS: Run `webjs check` and fix violations before committing.
59
+
60
+ ## Git rules
61
+
62
+ - COMMIT AND PUSH PER LOGICAL UNIT, NOT AT THE END. One feature, one fix,
63
+ one rename, one doc rewrite per commit. Always `git push` after
64
+ committing. This is automatic.
65
+ - HARD LIMIT: if you have 5+ unstaged files spanning different concerns,
66
+ commit before continuing. The Claude Code hook at
67
+ `.claude/hooks/nudge-uncommitted.sh` fires at threshold 4. Cursor users
68
+ should self-enforce the same rule. Batching multiple logical units into
69
+ one commit is the failure mode this rule exists to prevent.
70
+ - Write meaningful commit messages: what changed and why, not "update files"
71
+ - NEVER add "Co-Authored-By", "Generated by", "AI-assisted" or similar
72
+ attribution trailers to commits
73
+ - NEVER use em-dashes (U+2014), a hyphen-as-pause (` - `), or a
74
+ semicolon-as-pause (` ; `) in commit messages or anywhere else.
75
+ Rewrite the sentence so no pause-punctuation crutch is needed.
76
+ Use a period, comma, colon, parentheses, or a restructured phrasing.
77
+ Plain hyphens stay fine in compound words, CLI flags, filenames,
78
+ and ranges. Semicolons stay fine inside code
79
+ - Work on feature branches, not main
80
+ - NEVER push directly to main - create a pull request
81
+ - NEVER merge any branch without explicit user permission. Always ask:
82
+ "Ready to merge <branch> into <target>? Delete or keep <branch> after?"
83
+ Wait for approval AND the delete/keep preference before proceeding.
84
+ This applies to ALL merges, not just merges into main.
85
+ - Run tests before every commit
86
+ - Keep commits small and focused
87
+
88
+ ## Framework rules
89
+
90
+ - 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 falls back to esbuild + inline sourcemap for those files (~3x wire bytes per request).
92
+ - Web components with shadow DOM: use `static styles = css` not inline styles
93
+ - One function per server action file (*.server.ts)
94
+ - Components must call customElements.define('tag', Class)
95
+ - 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 - use plain template-literal expressions (`class=${cond ? 'a' : 'b'}`, `${cond ? a : b}`) and lifecycle hooks (`this.query('#el')` in `firstUpdated`) instead.
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 (read SSR-meaningful defaults in `constructor()`, not `connectedCallback` - the server doesn't call lifecycle hooks). Initial data for components comes from the page function (server-side fetch + pass as attribute/property), NOT from `fetch` calls in `connectedCallback`. For write-paths, prefer `<form action=...>` + server action over `fetch` + click handler - the framework upgrades plain forms to partial-swap submissions automatically.
98
+ - **Client navigation is auto-magic.** Real `<a href>` and `<form action>` get partial-swap behavior with no opt-in. Layouts persist across navigation - put shared chrome (sidenav, header) in `layout.ts`, 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
+ - See AGENTS.md for the complete directive decision guide
@@ -0,0 +1,18 @@
1
+ # EditorConfig - consistent formatting across editors and AI agents
2
+ # https://editorconfig.org
3
+
4
+ root = true
5
+
6
+ [*]
7
+ indent_style = space
8
+ indent_size = 2
9
+ end_of_line = lf
10
+ charset = utf-8
11
+ trim_trailing_whitespace = true
12
+ insert_final_newline = true
13
+
14
+ [*.md]
15
+ trim_trailing_whitespace = false
16
+
17
+ [Makefile]
18
+ indent_style = tab
@@ -0,0 +1,27 @@
1
+ # webjs environment variables
2
+ # Copy to .env and fill in your values: cp .env.example .env
3
+ #
4
+ # Opinionated defaults: set these and the framework auto-configures.
5
+
6
+ # ── Server ──────────────────────────────────────────────────────────
7
+ PORT=3000
8
+
9
+ # ── Auth (required for authentication) ──────────────────────────────
10
+ # Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
11
+ AUTH_SECRET=
12
+
13
+ # ── OAuth providers (optional - enable the ones you need) ───────────
14
+ # AUTH_GOOGLE_ID=
15
+ # AUTH_GOOGLE_SECRET=
16
+ # AUTH_GITHUB_ID=
17
+ # AUTH_GITHUB_SECRET=
18
+
19
+ # ── Redis (production scaling) ──────────────────────────────────────
20
+ # Set this and cache, sessions, rate limiting, and pub/sub all
21
+ # automatically use Redis. Without it, everything uses in-memory
22
+ # defaults (great for development).
23
+ # REDIS_URL=redis://localhost:6379
24
+
25
+ # ── Database ────────────────────────────────────────────────────────
26
+ # Used by Prisma. SQLite for dev, PostgreSQL/MySQL for production.
27
+ DATABASE_URL=file:./dev.db
@@ -0,0 +1,42 @@
1
+ #!/bin/bash
2
+ #
3
+ # Gemini CLI AfterTool hook.
4
+ #
5
+ # Counterpart of .claude/hooks/nudge-uncommitted.sh. After each
6
+ # write_file or replace, counts uncommitted changes in the working
7
+ # tree. When the count crosses a threshold (default 4, override
8
+ # with the WEBJS_COMMIT_NUDGE_THRESHOLD env var), injects a
9
+ # reminder via hookSpecificOutput.additionalContext (same shape
10
+ # as Claude Code).
11
+ #
12
+ # Soft nudge. Does NOT block the edit (exit 0). Skipped on
13
+ # main/master and outside a git work tree.
14
+
15
+ set -e
16
+
17
+ THRESHOLD="${WEBJS_COMMIT_NUDGE_THRESHOLD:-4}"
18
+
19
+ if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
20
+ exit 0
21
+ fi
22
+
23
+ BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo "")
24
+ if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
25
+ exit 0
26
+ fi
27
+
28
+ cat /dev/stdin >/dev/null 2>&1 || true
29
+
30
+ CHANGED=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
31
+
32
+ if [ -z "$CHANGED" ] || [ "$CHANGED" -lt "$THRESHOLD" ]; then
33
+ exit 0
34
+ fi
35
+
36
+ REASON="You have ${CHANGED} uncommitted changes on '${BRANCH}'. The webjs convention is small, focused commits per logical unit (one feature, one fix, one rename, one doc rewrite). Before continuing with more edits, group the current changes into a meaningful commit. See AGENTS.md \"Git workflow\" for the rule and the rationale. To raise the threshold for this hook in long-running tasks, set WEBJS_COMMIT_NUDGE_THRESHOLD."
37
+
38
+ jq -n --arg ctx "$REASON" '{
39
+ hookSpecificOutput: {
40
+ additionalContext: $ctx
41
+ }
42
+ }'
@@ -0,0 +1,15 @@
1
+ {
2
+ "hooks": {
3
+ "AfterTool": [
4
+ {
5
+ "matcher": "write_file|replace",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": ".gemini/hooks/nudge-uncommitted.sh"
10
+ }
11
+ ]
12
+ }
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,85 @@
1
+ # GitHub Copilot Instructions: 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. When AGENTS.md doesn't cover what you need,
6
+ the full hosted docs are at **https://docs.webjs.com**.
7
+
8
+ ## Persistence + scaffold rules (non-negotiable)
9
+
10
+ - **Use Prisma + SQLite for data, never JSON files.** It's already wired up
11
+ (`prisma/schema.prisma`, `lib/prisma.server.ts`, `npm run db:migrate`). For ANY
12
+ data the app stores (todos, posts, messages, products, comments…),
13
+ define a Prisma model. 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. `webjs check`'s
16
+ `no-json-data-files` rule will fail the build if you do.
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
+ - **Only three templates exist:** `webjs create <name>` (default
22
+ full-stack), `--template api`, `--template saas`. The CLI rejects any
23
+ other `--template` value. Pick:
24
+ - Any product UI (todo, blog, dashboard, marketplace, social…) → default
25
+ - HTTP/JSON API only, no UI → `--template api`
26
+ - Auth / login / signup / SaaS → `--template saas`
27
+
28
+ ## Before starting ANY work
29
+
30
+ FIRST, before writing any code:
31
+ 1. Check `git branch --show-current`.
32
+ - If on main/master: create a feature branch before editing.
33
+ - If on a feature branch: verify it matches the task at hand.
34
+ 2. Sync: `git fetch origin && git rebase origin/main` if behind.
35
+
36
+ ## Autonomous mode
37
+
38
+ If running without interactive approval (sandbox, auto-approve, etc.):
39
+ - On main? Auto-create feature/<task-slug> branch
40
+ - Parent behind? Auto-rebase. Merge? Auto-merge + delete feature branches.
41
+ - Auto-generate meaningful commit messages. Fix tests and violations.
42
+
43
+ ## Mandatory workflow
44
+
45
+ Every code change must include:
46
+ 1. Commit and push PER LOGICAL UNIT, not at the end. One feature, one fix,
47
+ one rename, one doc rewrite per commit. Always `git push` after
48
+ committing. Don't accumulate changes. If you have 5+ unstaged files
49
+ spanning different concerns, commit before continuing. The Claude Code
50
+ hook at `.claude/hooks/nudge-uncommitted.sh` enforces threshold 4 for
51
+ Claude users; Copilot users should self-enforce the same rule. Automatic.
52
+ 2. Server tests in test/<feature>/*.test.ts (node:test for actions, queries, utilities)
53
+ 3. Browser tests in test/<feature>/browser/*.test.js (WTR + Playwright, real Chromium)
54
+ 4. Documentation updates (AGENTS.md for API, docs/ for user guides)
55
+ 5. Convention validation: `webjs check` must pass
56
+
57
+ ## Git rules
58
+
59
+ - Commit after each logical unit of work
60
+ - Meaningful commit messages: what changed and why
61
+ - NEVER add Co-Authored-By or AI attribution trailers to commits
62
+ - Work on feature branches, create PRs, never push directly to main
63
+ - NEVER merge any branch without explicit user permission. Always ask:
64
+ "Ready to merge <branch> into <target>? Delete or keep <branch> after?"
65
+ Wait for approval AND the delete/keep preference. Applies to ALL merges.
66
+ - Run `webjs test` before every commit
67
+
68
+ ## Code patterns
69
+
70
+ - **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 falls back to esbuild + inline sourcemap for those files (~3x wire bytes per request).
71
+ - Tagged template: html`<div>${value}</div>` with css`...` for styles
72
+ - 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
+ - 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
+ - Context: import { createContext, ContextProvider, ContextConsumer } from '@webjsdev/core/context'
76
+ - 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`...`)
84
+ - 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
@@ -0,0 +1,14 @@
1
+ ## Summary
2
+
3
+ <!-- What does this PR do? 1-3 bullet points. -->
4
+
5
+ ## Test plan
6
+
7
+ - [ ] Unit tests added/updated (`webjs test` passes)
8
+ - [ ] E2E tests added/updated for user-facing changes (`webjs test --e2e` passes)
9
+ - [ ] `webjs check` passes (no convention violations)
10
+
11
+ ## Documentation
12
+
13
+ - [ ] AGENTS.md updated (if API surface changed)
14
+ - [ ] Docs updated (if docs/ exists and feature is documented)
@@ -0,0 +1,48 @@
1
+ #!/bin/bash
2
+ #
3
+ # pre-commit hook - blocks commits on main/master.
4
+ #
5
+ # No AI agent, no editor, no human can commit to main directly.
6
+ # Create a feature branch first. This is git-level enforcement.
7
+ #
8
+ # To bypass in emergencies: git commit --no-verify
9
+
10
+ BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null)
11
+
12
+ if [ "$BRANCH" = "main" ] || [ "$BRANCH" = "master" ]; then
13
+ echo ""
14
+ echo "ERROR: Cannot commit directly to '$BRANCH'."
15
+ echo ""
16
+ echo "Create a feature branch first:"
17
+ echo " git checkout -b feature/<name>"
18
+ echo ""
19
+ echo "To bypass (emergencies only): git commit --no-verify"
20
+ echo ""
21
+ exit 1
22
+ fi
23
+
24
+ # webjs test + webjs check on every commit. Tool-agnostic enforcement:
25
+ # fires regardless of which agent (Claude, Cursor, Windsurf, Copilot,
26
+ # human) is making the commit. Skipped if the CLI is not yet installed
27
+ # (fresh clone before npm install).
28
+ if command -v webjs >/dev/null 2>&1 || [ -x "node_modules/.bin/webjs" ]; then
29
+ echo "Running webjs test..."
30
+ if ! npx --no-install webjs test; then
31
+ echo ""
32
+ echo "ERROR: webjs test failed. Fix tests before committing."
33
+ echo "To bypass (emergencies only): git commit --no-verify"
34
+ echo ""
35
+ exit 1
36
+ fi
37
+
38
+ echo "Running webjs check..."
39
+ if ! npx --no-install webjs check; then
40
+ echo ""
41
+ echo "ERROR: webjs check failed. Fix convention violations before committing."
42
+ echo "To bypass (emergencies only): git commit --no-verify"
43
+ echo ""
44
+ exit 1
45
+ fi
46
+ fi
47
+
48
+ exit 0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * OpenCode commit-frequency nudge plugin.
3
+ *
4
+ * Counterpart of the Claude Code, Gemini CLI, and Cursor hooks in
5
+ * `.claude/hooks/`, `.gemini/hooks/`, and `.cursor/hooks/`. After
6
+ * each edit/write tool call, counts uncommitted changes in the
7
+ * working tree. When the count crosses a threshold (default 4,
8
+ * override with the WEBJS_COMMIT_NUDGE_THRESHOLD env var), appends
9
+ * a reminder to the tool result so the agent sees it on the next
10
+ * turn.
11
+ *
12
+ * Soft nudge by design. Does NOT block the edit. The goal is to
13
+ * keep the agent honest about the "commit per logical unit" rule,
14
+ * not to interrupt valid work.
15
+ *
16
+ * Skipped on main/master (different guard rules cover that) and
17
+ * outside a git work tree.
18
+ *
19
+ * Auto-discovered by OpenCode at startup. No opencode.json entry
20
+ * needed. Lives in .opencode/plugins/ at the project root.
21
+ *
22
+ * Docs: https://opencode.ai/docs/plugins/
23
+ */
24
+ import type { Plugin } from "@opencode-ai/plugin";
25
+
26
+ export const NudgeUncommitted: Plugin = async ({ $ }) => {
27
+ const THRESHOLD = Number(process.env.WEBJS_COMMIT_NUDGE_THRESHOLD ?? 4);
28
+
29
+ return {
30
+ "tool.execute.after": async (input, output) => {
31
+ if (input.tool !== "edit" && input.tool !== "write") return;
32
+
33
+ let branch = "";
34
+ try {
35
+ branch = (await $`git symbolic-ref --short HEAD`.text()).trim();
36
+ } catch {
37
+ return; // not in a git work tree
38
+ }
39
+ if (branch === "main" || branch === "master") return;
40
+
41
+ let changed = 0;
42
+ try {
43
+ const out = (await $`git status --porcelain`.text()).trim();
44
+ changed = out === "" ? 0 : out.split("\n").length;
45
+ } catch {
46
+ return;
47
+ }
48
+ if (changed < THRESHOLD) return;
49
+
50
+ const reason =
51
+ `[webjs] You have ${changed} uncommitted changes on '${branch}'. ` +
52
+ `The webjs convention is small, focused commits per logical unit ` +
53
+ `(one feature, one fix, one rename, one doc rewrite). Before ` +
54
+ `continuing with more edits, group the current changes into a ` +
55
+ `meaningful commit. See AGENTS.md "Git workflow" for the rule ` +
56
+ `and the rationale. To raise the threshold for this hook in ` +
57
+ `long-running tasks, set WEBJS_COMMIT_NUDGE_THRESHOLD.`;
58
+
59
+ output.output = output.output ? `${output.output}\n\n${reason}` : reason;
60
+ },
61
+ };
62
+ };