Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .claude/hooks/block-prose-punctuation.sh
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ fi
# positives (a wrongly blocked write), the same tradeoff as the rules above:
# e.g. a sentence-ending "built on webjs." is not flagged (trailing period),
# and the `bin/webjs.js` "webjs commands:" usage banner may rarely trip it.
webjs_cli='create|dev|start|test|check|db|ui|doctor|types|typecheck|mcp|vendor|help|add|init|generate|migrate|push|studio|seed|pin|unpin|list|audit|outdated|update|view|diff|info|build'
webjs_cli='create|dev|start|test|check|routes|db|ui|doctor|types|typecheck|mcp|vendor|help|version|add|init|generate|migrate|push|studio|seed|pin|unpin|list|audit|outdated|update|view|diff|info|build'

# Scan copy: drop fenced code blocks, inline code spans, and emphasis markers
# so a `webjs` inside code is never considered and **webjs** still matches.
Expand All @@ -288,9 +288,11 @@ brand_hits=$(printf '%s\n' "$brand_scan" \
if [ -n "$brand_hits" ]; then
# Drop lines whose "webjs" is a `webjs <subcommand>` CLI reference. The
# trailing class also admits a closing quote (" or ') so a package.json
# script value ending in a bare subcommand is a command, not brand prose.
# script value ending in a bare subcommand is a command, not brand prose,
# and an HTML tag boundary (< or >) so a docs `<h3>webjs routes</h3>` or
# `<code>webjs types</code>` reads as a command, not brand prose.
offending=$(printf '%s\n' "$brand_hits" \
| grep -vE "webjs[[:space:]]+(${webjs_cli})([[:space:]]|[.,:;)\"']|\$)" 2>/dev/null || true)
| grep -vE "webjs[[:space:]]+(${webjs_cli})([[:space:]]|[.,:;)\"'<>]|\$)" 2>/dev/null || true)
if [ -n "$offending" ]; then
cat >&2 <<'EOF'
BLOCKED: lowercase "webjs" naming the brand in prose.
Expand Down
5 changes: 4 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,9 +462,12 @@ webjs dev [--port N] [--no-hot] # dev server with live reload (node --watch o
webjs start [--port N] # prod server; source IS the runtime, plain HTTP/1.1 (reverse-proxy for TLS + HTTP/2). Runs webjs.start.before first (#550)
webjs test [--server] [--browser] [--watch]
webjs check [--rules] [--json] # correctness validator (report-only, no autofix); --json for an agent loop
webjs routes [--json] [--table] [--no-headers] # print the route table (path / owner file / methods, #975). Default tree; --json is byte-identical to the MCP list_routes tool; --no-headers drops the --table header for piping
webjs mcp # read-only MCP: routes, actions (RPC hashes), components, check
webjs doctor # project-health checklist (incl. a framework-resolve check that warns when @webjsdev/core can't be resolved from the app dir, the fresh-worktree-without-node_modules trap #954; a page/layout elision advisory); non-zero exit on a hard fail
webjs doctor [--json] [--strict] # project-health checklist (incl. a framework-resolve check that warns when @webjsdev/core can't be resolved from the app dir, the fresh-worktree-without-node_modules trap #954; a page/layout elision advisory); non-zero exit on a hard fail. --json emits `{ results, summary }` (results is the DoctorResult[], each carrying a stable code); --strict also fails the exit on warnings (#975)
webjs types # generate .webjs/routes.d.ts (typed Route union + per-route params, #258)
webjs version # print the installed @webjsdev/cli version (also: webjs --version / -v, #975)
webjs help [command] # full usage banner, or per-command usage + Options + Examples (e.g. webjs help routes, #975). Flag forms: webjs --help / -h (banner), webjs <command> --help / -h (that command). typecheck/db/ui --help forward to their wrapped tool; an unknown topic exits 1
webjs typecheck [tsc args...] # the project's own tsc --noEmit
webjs create <name> [--template api|saas]
webjs db <generate|migrate|push|studio|seed> # wraps drizzle-kit (+ runs db/seed.server.ts)
Expand Down
25 changes: 25 additions & 0 deletions docs/app/docs/configuration/page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,31 @@ webjs db push # drizzle-kit push
webjs db studio # drizzle-kit studio
webjs db seed # run db/seed.server.ts</pre>

<h3>webjs routes</h3>
<pre>webjs routes # a grouped tree of pages + route handlers
webjs routes --table # aligned KIND / PATH / METHODS / FILE columns
webjs routes --table --no-headers # same, without the header row (pipe-friendly)
webjs routes --json # structured JSON (matches the MCP list_routes tool)</pre>
<p>Prints the route table to stdout: every page (path, owner file, dynamic params) and every <code>route.&#123;js,ts&#125;</code> handler (path, owner file, HTTP methods). It reuses the same route walker that backs the typed-routes generator and the dev server, so it always reflects exactly what the framework will serve. The <code>--json</code> shape is byte-identical to the read-only MCP <code>list_routes</code> tool, so an agent gets the same data whether it shells out or calls the MCP.</p>

<h3>webjs doctor</h3>
<pre>webjs doctor # human-readable project-health checklist
webjs doctor --json # structured results (each with a stable code) + a summary
webjs doctor --strict # also fail the exit on warnings, not just hard failures</pre>
<p>Verifies project health: the Node version floor, <code>erasableSyntaxOnly</code>, <code>.env</code> drift, vendor-pin freshness, importmap coherence, <code>@webjsdev/*</code> version coherence, framework resolvability, the git hook, and a page/layout elision advisory. Each result carries a stable machine <code>code</code> (for example <code>NODE_VERSION</code>, <code>TSCONFIG_ERASABLE</code>, <code>IMPORTMAP_COHERENCE</code>) so an agent branches on the failure kind, not the message text. The <code>--json</code> payload is an object <code>&#123; results, summary &#125;</code> (the <code>results</code> array holds the per-check objects, each with its <code>code</code>). By default the exit is non-zero only on a hard <em>toolchain</em> failure; <code>--strict</code> also fails on warnings, so it can gate a fully-clean fix loop the way <code>webjs check --json</code> does.</p>

<h3>webjs version</h3>
<pre>webjs version # print the installed @webjsdev/cli version
webjs --version / -v # the same, flag form</pre>
<p>Prints the installed <code>@webjsdev/cli</code> version, so an agent can detect the toolchain version before relying on a command.</p>

<h3>webjs help</h3>
<pre>webjs help # the full command banner
webjs help routes # usage + summary + an Options table + Examples for one command
webjs --help / -h # the banner (flag form)
webjs routes --help # one command's help (flag form)</pre>
<p><code>webjs help &lt;command&gt;</code> prints that command's exact usage line, a one-line summary, an Options table documenting every flag, and worked examples, so you (or an agent) read the real invocation instead of guessing flags. The <code>--help</code> / <code>-h</code> flag forms are equivalent: bare at the top level for the banner, or after a command for that command's help. Commands that wrap an external tool (<code>typecheck</code> to <code>tsc</code>, <code>db</code> to drizzle-kit, <code>ui</code> to <code>@webjsdev/ui</code>) forward <code>--help</code> to that tool. An unknown help topic exits non-zero.</p>

<h2>tsconfig.json</h2>
<p>Optional but recommended for editor + CI type-checking:</p>
<pre>{
Expand Down
5 changes: 4 additions & 1 deletion packages/cli/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,13 +123,16 @@ README.md npm-facing package readme.
| `webjs start` | `startServer({ dev: false })`, plain HTTP/1.1 (front a reverse proxy for TLS + HTTP/2). Shares the dev framework-resolve preflight above (#954). |
| `webjs test [--server\|--browser]` | Runtime-native test runner (#570): server tests run under `node --test` on Node and `bun test` on Bun (`bun --test` is invalid), dispatched on `process.versions.bun`; browser tests run the app's resolved `@web/test-runner` (`wtr`) bin via `process.execPath` (no `npx`). |
| `webjs check [--rules] [--json]` | `checkConventions()` from `@webjsdev/server/check`. `--rules` lists the checks. `--json` emits the structured violations + a summary count as JSON (via `projectCheck` from `@webjsdev/mcp/check-report`, the same projector the MCP `check` tool uses, #415), so an agent in a loop consumes structured data instead of regex-scraping stdout; the non-zero exit on violations is preserved. Report-only: each violation carries a prose `fix` hint, but there is no `--fix` autofix flag (the rules either rewrite code or rename files, so an automatic codemod is not safe) |
| `webjs routes [--json\|--table] [--no-headers]` | Prints the route table to stdout (#975): every page (path, owner file, dynamic params) and every `route.{js,ts}` handler (path, owner file, HTTP methods). Reuses `buildRouteTable` from `@webjsdev/server` (the ONE walker, shared with `webjs types` + the dev server) and the shared `projectRoutes` projector from `@webjsdev/mcp/routes-report`, so `--json` is byte-identical to the MCP `list_routes` tool (the same split as `check --json` / `check-report.js`). Default is a grouped tree; `--table` is aligned KIND/PATH/METHODS/FILE columns and `--no-headers` drops the header row for piping. Read-only. Tests: `test/cli/routes.test.mjs` |
| `webjs mcp` | Delegates to `runMcpServer()` from the standalone `@webjsdev/mcp` package (#415; full surface in `packages/mcp/AGENTS.md`). A read-only MCP stdio server: INTROSPECTION (`list_routes` / `list_actions` / `list_components` / `check`), KNOWLEDGE (`init` primer, `docs`, `resources`, `prompts`), and a `source` tool. The scaffold's `.claude.json` registers the server directly as `{ "command": "npx", "args": ["@webjsdev/mcp"] }` (mountable in any MCP host, e.g. Cursor `.cursor/mcp.json`); `webjs mcp` stays as a back-compat alias. STDOUT is the JSON-RPC channel (diagnostics go to stderr) |
| `webjs doctor` | `runDoctorChecks()` from `lib/doctor.js`. A project-health checklist over existing signals (Node major, tsconfig `erasableSyntaxOnly`, `.env` drift vs `.env.example`, vendor-pin freshness, the `.gitignore` keeping `.webjs/vendor/` committable (`vendor-gitignore`, moved here from `webjs check` in #461 as a warn since it is a project-config concern, not source correctness), `@webjsdev/*` version coherence, a framework-resolve probe (#954: `checkFrameworkResolves` + the exported `frameworkResolves` helper WARN when `@webjsdev/core` cannot be resolved FROM the app dir via a directory-relative `createRequire` probe, naming the fresh-git-worktree-without-node_modules cause and the fix; silent PASS when it resolves, so a healthy app is untouched), importmap coherence, git pre-commit hook, and a page/layout elision advisory (#646: `checkElisionCarriers` runs `@webjsdev/server`'s `analyzeAppElision` and WARNS, naming the first client-effecting blocker, for each page/layout that ships whole instead of being elided as a carrier; advisory-only, skipped when elision is off or there is no `app/`)). PURE checks render with a `[pass]` / `[warn]` / `[fail]` marker; non-zero exit iff a HARD check fails (Node below the floor, or `erasableSyntaxOnly` missing in an existing tsconfig), so CI can gate. Warns (drift / staleness) never fail the exit. The only network touch (pin freshness, plus the importmap-coherence live resolve) is best-effort: a fetch failure is a warn, never a hard fail. The importmap-coherence check (#450) runs `@webjsdev/server`'s `checkImportmapCoherence` IDENTICALLY over the live importmap AND the vendored `.webjs/vendor/importmap.json`, warning when a pinned package needs a newer version of another pinned package than is pinned (the #446 skew class); it reads dependency metadata from the already-installed node_modules manifests (no network of its own) and degrades to "could not verify" when a manifest is unavailable. An onboarding/setup-verify tool, NOT a scaffold-CI hard gate. Tests: `test/cli/doctor.test.mjs` |
| `webjs doctor` | `runDoctorChecks()` from `lib/doctor.js`. A project-health checklist over existing signals (Node major, tsconfig `erasableSyntaxOnly`, `.env` drift vs `.env.example`, vendor-pin freshness, the `.gitignore` keeping `.webjs/vendor/` committable (`vendor-gitignore`, moved here from `webjs check` in #461 as a warn since it is a project-config concern, not source correctness), `@webjsdev/*` version coherence, a framework-resolve probe (#954: `checkFrameworkResolves` + the exported `frameworkResolves` helper WARN when `@webjsdev/core` cannot be resolved FROM the app dir via a directory-relative `createRequire` probe, naming the fresh-git-worktree-without-node_modules cause and the fix; silent PASS when it resolves, so a healthy app is untouched), importmap coherence, git pre-commit hook, and a page/layout elision advisory (#646: `checkElisionCarriers` runs `@webjsdev/server`'s `analyzeAppElision` and WARNS, naming the first client-effecting blocker, for each page/layout that ships whole instead of being elided as a carrier; advisory-only, skipped when elision is off or there is no `app/`)). PURE checks render with a `[pass]` / `[warn]` / `[fail]` marker; non-zero exit iff a HARD check fails (Node below the floor, or `erasableSyntaxOnly` missing in an existing tsconfig), so CI can gate. Warns (drift / staleness) never fail the exit. Each `DoctorResult` carries a stable SCREAMING_SNAKE `code` (#975, the `DOCTOR_CODES` map; e.g. `NODE_VERSION`, `TSCONFIG_ERASABLE`, `IMPORTMAP_COHERENCE`) so an agent branches on the failure KIND, not the message text. `--json` emits `{ results, summary }` where `results` is the raw `DoctorResult[]` (each carrying a `code`) and `summary` is `{ pass, warn, fail, strict, ok }` (the same array-under-a-key convention as `check --json`); `--strict` also fails the exit on warnings (not just hard failures), so doctor can gate a fully-clean fix loop. The only network touch (pin freshness, plus the importmap-coherence live resolve) is best-effort: a fetch failure is a warn, never a hard fail. The importmap-coherence check (#450) runs `@webjsdev/server`'s `checkImportmapCoherence` IDENTICALLY over the live importmap AND the vendored `.webjs/vendor/importmap.json`, warning when a pinned package needs a newer version of another pinned package than is pinned (the #446 skew class); it reads dependency metadata from the already-installed node_modules manifests (no network of its own) and degrades to "could not verify" when a manifest is unavailable. An onboarding/setup-verify tool, NOT a scaffold-CI hard gate. Tests: `test/cli/doctor.test.mjs` |
| `webjs types` | `generateRouteTypes()` from `@webjsdev/server`, writes `.webjs/routes.d.ts` (typed `Route` union + per-route params, #258). Also auto-emitted at `webjs dev` startup |
| `webjs typecheck [tsc args]` | Resolves the project's own `typescript/bin/tsc` (via `createRequire` from the app cwd) and spawns it with `--noEmit`, passing extra args through. Exits non-zero on a type error (a CI gate). A clear message + non-zero exit when typescript is not installed (#265). The framework runs the standard compiler, it does not embed one |
| `webjs create <name> [--template …] [--db …] [--runtime node\|bun]` | `scaffoldApp()` from `lib/create.js`. `--runtime bun` (or `bun create webjs`, auto-detected) emits a Bun-flavored app (#541): `dev`/`start` scripts force `bun --bun`, `bun.lock`, a pure `oven/bun:1` Dockerfile + bun-install CI, and bun-command agent docs. Orthogonal to `--template` (invariant 1 stays exactly 3 templates). |
| `webjs db <generate\|migrate\|push\|studio>` | Runs the app's resolved `drizzle-kit` bin via `process.execPath` (no codegen step; `generate` is schema-to-SQL). Resolves the bin from the app's node_modules + spawns it with the current runtime (no `npx`, #570), so it works on Node and Bun, including a Node-less `oven/bun` image. `webjs db seed` runs the app's `db/seed.server.ts` directly. |
| `webjs ui <init\|add\|list\|view\|diff\|info>` | Proxies to `@webjsdev/ui` (see "UI subcommand" below) |
| `webjs version` | Prints the installed `@webjsdev/cli` version (#975, `readCliVersion()` reads the package's own package.json). Also reachable as the top-level `webjs --version` / `-v` flag, handled at the top of `main()` before the Node preflight so it works on an old Node. Tests: `test/cli/help.test.mjs` |
| `webjs help [command]` | Bare: the full USAGE banner. `webjs help <command>` prints that command's usage line, a one-line summary, an **Options** table (each flag + a universal `-h, --help` row, matching the Remix CLI's per-command Options section), and an Examples block from the `HELP` map in `bin/webjs.js` (#975), so an agent reads the exact invocation instead of guessing flags. An unknown help topic prints an error + the banner and **exits 1** (`printCommandHelp` returns false). The `--help` / `-h` FLAG forms are equivalent and handled at the top of `main()` (before the Node preflight, so they work on an old Node): `webjs --help` / `-h` prints the banner; `webjs <command> --help` / `-h` prints that command's help and short-circuits the body. Commands that forward args to an external CLI (`HELP_FLAG_PASSTHROUGH` = `typecheck` to tsc, `db` to drizzle-kit, `ui` to `@webjsdev/ui`) are excluded so the wrapped tool's own `--help` reaches it; an unrecognised command is not intercepted either, so it hits the Unknown-command error (exit 1). Tests: `test/cli/help.test.mjs` |

## UI subcommand: proxies to `@webjsdev/ui`

Expand Down
Loading