diff --git a/.changeset/cli-auth-command-examples-resolve.md b/.changeset/cli-auth-command-examples-resolve.md new file mode 100644 index 0000000000..d350d92842 --- /dev/null +++ b/.changeset/cli-auth-command-examples-resolve.md @@ -0,0 +1,34 @@ +--- +"@objectstack/cli": patch +--- + +fix(cli): `register`/`whoami`/`logout` examples no longer spell `os auth ` in live `--help` output (#11221) + +`static override examples` is printed verbatim as part of oclif's `--help`. `register.ts`, +`whoami.ts` and `logout.ts` live at the **root** of `packages/cli/src/commands/`, so oclif's +pattern-strategy loader registers them as `register` / `whoami` / `logout` — but their +`examples` spelled an `os auth ` shape that has never resolved. A user copy-pasting +straight out of `--help` hit `Error: Command auth:whoami not found.` (exit 2), the same dead +command #10927 fixed in `packages/cli/README.md` and #10967 fixed for the `environments` +topic, this time on the root auth-family commands. + +Measured against the built CLI (`packages/cli/bin/run.js`) before the fix: `os auth whoami`, +`os auth register` and `os auth logout` each exited 2 with `Error: Command auth: not +found.`, while the bare `os whoami` / `os register` / `os logout` each exited 0 and printed +help — so the examples named the one spelling that could not work. All seven `examples` +entries now say the bare, registered spelling. + +The exported default class on each file is renamed to match its real, file-path-derived +command id (`AuthRegister` → `Register`, `AuthWhoami` → `Whoami`, `AuthLogout` → `Logout`). +oclif derives a command's id purely from its file path, never from the class name, so this +changes no runtime resolution — confirmed by rebuilding the CLI and re-running `--help` on +all three. The rename also brings them onto this package's measured convention: every other +root-level command class is exactly the PascalCase of its filename. `login.ts` keeps +`AuthLogin` — its `examples` were already correct (`$ os login`), so it is outside this +card's file surface; that lone remaining class-name holdout is reported, not swept. + +`environments.test.ts`'s `#10967` pin carried a deliberately self-retiring `EXCLUDED` entry +for each of these three files, asserting the defect was *still present* so the exemption +could not outlive its cause. This fix removed the last unresolved entry, that assertion went +red exactly as designed, and the three entries are retired — the map is now empty and all +three files are scanned by the main assertion like every other command source. diff --git a/.changeset/cli-readme-drop-os-studio.md b/.changeset/cli-readme-drop-os-studio.md new file mode 100644 index 0000000000..cb373beb36 --- /dev/null +++ b/.changeset/cli-readme-drop-os-studio.md @@ -0,0 +1,20 @@ +--- +"@objectstack/cli": patch +--- + +docs(cli): drop the `os studio` row from the README command table — the CLI ships no such command (#11180) + +`packages/cli/README.md`'s **Development** command table listed +`` | `os studio [config]` | Launch Studio UI with development server | ``. The CLI has no +`studio` command and has not had one: the oclif command set is pattern-derived from +`packages/cli/src/commands/**`, and loading the built CLI's own `Config` enumerates 60 +registered ids with **zero** matching `studio` (control: `dev`, `serve`, `login`, `logout`, +`register`, `whoami` are all present in the same enumeration, so the check is not vacuous). +Running it confirms the same from the outside — `os studio --help` exits 2 with +`Error: Command studio not found.` + +The row is deleted rather than rewritten. Studio is not reached by a CLI command at all — +it is served by the console at `/_console/studio` after `os dev` or `os serve`, both of +which the same table already lists — so a replacement row would reintroduce the category +error that made this one wrong: a Commands table is a list of commands, and a browser route +is not one. diff --git a/packages/cli/README.md b/packages/cli/README.md index 0e6cce5a8f..6177b13a65 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -42,7 +42,6 @@ os compile | `os init [name]` | Initialize a new ObjectStack project in the current directory | | `os dev [package]` | Start development mode with hot reload | | `os serve [config]` | Start the ObjectStack server with plugin auto-detection | -| `os studio [config]` | Launch Studio UI with development server | ### Build & Validate diff --git a/packages/cli/src/commands/environments/environments.test.ts b/packages/cli/src/commands/environments/environments.test.ts index cc566beb3c..6a0fcb6e1d 100644 --- a/packages/cli/src/commands/environments/environments.test.ts +++ b/packages/cli/src/commands/environments/environments.test.ts @@ -151,23 +151,27 @@ describe('os environments commands', () => { * back" (AGENTS.md, Route & surface ownership §3): a shape this pin cannot * parse is exactly the shape that could hide a stale command undetected. * - * ## The one exclusion, and why it must self-retire + * ## The one exclusion, and why it self-retired * * `register.ts` / `whoami.ts` / `logout.ts` are root-level commands whose - * `examples` say `os auth register` / `os auth whoami` / `os auth logout`, - * though no `auth` topic has ever existed for them (confirmed via + * `examples` USED TO say `os auth register` / `os auth whoami` / `os auth + * logout`, though no `auth` topic has ever existed for them (confirmed via * `--help`: `Error: Command auth:whoami not found.`) — the same defect * class as #10967, found by scanning the whole tree, but not #10967's to - * fix (outside its dispatched file surface). Filed as #11221. `EXCLUDED` - * carves exactly those three files out of the main assertion below, but a - * silent, permanent exemption is its own defect — a file excluded here - * stops being checked by this pin forever, even after #11221 lands and the - * excluded condition no longer holds. So a second `it.each` re-runs the - * SAME predicate over the excluded files and asserts it still finds an - * unresolved entry: when #11221's fix removes the last one, that assertion - * goes red on purpose, and the failure message says to delete the entry. - * The pattern (map-of-reason + filtered main assertion + a "still needs - * its exclusion" retiring assertion) matches + * fix (outside its dispatched file surface). Filed as #11221 and fixed + * there, so `EXCLUDED` is now empty and all three are scanned by the main + * assertion like every other command source. + * + * The mechanism stays, because it is what made that handoff safe: a silent, + * permanent exemption is its own defect — a file excluded here stops being + * checked by this pin forever, even after the excluded condition no longer + * holds. So a second `it.each` re-runs the SAME predicate over the excluded + * files and asserts it still finds an unresolved entry. That is not + * hypothetical here: when #11221's fix removed the last unresolved entry, + * this assertion went red on purpose for all three files, and its message + * ("remove it from EXCLUDED above") is what retired them. The pattern + * (map-of-reason + filtered main assertion + a "still needs its exclusion" + * retiring assertion) matches * `packages/create-objectstack/src/starter-comments-self-contained.test.ts`'s * `EXCLUDED`, which has retired this same way before (#11022). */ @@ -287,15 +291,15 @@ describe('#10967 pin: examples resolve to a real command id', () => { } /** - * Files with a KNOWN, currently-live instance of this defect class this - * card does not own the fix for (#11221). The retiring assertion below - * proves each entry is still load-bearing, not decorative. + * Nothing is excluded — every command source is scanned. `register.ts` / + * `whoami.ts` / `logout.ts` each carried a self-retiring entry here while + * their `os auth …` examples were #11221's to fix; that fix landed, the + * retiring assertion below went red exactly as designed, and the map goes + * back to empty rather than staying around as a silent exemption over three + * root-level commands. The assertion stays, so the next entry added here is + * held to the same self-retirement. */ - const EXCLUDED = new Map([ - ['register.ts', '#11221: examples say `os auth register`, no `auth` topic exists'], - ['whoami.ts', '#11221: examples say `os auth whoami`, no `auth` topic exists'], - ['logout.ts', '#11221: examples say `os auth logout`, no `auth` topic exists'], - ]); + const EXCLUDED = new Map(); const sourceFiles = commandSourceFiles(); const registeredIds = registeredCommandIds(); diff --git a/packages/cli/src/commands/logout.ts b/packages/cli/src/commands/logout.ts index 25888add92..74f19a9778 100644 --- a/packages/cli/src/commands/logout.ts +++ b/packages/cli/src/commands/logout.ts @@ -5,11 +5,11 @@ import { printHeader, printSuccess, printError, emitJson } from '../utils/format import { deleteAuthConfig, readAuthConfig } from '../utils/auth-config.js'; import { ObjectStackClient } from '@objectstack/client'; -export default class AuthLogout extends Command { +export default class Logout extends Command { static override description = 'Clear stored authentication credentials'; static override examples = [ - '$ os auth logout', + '$ os logout', ]; static override flags = { @@ -19,7 +19,7 @@ export default class AuthLogout extends Command { }; async run(): Promise { - const { flags } = await this.parse(AuthLogout); + const { flags } = await this.parse(Logout); try { if (!flags.json) { diff --git a/packages/cli/src/commands/register.ts b/packages/cli/src/commands/register.ts index a16a282d0a..5e84c66588 100644 --- a/packages/cli/src/commands/register.ts +++ b/packages/cli/src/commands/register.ts @@ -59,13 +59,13 @@ async function promptPassword(promptText: string): Promise { }); } -export default class AuthRegister extends Command { +export default class Register extends Command { static override description = 'Create a new account and store credentials'; static override examples = [ - '$ os auth register', - '$ os auth register --email user@example.com --name "Jane Doe" --password mypassword', - '$ os auth register --url https://api.example.com', + '$ os register', + '$ os register --email user@example.com --name "Jane Doe" --password mypassword', + '$ os register --url https://api.example.com', ]; static override flags = { @@ -93,7 +93,7 @@ export default class AuthRegister extends Command { }; async run(): Promise { - const { flags } = await this.parse(AuthRegister); + const { flags } = await this.parse(Register); try { if (!flags.json) { diff --git a/packages/cli/src/commands/whoami.ts b/packages/cli/src/commands/whoami.ts index d6d8a8ea6d..9cd05cc9c4 100644 --- a/packages/cli/src/commands/whoami.ts +++ b/packages/cli/src/commands/whoami.ts @@ -5,13 +5,13 @@ import { printHeader, printError, printKV, emitJson } from '../utils/format.js'; import { createApiClient, requireAuth } from '../utils/api-client.js'; import { formatOutput } from '../utils/output-formatter.js'; -export default class AuthWhoami extends Command { +export default class Whoami extends Command { static override description = 'Show current session information'; static override examples = [ - '$ os auth whoami', - '$ os auth whoami --format json', - '$ os auth whoami --url https://api.example.com --token ', + '$ os whoami', + '$ os whoami --format json', + '$ os whoami --url https://api.example.com --token ', ]; static override flags = { @@ -34,7 +34,7 @@ export default class AuthWhoami extends Command { }; async run(): Promise { - const { flags } = await this.parse(AuthWhoami); + const { flags } = await this.parse(Whoami); try { const { client, token } = await createApiClient({