Skip to content

Commit 53fd35e

Browse files
docs(spec): cli-extension TSDoc step 2 "Discover" says what loads an oclif plugin into os (#21443)
Fixes #21286 Clause-②: no ## What changes - `packages/spec/src/kernel/cli-extension.zod.ts`: the module TSDoc, "How It Works", step 2 "Discover" is rewritten. No schema, export, type or `.describe()` change. - `content/docs/references/kernel/cli-extension.mdx`: regenerated with the repo's generator (`pnpm --filter @objectstack/spec build`, then `gen:docs`), not edited by hand. It moves by exactly the same five lines. - `.changeset/21286-cli-extension-tsdoc-discover.md`: an `@objectstack/spec` `patch`. The package's `files[]` lists `src/**/*.zod.ts`, so this source text ships in the tarball. ## Sentences changed (before, then after) Only one sentence in the module TSDoc was false. It is step 2, "Discover". Before (the placeholder `PKG` stands for an angle-bracketed placeholder in the source): > 2. **Discover** — The main CLI (`@objectstack/cli`) lists the plugin in its `oclif.plugins` array, or users install it via `os plugins install PKG`. After: > 2. **Discover** — oclif loads a plugin that the CLI's own `package.json` lists in both `oclif.plugins` and `dependencies`. `@objectstack/cli` lists none and ships no plugin manager, so `os plugins` is not a command. To add a plugin's commands to `os`, build an `os` distribution: a package whose own `package.json` lists the plugin in both places. Both halves of the old sentence were false. `@objectstack/cli` keeps no `oclif.plugins` list, and `os plugins install` is not a command. The new text matches `packages/cli/README.md` ("How Plugin Extension Works", step 3) and the callout in `content/docs/plugins/index.mdx`, so all three surfaces say the same thing. ## Sentences measured and kept Each sentence was checked against `@objectstack/cli` and `@oclif/core` 5.1.2 on `origin/main` `3a6d92f78b`: - Intro: "This enables third-party packages … to register new CLI commands via oclif's built-in plugin system." True. Probe legs C and E (below) show a third-party plugin registering a command through oclif's plugin loader. - Step 1, "Declare": true. The plugin's own `oclif` section is what the loader reads. - Step 3, "Load": true. Once a plugin is loaded, its pattern-strategy commands register (`acme-hello` in legs B, C, E and F). - "Plugin Package Contract" and the two examples: true. They describe a valid oclif plugin. - "Migration from Commander.js": true. `git grep` finds no `commander` and no `contributes.commands` read in `packages/cli/src`, so `objectstack.config.ts` plugins do not decide CLI commands. ## Measurements **What `packages/cli/package.json` declares.** `jq '.oclif.plugins'` answers `null`. The `oclif` keys are `bin`, `dirname`, `commands`, `hooks` and `topicSeparator`, and no dependency field names an `@oclif/plugin-*` package. This confirms the PM's mechanism assumption 1. **Loader probe.** It calls `Config.load()` from `@oclif/core` 5.1.2, which is the call `bin/run.js`'s `run()` makes. A throwaway plugin `@acme/os-ext` carries one command, `acme-hello`. `packages/cli/dist` was not built, so the root's own command count is 0 in every leg. The probe reads the plugin list and whether `acme-hello` registered. | leg | root | data dir (`OS_DATA_DIR`) | plugins loaded | `acme-hello` | |---|---|---|---|---| | A | `packages/cli` | empty | `@objectstack/cli` only | no | | B | `packages/cli` | `package.json` lists the plugin as `type: user` | `@objectstack/cli`, `@acme/os-ext` (user) | yes | | C | a distribution: `oclif.plugins` plus `dependencies` | empty | dist root, `@acme/os-ext` (core) | yes | | D | a distribution: `oclif.plugins` plus `devDependencies` only | empty | dist root only | no | | E | a distribution listing `@objectstack/cli` and the plugin | empty | dist root, `@objectstack/cli`, `@acme/os-ext` (all core) | yes | | F | `packages/cli` | `package.json` lists the plugin as `type: link` | `@objectstack/cli`, `@acme/os-ext` (link) | yes | Legs C and D are the rule the new step states, with its control. Leg E shows that a distribution loads `@objectstack/cli` itself as a core plugin. That reading is at the plugin level only: `@objectstack/cli`'s own commands are NOT MEASURED there, because its `dist` was not built. **The reference page moves by exactly the changed sentences.** These are the generator's own checks: - With the base tree's page written back on disk (the old sentence present, the new one absent), `pnpm check:docs` exits 1 and names one file, `content/docs/references/kernel/cli-extension.mdx (out of date)`. - The page was then restored from `HEAD`, and `git hash-object` matched the `HEAD` blob `df95a843d9`. - `pnpm --filter @objectstack/spec check:generated` (15 artifacts) is green at that tree. **Other mentions.** `git grep` over `packages/spec/**` and `content/docs/**` for `os plugins`, `plugins install`, `plugin-plugins` and `oclif.plugins` found no other spec-surface hit. The remaining hits are `content/docs/plugins/index.mdx:402-410`, the callout, which is already correct. See the acceptance notes for `objectstack plugin install` (singular). ## Acceptance notes (not changed here) - **`os` also loads plugins from its data directory.** Legs B and F show this. `@oclif/core`'s user-plugin loader runs whether or not a plugin manager is installed. It reads `package.json` in the data directory (`$OS_DATA_DIR`, else `$XDG_DATA_HOME/objectstack`, else `~/.local/share/objectstack`) and loads `type: user` and `type: link` entries. Nothing `os` ships writes that file: it is the store `@oclif/plugin-plugins` maintains. The new step does not name it and does not claim the distribution is the only route. It also bears on the separate `OclifPluginConfig` enforce-or-remove question. One more consequence: `packages/cli/bin/run.js`'s comment that a linked-plugin path "is not reachable today" holds only for `os plugins link`. A hand-written `type: link` entry still loads (leg F). Carrier: none (`domain:cli`'s file). - **`content/docs/protocol/kernel/lifecycle.mdx` (lines 194, 287, 357-403)** documents `objectstack plugin install`, `plugin enable` and `plugin test`. `packages/cli/src/commands/plugin/` holds only `build`, `publish` and `sign`, and `packages/cli/README.md` says the group has no `install`. This was found by reading the files, not by running a built binary. It is a hand-written docs page outside this card's surface. Carrier: none. - **The code comment under the module TSDoc in `cli-extension.zod.ts`** (the retirement note) calls `OclifPluginConfigSchema` "that live surface". It is not part of the TSDoc block, and whether the surface is live is the maintainer's enforce-or-remove question, so it is left alone. ## Verification Every reading below was taken at `52ece7d949`, the PR's head. - `pnpm --filter @objectstack/spec build` exits 0 (the lock printed `VERDICT command-exit 0`). Then `gen:docs` exits 0 ("Generated 227 files"), and `check:generated` exits 0 ("All 15 generated artifacts are up to date"). - `pnpm --filter @objectstack/spec test` exits 0: 600 test files, 17681 passed, 1 todo. - `pnpm --filter @objectstack/spec typecheck` exits 0, with `check:test-typecheck: OK`. - The red leg of `check:docs` is described under Measurements above. - Gate derivation: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands`, with no paths, listed 104 commands for this change set. Each was run with its exit code captured before any pipe. - 103 exit 0. Five of them first refused with exit 3 (`PREREQUISITE NOT MET`): `check:doc-formula-expressions`, `check:doc-security-posture`, `check:skill-examples`, `check:docs-transcript-drift` and `check:lean-entry-closure`. Each exits 0 after its prerequisite was built with `turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --filter=@objectstack/client-react --filter=@objectstack/objectql` (exit 0). - `check:dual-build-cjs-loads` is NOT MEASURED. It needs every package's `dist` (a full `pnpm build`, 86 packages). This narrowing is declared: CI's `Lint & Repo Gates` runs it on a full build. - `--ran` reconciliation: "104 derived famil(ies) accounted for — 103 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)". - Left to CI and not run here: the artifact-roster, wide-population and path-scheduled CI families, plus the type-check lanes that the derivation lists outside its 104. - The loader probe is a throwaway script and is not committed. --- _Generated by [Claude Code](https://claude.ai/code/session_01UtnxvdiN376GF3sgXwAw4d)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent b793010 commit 53fd35e

3 files changed

Lines changed: 19 additions & 4 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
The `kernel/cli-extension` module documentation no longer tells plugin authors to run `os plugins install`. Step 2, "Discover", said the plugin is listed in `@objectstack/cli`'s `oclif.plugins` array, or that users install it with `os plugins install`. Neither is true: `@objectstack/cli` declares no `oclif.plugins` and ships no plugin manager, so `os plugins` is not a command. The step now says what loads a plugin: oclif loads a plugin that the CLI's own `package.json` lists in both `oclif.plugins` and `dependencies`. To add a plugin's commands to `os`, build an `os` distribution whose own `package.json` lists the plugin in both places. The generated reference page carries the same text.
6+
7+
Clause-②: no
8+
9+
Documentation only. No schema, export or type changes.

‎content/docs/references/kernel/cli-extension.mdx‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ built-in plugin system.
1717

1818
1. **Declare** — Plugin's `package.json` includes an `oclif` config section
1919
declaring its commands directory and any topics.
20-
2. **Discover** — The main CLI (`@objectstack/cli`) lists the plugin in its
21-
`oclif.plugins` array, or users install it via `os plugins install <pkg>`.
20+
2. **Discover** — oclif loads a plugin that the CLI's own `package.json`
21+
lists in both `oclif.plugins` and `dependencies`. `@objectstack/cli`
22+
lists none and ships no plugin manager, so `os plugins` is not a
23+
command. To add a plugin's commands to `os`, build an `os` distribution:
24+
a package whose own `package.json` lists the plugin in both places.
2225
3. **Load** — oclif automatically discovers and registers all Command classes
2326
exported from the plugin's commands directory.
2427

‎packages/spec/src/kernel/cli-extension.zod.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ import { z } from 'zod';
1414
*
1515
* 1. **Declare** — Plugin's `package.json` includes an `oclif` config section
1616
* declaring its commands directory and any topics.
17-
* 2. **Discover** — The main CLI (`@objectstack/cli`) lists the plugin in its
18-
* `oclif.plugins` array, or users install it via `os plugins install <pkg>`.
17+
* 2. **Discover** — oclif loads a plugin that the CLI's own `package.json`
18+
* lists in both `oclif.plugins` and `dependencies`. `@objectstack/cli`
19+
* lists none and ships no plugin manager, so `os plugins` is not a
20+
* command. To add a plugin's commands to `os`, build an `os` distribution:
21+
* a package whose own `package.json` lists the plugin in both places.
1922
* 3. **Load** — oclif automatically discovers and registers all Command classes
2023
* exported from the plugin's commands directory.
2124
*

0 commit comments

Comments
 (0)