Filed unassigned and bare by the os-dev seat that landed #15816 (branch claude/issue-15816-create-validates-project-name). ⛔ Not graded here — no domain:*, no type, no priority. Out of #15816's scope and deliberately not folded into its PR.
Measured
On origin/main b398ad258b9, driving the published entry with NO_COLOR=1, streams captured separately, exit code read before any pipe:
node packages/cli/bin/run.js create plugin foo.bar
exit 0 — writes ./plugin-foo.bar/
src/index.ts contains: export const foo.barPlugin: Plugin = {
Parsed with TypeScript's own parser (ts.createSourceFile, syntactic diagnostics only):
foo.bar -> 1 syntactic diagnostic export const foo.barPlugin: Plugin = {
my-app -> 0 syntactic diagnostics (same template, control)
Why this is NOT the defect #15816 fixed, and is not closed by it
#15816 was about names npm refuses. foo.bar is a perfectly legal npm package name, and @objectstack/plugin-foo.bar is publishable. It passes validateProjectName() — the charset rule is ^[a-z0-9][a-z0-9._-]*$, which admits . on purpose, because a dot is legal in an npm name.
So the validation #15816 adds accepts this input, correctly, and the emitted project still does not compile. Verified after that fix: os create plugin foo.bar still exits 0 and still writes the broken source.
The mechanism
packages/cli/src/commands/create.ts:
function toCamelCase(str: string): string {
return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
}
It only folds -x into X. Every other character legal in an npm name but illegal in a JS identifier passes through into an identifier position:
foo.bar becomes foo.barPlugin (a property access, not an identifier)
- a leading digit (
1foo, legal for npm) becomes 1fooPlugin
_ and ~ shapes are admitted by npm and reach the same position
The identifier is used twice in src/index.ts and twice more in the emitted README.md.
Why it was not fixed alongside #15816
The correct form is a design choice, not a mechanical one, and picking it inside another card's PR would be deciding it silently. At least three self-consistent answers exist:
- narrow what
os create accepts further than npm does (reject any name that is not a clean identifier stem);
- sanitise the identifier only — keep the npm name as the user typed it, derive a valid identifier from it, the way
sanitizeNamespace() already derives a namespace;
- stop deriving an identifier at all — emit a default export, or a fixed name.
Option 2 is the shape the repo already uses one line away, but it makes the exported symbol no longer match the package name, which is a user-visible naming decision.
⚠️ Whoever takes this should check it against #15530 (emitted scope, with the maintainer) and #15816's PR, so a third philosophy is not introduced into the same emitted manifest.
Re-check
node packages/cli/bin/run.js create plugin foo.bar
sed -n '1,8p' ./plugin-foo.bar/src/index.ts
grep -n "toCamelCase" packages/cli/src/commands/create.ts
Dedup
One targeted search_issues pass (repo-scoped REST is 403 for this seat, gh absent, so the REST list route was unavailable). Positive control: the query returned #15816, a known-indexed os create emission card, so it reached the index. Total hits 1, that one being #15816 itself. ⛔ Not exhaustive — a single semantic query, not a full-corpus sweep.
Filed unassigned and bare by the
os-devseat that landed #15816 (branchclaude/issue-15816-create-validates-project-name). ⛔ Not graded here — nodomain:*, no type, no priority. Out of #15816's scope and deliberately not folded into its PR.Measured
On
origin/mainb398ad258b9, driving the published entry withNO_COLOR=1, streams captured separately, exit code read before any pipe:Parsed with TypeScript's own parser (
ts.createSourceFile, syntactic diagnostics only):Why this is NOT the defect #15816 fixed, and is not closed by it
#15816 was about names npm refuses.
foo.baris a perfectly legal npm package name, and@objectstack/plugin-foo.baris publishable. It passesvalidateProjectName()— the charset rule is^[a-z0-9][a-z0-9._-]*$, which admits.on purpose, because a dot is legal in an npm name.So the validation #15816 adds accepts this input, correctly, and the emitted project still does not compile. Verified after that fix:
os create plugin foo.barstill exits 0 and still writes the broken source.The mechanism
packages/cli/src/commands/create.ts:It only folds
-xintoX. Every other character legal in an npm name but illegal in a JS identifier passes through into an identifier position:foo.barbecomesfoo.barPlugin(a property access, not an identifier)1foo, legal for npm) becomes1fooPlugin_and~shapes are admitted by npm and reach the same positionThe identifier is used twice in
src/index.tsand twice more in the emittedREADME.md.Why it was not fixed alongside #15816
The correct form is a design choice, not a mechanical one, and picking it inside another card's PR would be deciding it silently. At least three self-consistent answers exist:
os createaccepts further than npm does (reject any name that is not a clean identifier stem);sanitizeNamespace()already derives a namespace;Option 2 is the shape the repo already uses one line away, but it makes the exported symbol no longer match the package name, which is a user-visible naming decision.
Re-check
Dedup
One targeted
search_issuespass (repo-scoped REST is 403 for this seat,ghabsent, so the REST list route was unavailable). Positive control: the query returned #15816, a known-indexedos createemission card, so it reached the index. Total hits 1, that one being #15816 itself. ⛔ Not exhaustive — a single semantic query, not a full-corpus sweep.