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
62 changes: 62 additions & 0 deletions .changeset/os-create-example-retired.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
"@objectstack/cli": minor
---

feat(cli)!: retire `os create example` — it was a weaker `os init` plus a README, and the refusal now names `os init` (#16483, #15531)

**BREAKING** — a published CLI surface is removed. `os create example <name>` is a
command a user can run today and cannot run after this release. Graded `minor`
rather than `major` under this repo's lockstep launch-window convention (no
package here has ever shipped a `major` changeset); the break is stated here
instead of in the number.

<!-- adr-0087: not-required (no-migration-prescription) the retired surface is a CLI subcommand, not authored metadata: no Zod schema, no `packages/spec` declaration, no authorable key and no stored `sys_metadata` shape changes, so `objectstack migrate meta` has nothing to rewrite and the ledger has no step to carry. The channel that reaches every affected caller is the command itself, which now exits non-zero naming `os init`. -->

**No alias and no deprecation window.** `os create example` will not come back,
so change the command rather than pinning an older CLI. (Those terms are recorded
on card #16483 and are pending maintainer confirmation — the removal itself is
settled by the #15531 batch entry below. The behaviour ships either way, and it is
the same shape `os g agent` already shipped.)

#15531 rendered the real emission of both scaffolder families and hashed it file
by file. The only template-level duplication left between them was this one
template: `os create example` wrote a **subset** of what `os init` writes, plus
one README. The two families' emission policy is already unified through four
shared exports, so the remaining duplicate was the template itself — and the
ruling (decision batch #66, option B) is that it goes, not that the two command
families merge. They emit two different artifacts: a kernel code `Plugin` is not
a declarative app, and collapsing them would make that collision structural.

**What to run instead**

```bash
os init <name> # a full application project
os init <name> -t empty # config only, no src/objects
```

`os init` writes the same `tsconfig.json` the retired template did (byte-identical,
measured) and an **equivalent** `objectstack.config.ts` — both manifests are
`ManifestSchema`-valid but they are not the same bytes: the retired template wrote
`name: '<name>'`, `description: '<name> example application'` and commented-out
barrels, where `os init` writes a title-cased `name`, an empty `description` and no
barrels. On top of that `os init` adds `src/objects`, a `.gitignore` and the
dependency install the retired template never had.

**`os create plugin` is unaffected.** It scaffolds the kernel code `Plugin`
contract — `src/index.ts` exporting a `Plugin` with `init` / `destroy`, built by
`tsc`, publishable as `@objectstack/plugin-<name>` — which `os init` does not
emit. `os create`'s flags and its standalone emission policy are unchanged for
`plugin`. ⚠️ `--in-repo` is narrowed rather than untouched: the flag survives, but
its `examples/<name>` placement is removed with the template and gets no
replacement — `--in-repo` now only ever lands in `packages/plugins/plugin-<name>`.

**The removal is a signpost, not a deletion.** `os create example` still answers:
it exits **1** and names `os init`, rather than falling through to the generic
`Unknown type:` roster. A reader arriving from an older tutorial or a CI script
that still calls it learns what replaced it instead of learning only that their
spelling is off the list. Pinned end-to-end by driving the real CLI in
`packages/cli/test/create-example-retired.e2e.test.ts`, which asserts both halves
— the non-zero exit **and** the message naming `os init`. The four public doc
pages are held to the same promise by a SEPARATE pin,
`packages/cli/test/create-example-retired-docs-parity.test.ts`: it spawns nothing,
so unlike the `.e2e` file it runs in the per-PR tier rather than the nightly one.
25 changes: 18 additions & 7 deletions content/docs/deployment/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1241,7 +1241,7 @@ only.
| Command | Alias | Description |
|---------|-------|-------------|
| `os generate <type> <name>` | `os g` | Generate metadata files |
| `os create <type> [name]` | | Scaffold a standalone plugin or example project |
| `os create <type> [name]` | | Scaffold a standalone **kernel code** plugin project |

#### `os generate` (alias: `os g`)

Expand Down Expand Up @@ -1314,19 +1314,31 @@ third-party extension primitive, authored as `src/skills/<name>.skill.ts` with

#### `os create`

Scaffolds a **standalone** project — a **kernel code** plugin (the `Plugin` contract,
Scaffolds a **standalone** **kernel code** plugin project (the `Plugin` contract,
built by `tsc`, publishable; *not* the metadata plugin `os init -t plugin` emits — see
[Which scaffolder?](#os-init)), or an example application — into the current directory:
[Which scaffolder?](#os-init)) into the current directory:

```bash
os create plugin analytics # Create ./plugin-analytics
os create example my-app # Create ./my-app

cd plugin-analytics
pnpm install
pnpm build
```

<Callout type="warn" title="`os create example` is retired">
Use [`os init`](#os-init) to scaffold an application. `os create example` emitted a
subset of what `os init` writes plus one README, so it was withdrawn in
[#16483](https://github.com/objectstack-ai/objectstack/issues/16483) rather than kept as
a second, weaker way to do the same thing — with **no alias and no deprecation window**.
Running it now exits non-zero and names `os init`.

```bash
os init my-app # a full application project
os init my-app -t empty # config only, no src/objects
```
</Callout>

The emitted `package.json` declares its `@objectstack/*` dependencies as
published semver ranges pinned to the version of the CLI that generated it, and
the emitted `tsconfig.json` is self-contained, so the project installs and
Expand All @@ -1335,9 +1347,8 @@ builds anywhere — a workspace around it is neither needed nor assumed.
**Options:**
- `-d, --dir <directory>` — Write the project here instead of `./<name>`
- `--in-repo` — Scaffold **inside an ObjectStack monorepo checkout** instead
(`packages/plugins/<name>` for a plugin, `examples/<name>` for an example),
with `workspace:*` dependencies and a `tsconfig.json` that extends the
repository root config. For ObjectStack platform work only: the project it
(`packages/plugins/plugin-<name>`), with `workspace:*` dependencies and a
`tsconfig.json` that extends the repository root config. For ObjectStack platform work only: the project it
writes installs nowhere else, and the command refuses the flag when the
current directory is not a pnpm workspace root.

Expand Down
Loading
Loading