You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
All line anchors in this issue were re-verified against HEAD 5ac991ce on 2026-08-06. If a file has moved under you, re-anchor by the quoted text, which is exact.
Problem
The root README.md still advertises @webjsdev/ui to projects outside WebJs, which #1235 retired, and it does so with a command that cannot work from the audience it names.
README.md L60 is the **WebJs UI is the matching AI-first component library.** bullet. Its final sentence reads, verbatim:
It is auto-installed with @webjsdev/cli, and projects outside WebJs can use npx webjsui add button card dialog.
That one clause carries two independent defects.
1. It contradicts the scoping decision.#1235 landed as b0bc2742, feat!: scope @webjsdev/ui to WebJs apps, drop project detection. It deleted detectProject() and defaultsForProject(), replaced the per-framework defaults with fixed constants, and recorded the scope as invariant 7 in packages/ui/AGENTS.md L403: "The kit targets WebJs apps, and only WebJs apps (#1129) ... Do NOT reintroduce a detectProject() in any form." The same commit rewrote packages/ui/README.md, whose install section now reads "Option A : through @webjsdev/cli (the normal path)" and "Option B : the standalone binary", where Option B is framed at L76 as "a WebJs app that skipped the global install", not as a path for another framework. The root README kept the pre-#1235 framing, so the project's most-read surface still makes an offer the package deliberately withdrew.
2. The command 404s from the audience the sentence names.npx webjsui ... resolves the PACKAGE name webjsui. No such package is published. Verified on 2026-08-06:
The binary exists only inside @webjsdev/ui, which declares "bin": { "webjsui": "bin/webjsui.js" } in packages/ui/package.json L6-L8. So npx webjsui resolves only when @webjsdev/ui is already a local dependency and npx finds node_modules/.bin/webjsui. That is precisely the case the sentence excludes, since it addresses a project that has installed nothing. packages/ui/README.md gets this right by putting npm install -D @webjsdev/ui and npm install @webjsdev/core (L81-L82) ahead of its two npx webjsui lines (L83-L84).
The same unrunnable-command defect is repeated in the CLI's own printed hints. Eight command strings across five files in packages/ui/src tell the user to run npx webjsui <cmd> or a bare webjsui <cmd>. Neither resolves for the most common caller. A user who reached the command through webjs ui add has @webjsdev/ui resolved from the CLI's own install, not linked into the project, and packages/cli/lib/create.js L457-L462 states outright that "@webjsdev/ui is intentionally NOT pinned" by the scaffold. The package already wrote down the correct form and the reason, in the pointerLine() JSDoc at packages/ui/src/registry/example.js L81-L86, which uses the explicit npx @webjsdev/ui view form. The printed hints never adopted it.
Found during the #1100 review cycle. The packages/ui/README.md half was fixed by #1235. The root README and the printed hints were missed.
Corrections to the previous statement of this issue
The earlier draft of this body is superseded on three points, each corrected here.
It settled on npx webjsdev ui add button card dialog as the README replacement. This body reverses that, for the reason given under Design.
It scoped the printed-hint fix to init.js and add.js only. The sweep below found the same defect in diff.js, info.js, and fetcher.js. All eight strings move together.
It cited packages/ui/src/registry/example.js as authority without noting that the JSDoc's stated MECHANISM is wrong. That comment says npx "would resolve to an unrelated webjsui package". There is no webjsui package at all, so npx fails to resolve rather than resolving to something unrelated. The conclusion is right and the reason is not, and this PR corrects it because it cites it.
Design / approach
Settled framing. The README's replacement sentence mirrors packages/ui/README.md's post-#1235 install section, both halves of it. Option A is that a WebJs app with @webjsdev/cli installed has nothing to add, because the kit is a hard dependency of the CLI (packages/cli/package.json L22 declares "@webjsdev/ui": "^0.3.1"). Option B is that a WebJs app which skipped the global install runs the two npm installs first, then npx webjsui. Nothing in the sentence names another framework.
What settled it: packages/ui/README.md is the source of truth that #1235 corrected, and the root README's job is to agree with it rather than to invent a third dialect. Putting the install commands ahead of the npx webjsui line is also what fixes defect 2 at its root, since the bin resolves once the package is a local dependency. This is standard practice for a package README pair, where the umbrella README summarises and the package README defines.
Rejected, with reasons:
npx webjsdev ui add button card dialog as the README fallback. It resolves (the webjsdev wrapper is published and depends on @webjsdev/cli), it is one command instead of three, and it is the form .agents/skills/webjs/references/ui-kit.md and the scaffold templates already teach. The honest tradeoff is that it is shorter. It loses because the README would then teach a form the package's own README does not mention, which is how the drift in this issue started. The agent skill and the scaffold speak to an agent inside a project, where brevity in a generated hint matters. The README speaks to a reader who will click through to packages/ui/README.md next, and those two pages disagreeing is the defect being fixed.
Keep npx webjsui with no install line in front of it. That leaves defect 2 unfixed.
Cut the clause entirely and link to packages/ui/README.md. It throws away a useful answer for the reader who does not have the global CLI, and the bullet already links to webjs.dev/ui.
Settled form for the printed CLI hints: the explicit npx @webjsdev/ui <cmd> form. What settled it: the package already documented this exact rule and its rationale in pointerLine(), and @webjsdev/ui is published, so the command resolves with zero prior install no matter how the user reached the CLI. Using webjs ui <cmd> instead was rejected because a user running the standalone binary may have no @webjsdev/cli at all. Using the bare webjsui name was rejected because that is the defect.
Guard question, settled: no guard on the prose, one unit test on the printed hints.
No test asserting the README wording. A grep-based guard over README.md pins marketing copy to a string literal, so any later rewording reds CI for a non-defect and trains the next author to edit the guard rather than think. Industry practice for one-off prose drift is a review catch, not a regression test, and the drift here was a genuine one-off (the package README half was already corrected in the same release cycle). The acceptance criteria below carry the greps as a manual check instead.
Yes, unit tests on the CLI hints. Those are not prose. They are strings a program prints, with a functional contract (the printed command must resolve for the caller who sees it), which is exactly the kind of thing a test should hold. They are also cheap, because the capture-and-assert harness already exists in the package. Independently, .claude/hooks/require-tests-with-src.sh requires a test alongside any packages/*/src change, so the tests are not optional here in any case.
Implementation plan
Each step names the file, the line anchor at HEAD 5ac991ce, the text as it exists today, and the exact replacement.
Step 1. Rewrite the final sentence of the README UI bullet
File: README.md, L60. Change only the final sentence. The rest of the bullet is correct and stays byte-identical.
Remove:
It is auto-installed with `@webjsdev/cli`, and projects outside WebJs can use `npx webjsui add button card dialog`.
Replace with:
It ships as a hard dependency of `@webjsdev/cli`, so a WebJs app that installed the CLI needs no separate install, and a WebJs app that skipped the global install runs `npm install -D @webjsdev/ui` and `npm install @webjsdev/core` first, then `npx webjsui add button card dialog`.
The resulting L60 in full, so the implementer can diff against it:
- **WebJs UI is the matching AI-first component library.** Its 32 primitives at [webjs.dev/ui](https://webjs.dev/ui) are written for AI agents, in two tiers: pure class-helper functions (`buttonClass`, `cardClass`, `inputClass`) for visual primitives, plus a small set of stateful custom elements (`<ui-dialog>`, `<ui-tabs>`, `<ui-popover>`) for the cases where state matters. Running `webjs ui add button card dialog` copies the source into your project, so you own it and can edit it. It ships as a hard dependency of `@webjsdev/cli`, so a WebJs app that installed the CLI needs no separate install, and a WebJs app that skipped the global install runs `npm install -D @webjsdev/ui` and `npm install @webjsdev/core` first, then `npx webjsui add button card dialog`.
`Unknown registry item "${name}". Run \`webjsui list\` to see the available components.`,
Replace with:
`Unknown registry item "${name}". Run \`npx @webjsdev/ui list\` to see the available components.`,
Step 7. Correct the stale mechanism in the pointerLine() JSDoc
File: packages/ui/src/registry/example.js, L81-L86. This PR cites this comment as the authority for steps 2 to 6, and its stated mechanism is wrong, so it is corrected in the same pass. Today:
/** * The one-line pointer left in place of a stripped example. Uses the EXPLICIT * `npx @webjsdev/ui view` form (not the bare `webjsui` bin name, which npx would * resolve to an unrelated `webjsui` package when it is not a direct dep, e.g. in * a scaffolded app where `@webjsdev/ui` is only transitive). Works in a WebJs * app and a standalone project alike. */
Replace with:
/** * The one-line pointer left in place of a stripped example. Uses the EXPLICIT * `npx @webjsdev/ui view` form, not the bare `webjsui` bin name. `webjsui` is a * bin declared INSIDE `@webjsdev/ui` and is not a published package name (the * registry 404s on it), so `npx webjsui` resolves only where the kit is already * a direct dep. It is not one in a scaffolded app, where `@webjsdev/ui` is * intentionally unpinned (packages/cli/lib/create.js). The explicit form works * in a WebJs app and a standalone project alike, and every user-facing hint * printed by this package's commands uses it for the same reason (#1264). */
The pointerLine() return value at L88 is already the explicit form and does not change.
Step 8. Fix the command-form mismatch on the website UI page
File: website/app/ui/page.ts, L124. The page's own Install block at L97-L98 teaches webjs ui init / webjs ui add, then ten lines later the prose switches dialect. Today:
<code>webjsui view <name></code> and by the read-only MCP <code>ui</code> tool, rather
Replace with:
<code>webjs ui view <name></code> and by the read-only MCP <code>ui</code> tool, rather
webjs ui view is correct here and npx @webjsdev/ui view is not, because this paragraph sits inside a page whose Install block has already established a WebJs app with the CLI. Keep the < / > entities.
Step 9. Add the two unit tests
Specified under Tests below.
Full sweep
Every path was found with git grep -n over the whole repository for webjsui, outside WebJs, non-WebJs, and @webjsdev/ui, plus a targeted pass over README.md, AGENTS.md, .agents/, website/, docs/, packages/ui/, packages/cli/templates/, and blog/.
Must move (9 files, 12 strings):
File and line
Today
Verdict
README.md L60
It is auto-installed with @webjsdev/cli, and projects outside WebJs can use npx webjsui add button card dialog.
Both defects. Step 1. It is the only surviving "outside WebJs" offer in the repository.
packages/ui/src/commands/init.js L165
npx webjsui add button card dialog
Unrunnable hint. Step 2.
packages/ui/src/commands/add.js L28
npx webjsui init
Unrunnable hint. Step 3.
packages/ui/src/commands/add.js L34
npx webjsui add button, npx webjsui list
Unrunnable hints. Step 3.
packages/ui/src/commands/diff.js L18
Run `webjsui init` first.
Unrunnable hint. Step 4.
packages/ui/src/commands/diff.js L55
webjsui add <name> -o
Unrunnable hint. Step 4.
packages/ui/src/commands/info.js L18
(run `webjsui init`)
Unrunnable hint. Step 5.
packages/ui/src/registry/fetcher.js L58
Run `webjsui list`
Unrunnable hint in a thrown error. Step 6.
packages/ui/src/registry/example.js L81-L86
JSDoc claiming npx resolves an "unrelated webjsui package"
Wrong mechanism in the rationale this PR cites. Step 7.
website/app/ui/page.ts L124
webjsui view <name>
Dialect mismatch with the same page's Install block. Step 8.
Correct as written, do NOT touch:
File and line
Why it stays
packages/ui/README.md L74-L90
The post-#1235 "Option B : the standalone binary" section. L81-L82 install both packages, so L83-L84's npx webjsui init / npx webjsui add button card dialog resolve. This is the source of truth step 1 aligns with.
packages/ui/README.md L40, L108, L113, L116-L126
Bin-name references and the command table inside the package's own README, where webjsui IS the binary. Not copy-and-run hints for a caller who may lack it.
The internal command table and module map. Bin-name references in agent docs.
packages/ui/AGENTS.md L403-L415
Invariant 7. Its "it will render in a non-WebJs host, but that is not a supported, tested, or advertised path" is the scoping STATEMENT, the opposite of an offer. It is what the README must agree with.
The npm registry blurb. Already WebJs-scoped ("The AI-first component library for WebJs ..."), corrected by #1235. Keywords are shadcn, ui, web-components, tailwind, components, webjs, with no cross-framework claim.
packages/ui/src/index.js L19
.name('webjsui'), the commander program name. It IS the bin name.
packages/ui/src/registry/local.js L5, L19, L87 and extract.js L5, L128 and fetcher.js L47
JSDoc naming the CLI leaf, not printed to a user.
packages/cli/bin/webjs.js L527-L542
Bin resolution for the webjs ui proxy. Real identifiers.
Bin-name references in generated CSS and the nested gallery's notes.
packages/ui/test/*.test.js tmpdir prefixes (webjsui-add-, webjsui-init-, and so on)
Directory-name prefixes, not commands.
package-lock.json L7088
The generated bin map. Never hand-edited.
docs/
No hit at all. It is the redirect-only host.
Tests
New, in packages/ui/test/init-command.test.js. Add a case at the end of the file that runs a full successful init while capturing console.log, then asserts the printed hint. logger.info is a thin console.log wrapper (packages/ui/src/utils/logger.js L4), so a single reassignment captures it. The file already carries the stubFetch() and tmp() helpers and the reassign-then-restore-in-finally pattern (see L176-L194).
test('init: the success hint prints a command that resolves without a prior install (#1264)',async()=>{stubFetch();constd=tmp();constorigLog=console.log;constout=[];console.log=(...args)=>out.push(args.join(' '));try{awaitinit.parseAsync(['--yes','--cwd',d,'--registry','http://test/r'],{from: 'user'});}finally{console.log=origLog;globalThis.fetch=origFetch;rmSync(d,{recursive: true});}consttext=out.join('\n');assert.match(text,/npx@webjsdev\/uiadd/);assert.doesNotMatch(text,/npxwebjsui/,'the bare bin name does not resolve for a `webjs ui` caller');});
New, in packages/ui/test/add-command.test.js. Add two cases covering the two early-return hint paths. Both call process.exit(1), so reuse the existing stub-and-throw pattern from L136-L156 rather than inventing one.
test('add: the missing-config hint prints a resolvable command (#1264)',async()=>{stubFetch();constd=mkdtempSync(join(tmpdir(),'webjsui-add-hint-'));constorigExit=process.exit;constorigError=console.error;constorigLog=console.log;constout=[];process.exit=((c)=>{thrownewError('exit:'+c);});console.error=(...args)=>out.push(args.join(' '));console.log=(...args)=>out.push(args.join(' '));try{awaitassert.rejects(()=>add.parseAsync(['button','--cwd',d,'--registry','http://test/r'],{from: 'user'}),/exit:1/,);}finally{process.exit=origExit;console.error=origError;console.log=origLog;globalThis.fetch=origFetch;rmSync(d,{recursive: true});}consttext=out.join('\n');assert.match(text,/npx@webjsdev\/uiinit/);assert.doesNotMatch(text,/npxwebjsui/);});test('add: the no-components hint prints resolvable commands (#1264)',async()=>{stubFetch();constd=tmp();constorigExit=process.exit;constorigError=console.error;constorigLog=console.log;constout=[];process.exit=((c)=>{thrownewError('exit:'+c);});console.error=(...args)=>out.push(args.join(' '));console.log=(...args)=>out.push(args.join(' '));try{awaitassert.rejects(()=>add.parseAsync(['--cwd',d,'--registry','http://test/r'],{from: 'user'}),/exit:1/,);}finally{process.exit=origExit;console.error=origError;console.log=origLog;globalThis.fetch=origFetch;rmSync(d,{recursive: true});}consttext=out.join('\n');assert.match(text,/npx@webjsdev\/uiaddbutton/);assert.match(text,/npx@webjsdev\/uilist/);assert.doesNotMatch(text,/npxwebjsui/);});
No ANSI stripping is needed. logger.cyan wraps the whole command string, so kleur's escape codes sit outside npx @webjsdev/ui add rather than inside it, and the assertions below match either way.
diff.js, info.js, and fetcher.js are covered by the existing suites (diff-command.test.js, list-view-info.test.js, fetcher.test.js). Extend an existing case in each with the same stripped-text assertion rather than adding a third harness, if the case already captures output. Where it does not, the two cases above plus the manual greps in the acceptance criteria are sufficient, because those three strings are literals with no branching around them.
Counterfactual, required before the PR is ready. Restore npx webjsui add button card dialog at init.js L165 alone and confirm the new init-command.test.js case reds. Restore add.js L28 alone and confirm the missing-config case reds. Record both in the PR body.
Run:npm test --workspace=@webjsdev/ui.
Layers that are N/A, with reasons:
Browser: nothing renders. The only source edits are strings printed to a terminal.
E2E: no HTTP surface changes.
Smoke: no scaffold output changes. webjs create does not emit any of the edited strings, and the scaffold's own npx webjsdev ui ... hints in create.js L1601-L1611 are unchanged.
Bun parity: nothing runtime-sensitive. No serializer, listener, dispatch, stream, crypto, TS-stripper, auth, or CORS path is touched. If .claude/hooks/require-bun-parity-with-runtime-src.sh fires on the packages/ui/src staging, the change still carries no cross-runtime behaviour to prove, so record that in the PR body rather than writing a hollow test/bun/* file.
Docs
This is primarily a doc change, and the doc gate (.claude/hooks/require-docs-with-src.sh) is satisfied by steps 1 and 8 riding in the same commit as steps 2 to 7. Do not reach for WEBJS_NO_DOC_GATE=1.
Surfaces that must be in sync when this lands:
README.md L60. Edited in step 1. The npm-visible root README and the highest-traffic surface.
packages/ui/README.md L61-L92. Unchanged, and it is the surface step 1 is being aligned TO. Re-read it before writing step 1 and confirm the two agree.
packages/ui/AGENTS.md invariant 7 (L403-L415). Unchanged. It is the recorded scope decision the README must not contradict.
website/app/ui/page.ts L124. Edited in step 8. The public UI kit page.
packages/ui/src/registry/example.js L81-L86. Edited in step 7. It is the in-code rule the other seven strings now follow.
.agents/skills/webjs/references/ui-kit.md. Verified already correct (npx webjsdev ui <cmd>). No edit. The two forms coexist deliberately: the skill addresses an agent inside a project, the ui package's own hints address whoever ran the binary.
packages/cli/templates/**. Verified already correct. No scaffold output changes, so no webjs create regeneration is needed.
packages/cli/README.md. Verified already correct. No edit.
AGENTS.md (root) L516. Lists webjs ui init | add | list | view in the CLI reference. Correct, no edit.
Changelog. Do not hand-write one. The PR title feeds backfill-changelog.js, so title the PR docs: stop offering @webjsdev/ui to non-WebJs projects via a 404 command (or another docs: subject under 72 characters). Put Closes #1264 in the PR body.
No version bump. @webjsdev/ui is published and this touches its src, so it accrues release debt, but the release PR is separate and is opened only on the owner's go-ahead.
Acceptance criteria
README.md L60 no longer offers @webjsdev/ui to projects outside WebJs, and names WebJs as the host in both halves of the replacement sentence
The replacement sentence puts npm install -D @webjsdev/ui and npm install @webjsdev/core ahead of npx webjsui add button card dialog, matching packages/ui/README.md L81-L84
packages/ui/src/registry/example.js no longer claims npx resolves an "unrelated webjsui package", and pointerLine()'s return value is unchanged
npm test --workspace=@webjsdev/ui passes, including the three new cases
Each new case reds when its own source line alone is reverted, and both counterfactuals are recorded in the PR body
webjs check is clean, and webjs doctor is clean on website (a website/ file is edited, and the required conventions CI job runs doctor over it, gating on that app's webjs.doctor.gateerror entries)
No AGENTS.md invariant 11 violation in any new prose. No em-dash, no space-surrounded hyphen or semicolon as a pause, no colon attached to a code-shaped left-hand side, WebJs capitalized wherever it names the project, lowercase webjs only as a code token
The PR body records the decision to widen into packages/ui/src, with the reason that the scaffold does not pin @webjsdev/ui (packages/cli/lib/create.js L457-L462), so a webjs ui-reached caller cannot run a bare webjsui
Out of scope
The implementer must not widen into any of these.
Publishing a webjsui package to claim the name. The name resolving to nothing is not what this issue fixes, and squatting an npm name is a separate decision with its own security surface.
Renaming the webjsui bin, or adding a second bin to @webjsdev/ui. The bin name is correct. Only the places that TELL a user to type it are wrong.
Rewriting packages/ui/README.md. It is the target of the alignment, not a subject of it.
Changing the npx webjsdev ui <cmd> form used by the agent skill and the scaffold templates. Those resolve and are addressed to a different reader. Two forms coexisting is the settled outcome, not drift.
A repo-wide command-form linter. Rejected under the guard question above.
A version bump or a release PR for @webjsdev/ui. Release debt is reported to the owner, never opened unprompted.
Editing changelog/ui/0.3.9.md or any other changelog entry.
Filing follow-up issues. Anything this PR turns up goes in the PR body or a message to the owner. A small tweak in a file this PR already touches is folded in.
Problem
The root
README.mdstill advertises@webjsdev/uito projects outside WebJs, which #1235 retired, and it does so with a command that cannot work from the audience it names.README.mdL60 is the**WebJs UI is the matching AI-first component library.**bullet. Its final sentence reads, verbatim:That one clause carries two independent defects.
1. It contradicts the scoping decision. #1235 landed as
b0bc2742,feat!: scope @webjsdev/ui to WebJs apps, drop project detection. It deleteddetectProject()anddefaultsForProject(), replaced the per-framework defaults with fixed constants, and recorded the scope as invariant 7 inpackages/ui/AGENTS.mdL403: "The kit targets WebJs apps, and only WebJs apps (#1129) ... Do NOT reintroduce adetectProject()in any form." The same commit rewrotepackages/ui/README.md, whose install section now reads "Option A : through@webjsdev/cli(the normal path)" and "Option B : the standalone binary", where Option B is framed at L76 as "a WebJs app that skipped the global install", not as a path for another framework. The root README kept the pre-#1235 framing, so the project's most-read surface still makes an offer the package deliberately withdrew.2. The command 404s from the audience the sentence names.
npx webjsui ...resolves the PACKAGE namewebjsui. No such package is published. Verified on 2026-08-06:The binary exists only inside
@webjsdev/ui, which declares"bin": { "webjsui": "bin/webjsui.js" }inpackages/ui/package.jsonL6-L8. Sonpx webjsuiresolves only when@webjsdev/uiis already a local dependency and npx findsnode_modules/.bin/webjsui. That is precisely the case the sentence excludes, since it addresses a project that has installed nothing.packages/ui/README.mdgets this right by puttingnpm install -D @webjsdev/uiandnpm install @webjsdev/core(L81-L82) ahead of its twonpx webjsuilines (L83-L84).The same unrunnable-command defect is repeated in the CLI's own printed hints. Eight command strings across five files in
packages/ui/srctell the user to runnpx webjsui <cmd>or a barewebjsui <cmd>. Neither resolves for the most common caller. A user who reached the command throughwebjs ui addhas@webjsdev/uiresolved from the CLI's own install, not linked into the project, andpackages/cli/lib/create.jsL457-L462 states outright that "@webjsdev/ui is intentionally NOT pinned" by the scaffold. The package already wrote down the correct form and the reason, in thepointerLine()JSDoc atpackages/ui/src/registry/example.jsL81-L86, which uses the explicitnpx @webjsdev/ui viewform. The printed hints never adopted it.Found during the #1100 review cycle. The
packages/ui/README.mdhalf was fixed by #1235. The root README and the printed hints were missed.Corrections to the previous statement of this issue
The earlier draft of this body is superseded on three points, each corrected here.
npx webjsdev ui add button card dialogas the README replacement. This body reverses that, for the reason given under Design.init.jsandadd.jsonly. The sweep below found the same defect indiff.js,info.js, andfetcher.js. All eight strings move together.packages/ui/src/registry/example.jsas authority without noting that the JSDoc's stated MECHANISM is wrong. That comment says npx "would resolve to an unrelatedwebjsuipackage". There is nowebjsuipackage at all, so npx fails to resolve rather than resolving to something unrelated. The conclusion is right and the reason is not, and this PR corrects it because it cites it.Design / approach
Settled framing. The README's replacement sentence mirrors
packages/ui/README.md's post-#1235 install section, both halves of it. Option A is that a WebJs app with@webjsdev/cliinstalled has nothing to add, because the kit is a hard dependency of the CLI (packages/cli/package.jsonL22 declares"@webjsdev/ui": "^0.3.1"). Option B is that a WebJs app which skipped the global install runs the two npm installs first, thennpx webjsui. Nothing in the sentence names another framework.What settled it:
packages/ui/README.mdis the source of truth that #1235 corrected, and the root README's job is to agree with it rather than to invent a third dialect. Putting the install commands ahead of thenpx webjsuiline is also what fixes defect 2 at its root, since the bin resolves once the package is a local dependency. This is standard practice for a package README pair, where the umbrella README summarises and the package README defines.Rejected, with reasons:
npx webjsdev ui add button card dialogas the README fallback. It resolves (thewebjsdevwrapper is published and depends on@webjsdev/cli), it is one command instead of three, and it is the form.agents/skills/webjs/references/ui-kit.mdand the scaffold templates already teach. The honest tradeoff is that it is shorter. It loses because the README would then teach a form the package's own README does not mention, which is how the drift in this issue started. The agent skill and the scaffold speak to an agent inside a project, where brevity in a generated hint matters. The README speaks to a reader who will click through topackages/ui/README.mdnext, and those two pages disagreeing is the defect being fixed.npx webjsuiwith no install line in front of it. That leaves defect 2 unfixed.packages/ui/README.md. It throws away a useful answer for the reader who does not have the global CLI, and the bullet already links to webjs.dev/ui.packages/ui/README.mdL83-L84 instead. Those two lines sit directly under the two npm installs, so the bin is linked by the time they run. feat!: scope @webjsdev/ui to WebJs apps, drop project detection #1235 wrote them deliberately and they are correct.Settled form for the printed CLI hints: the explicit
npx @webjsdev/ui <cmd>form. What settled it: the package already documented this exact rule and its rationale inpointerLine(), and@webjsdev/uiis published, so the command resolves with zero prior install no matter how the user reached the CLI. Usingwebjs ui <cmd>instead was rejected because a user running the standalone binary may have no@webjsdev/cliat all. Using the barewebjsuiname was rejected because that is the defect.Guard question, settled: no guard on the prose, one unit test on the printed hints.
README.mdpins marketing copy to a string literal, so any later rewording reds CI for a non-defect and trains the next author to edit the guard rather than think. Industry practice for one-off prose drift is a review catch, not a regression test, and the drift here was a genuine one-off (the package README half was already corrected in the same release cycle). The acceptance criteria below carry the greps as a manual check instead..claude/hooks/require-tests-with-src.shrequires a test alongside anypackages/*/srcchange, so the tests are not optional here in any case.Implementation plan
Each step names the file, the line anchor at HEAD
5ac991ce, the text as it exists today, and the exact replacement.Step 1. Rewrite the final sentence of the README UI bullet
File:
README.md, L60. Change only the final sentence. The rest of the bullet is correct and stays byte-identical.Remove:
Replace with:
The resulting L60 in full, so the implementer can diff against it:
Step 2. Fix the printed hint in
initFile:
packages/ui/src/commands/init.js, L165. Today:Replace with:
Step 3. Fix the two printed hints in
addFile:
packages/ui/src/commands/add.js, L28 and L34. Today:Replace with:
Step 4. Fix the two printed hints in
diffFile:
packages/ui/src/commands/diff.js, L18 and L55. Today:Replace with:
Step 5. Fix the printed hint in
infoFile:
packages/ui/src/commands/info.js, L18. Today:Replace with:
Keep the
✔/✖glyphs exactly as they are.Step 6. Fix the unknown-item error message in the fetcher
File:
packages/ui/src/registry/fetcher.js, L58. Today:Replace with:
Step 7. Correct the stale mechanism in the
pointerLine()JSDocFile:
packages/ui/src/registry/example.js, L81-L86. This PR cites this comment as the authority for steps 2 to 6, and its stated mechanism is wrong, so it is corrected in the same pass. Today:Replace with:
The
pointerLine()return value at L88 is already the explicit form and does not change.Step 8. Fix the command-form mismatch on the website UI page
File:
website/app/ui/page.ts, L124. The page's own Install block at L97-L98 teacheswebjs ui init/webjs ui add, then ten lines later the prose switches dialect. Today:Replace with:
webjs ui viewis correct here andnpx @webjsdev/ui viewis not, because this paragraph sits inside a page whose Install block has already established a WebJs app with the CLI. Keep the</>entities.Step 9. Add the two unit tests
Specified under Tests below.
Full sweep
Every path was found with
git grep -nover the whole repository forwebjsui,outside WebJs,non-WebJs, and@webjsdev/ui, plus a targeted pass overREADME.md,AGENTS.md,.agents/,website/,docs/,packages/ui/,packages/cli/templates/, andblog/.Must move (9 files, 12 strings):
README.mdL60It is auto-installed with @webjsdev/cli, and projects outside WebJs can use npx webjsui add button card dialog.packages/ui/src/commands/init.jsL165npx webjsui add button card dialogpackages/ui/src/commands/add.jsL28npx webjsui initpackages/ui/src/commands/add.jsL34npx webjsui add button,npx webjsui listpackages/ui/src/commands/diff.jsL18Run `webjsui init` first.packages/ui/src/commands/diff.jsL55webjsui add <name> -opackages/ui/src/commands/info.jsL18(run `webjsui init`)packages/ui/src/registry/fetcher.jsL58Run `webjsui list`packages/ui/src/registry/example.jsL81-L86webjsuipackage"website/app/ui/page.tsL124webjsui view <name>Correct as written, do NOT touch:
packages/ui/README.mdL74-L90npx webjsui init/npx webjsui add button card dialogresolve. This is the source of truth step 1 aligns with.packages/ui/README.mdL40, L108, L113, L116-L126webjsuiIS the binary. Not copy-and-run hints for a caller who may lack it.packages/ui/AGENTS.mdL3, L84, L282-L292, L330, L340, L452, L552packages/ui/AGENTS.mdL403-L415packages/ui/package.jsonL5description, L28-L35keywordsshadcn,ui,web-components,tailwind,components,webjs, with no cross-framework claim.packages/ui/src/index.jsL19.name('webjsui'), the commander program name. It IS the bin name.packages/ui/src/registry/local.jsL5, L19, L87 andextract.jsL5, L128 andfetcher.jsL47packages/cli/bin/webjs.jsL527-L542webjs uiproxy. Real identifiers.packages/cli/test/resolve-bin/resolve-bin.test.mjsL34-L50packages/cli/lib/create.jsL170, L1135, L1601-L1611npx webjsdev ui ...rather thannpx webjs ui .... Correct and unrelated.packages/cli/README.mdL54-L65webjs ui init/webjs ui addand says the kit is a hard dependency of the CLI. No cross-framework claim, no barewebjsuiinvocation.packages/mcp/README.mdL41,packages/mcp/AGENTS.mdL51,packages/mcp/src/mcp.jsL383,packages/mcp/test/mcp.test.mjsL354webjsui viewas the CLI leaf the MCPuitool mirrors. Internal reference, not an invocation..agents/skills/webjs/references/ui-kit.mdL5-L7, L35, L39, L43, L53, L56, L72npx webjsdev ui <cmd>, which resolves. Agent-facing, inside a project..agents/skills/webjs/references/styling.mdL76, L94npx webjsdev ui add..agents/skills/webjs/SKILL.mdL47packages/cli/templates/partials/agents-playbook-fullstack.mdL16, L49, L53, L126-L127npx webjsdev ui <cmd>.packages/cli/templates/scripts/clear-gallery.mjsL23, L32, L64, L143, L147, L187npx webjsdev ui add <name>orwebjs ui add.packages/cli/templates/gallery/components/ui/*.tswebjs ui add <name>inside a scaffolded WebJs app.website/app/ui/page.tsL89-L98website/app/docs/ai-first/page.tsL52,website/app/docs/configuration/page.tsL74,website/app/docs/layout.tsL90website/AGENTS.mdL45, L60, L126,website/.gitignoreL2,website/public/input.cssL4,website/modules/ui/utils/tier.tsL2,website/app/ui/registry/route.tsL8blog/why-webjs.mdL42, L104 andblog/accessible-web-components-by-default.mdL19-L23, L91webjs ui add button card dialoginside an explicitly WebJs context. Correct, and blog posts are dated artefacts.blog/light-dom-by-default.mdL92,blog/light-dom-slots-with-full-parity.mdL39, L135,blog/signals-replaced-setstate.mdL102changelog/ui/0.3.9.mdL11packages/ui/packages/registry/themes/index.cssL2,packages/ui/packages/website/AGENTS.mdL26,packages/ui/packages/website/middleware.tsL22packages/ui/test/*.test.jstmpdir prefixes (webjsui-add-,webjsui-init-, and so on)package-lock.jsonL7088docs/Tests
New, in
packages/ui/test/init-command.test.js. Add a case at the end of the file that runs a full successfulinitwhile capturingconsole.log, then asserts the printed hint.logger.infois a thinconsole.logwrapper (packages/ui/src/utils/logger.jsL4), so a single reassignment captures it. The file already carries thestubFetch()andtmp()helpers and the reassign-then-restore-in-finallypattern (see L176-L194).New, in
packages/ui/test/add-command.test.js. Add two cases covering the two early-return hint paths. Both callprocess.exit(1), so reuse the existing stub-and-throw pattern from L136-L156 rather than inventing one.No ANSI stripping is needed.
logger.cyanwraps the whole command string, so kleur's escape codes sit outsidenpx @webjsdev/ui addrather than inside it, and the assertions below match either way.diff.js,info.js, andfetcher.jsare covered by the existing suites (diff-command.test.js,list-view-info.test.js,fetcher.test.js). Extend an existing case in each with the same stripped-text assertion rather than adding a third harness, if the case already captures output. Where it does not, the two cases above plus the manual greps in the acceptance criteria are sufficient, because those three strings are literals with no branching around them.Counterfactual, required before the PR is ready. Restore
npx webjsui add button card dialogatinit.jsL165 alone and confirm the newinit-command.test.jscase reds. Restoreadd.jsL28 alone and confirm the missing-config case reds. Record both in the PR body.Run:
npm test --workspace=@webjsdev/ui.Layers that are N/A, with reasons:
webjs createdoes not emit any of the edited strings, and the scaffold's ownnpx webjsdev ui ...hints increate.jsL1601-L1611 are unchanged..claude/hooks/require-bun-parity-with-runtime-src.shfires on thepackages/ui/srcstaging, the change still carries no cross-runtime behaviour to prove, so record that in the PR body rather than writing a hollowtest/bun/*file.Docs
This is primarily a doc change, and the doc gate (
.claude/hooks/require-docs-with-src.sh) is satisfied by steps 1 and 8 riding in the same commit as steps 2 to 7. Do not reach forWEBJS_NO_DOC_GATE=1.Surfaces that must be in sync when this lands:
README.mdL60. Edited in step 1. The npm-visible root README and the highest-traffic surface.packages/ui/README.mdL61-L92. Unchanged, and it is the surface step 1 is being aligned TO. Re-read it before writing step 1 and confirm the two agree.packages/ui/AGENTS.mdinvariant 7 (L403-L415). Unchanged. It is the recorded scope decision the README must not contradict.website/app/ui/page.tsL124. Edited in step 8. The public UI kit page.packages/ui/src/registry/example.jsL81-L86. Edited in step 7. It is the in-code rule the other seven strings now follow..agents/skills/webjs/references/ui-kit.md. Verified already correct (npx webjsdev ui <cmd>). No edit. The two forms coexist deliberately: the skill addresses an agent inside a project, the ui package's own hints address whoever ran the binary.packages/cli/templates/**. Verified already correct. No scaffold output changes, so nowebjs createregeneration is needed.packages/cli/README.md. Verified already correct. No edit.AGENTS.md(root) L516. Listswebjs ui init | add | list | viewin the CLI reference. Correct, no edit.backfill-changelog.js, so title the PRdocs: stop offering @webjsdev/ui to non-WebJs projects via a 404 command(or anotherdocs:subject under 72 characters). PutCloses #1264in the PR body.@webjsdev/uiis published and this touches itssrc, so it accrues release debt, but the release PR is separate and is opened only on the owner's go-ahead.Acceptance criteria
README.mdL60 no longer offers@webjsdev/uito projects outside WebJs, and names WebJs as the host in both halves of the replacement sentencenpm install -D @webjsdev/uiandnpm install @webjsdev/coreahead ofnpx webjsui add button card dialog, matchingpackages/ui/README.mdL81-L84git grep -n 'outside WebJs' -- '*.md' 'website/**'returns nothinggit grep -n 'npx webjsui' -- packages/ui/src/ website/ README.mdreturns nothinggit grep -n 'webjsui' -- packages/ui/src/commands/ packages/ui/src/registry/fetcher.jsreturns nothing in a user-printed stringpackages/ui/README.mdL74-L92 is byte-identical to HEAD5ac991cewebsite/app/ui/page.tsL124 readswebjs ui view <name>packages/ui/src/registry/example.jsno longer claims npx resolves an "unrelatedwebjsuipackage", andpointerLine()'s return value is unchangednpm test --workspace=@webjsdev/uipasses, including the three new caseswebjs checkis clean, andwebjs doctoris clean onwebsite(awebsite/file is edited, and the requiredconventionsCI job runs doctor over it, gating on that app'swebjs.doctor.gateerrorentries)WebJscapitalized wherever it names the project, lowercasewebjsonly as a code tokenpackages/ui/src, with the reason that the scaffold does not pin@webjsdev/ui(packages/cli/lib/create.jsL457-L462), so awebjs ui-reached caller cannot run a barewebjsuiOut of scope
The implementer must not widen into any of these.
webjsuipackage to claim the name. The name resolving to nothing is not what this issue fixes, and squatting an npm name is a separate decision with its own security surface.webjsuibin, or adding a second bin to@webjsdev/ui. The bin name is correct. Only the places that TELL a user to type it are wrong.detectProject(), a per-framework default, or hedged "it might work elsewhere" copy anywhere.packages/ui/README.md. It is the target of the alignment, not a subject of it.npx webjsdev ui <cmd>form used by the agent skill and the scaffold templates. Those resolve and are addressed to a different reader. Two forms coexisting is the settled outcome, not drift.@webjsdev/ui. Release debt is reported to the owner, never opened unprompted.changelog/ui/0.3.9.mdor any other changelog entry.