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
39 changes: 39 additions & 0 deletions .changeset/cli-plugin-word-disambiguation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
"@objectstack/cli": patch
---

docs(cli): give the two `plugin` artifacts their own nouns, and rewrite "Which scaffolder?" as a two-question decision (#16484, #15531)

`plugin` names two different artifacts in this CLI, and neither the help text
nor the docs said which one a reader was about to get:

- `os init <name> -t plugin` scaffolds a **metadata package** — declarative
objects another stack loads, built by `objectstack compile`, emitted
`private: true`.
- `os create plugin <name>` scaffolds a **kernel code plugin** — TypeScript
implementing the kernel `Plugin` contract, built by `tsc`, publishable as
`@objectstack/plugin-<name>`.

Someone who wanted a "plugin skeleton" and reached for the nearer of the two got
the wrong artifact, with no failure anywhere to tell them so — the metadata
package has no `Plugin` to implement, and the kernel code plugin has no
declarative objects to compile.

**No flag and no subcommand is renamed.** `-t plugin` and `os create plugin` are
published surface and are spelled exactly as before; renaming them is a separate
decision, not this change. What moved is the NOUN each surface uses for the
artifact, so the two shapes stop sharing one word:

- `os init --help` now reads `Template: app, plugin (a metadata package), empty`,
and the `plugin` template describes itself as
`Metadata package: declarative objects another stack loads` rather than
`Reusable plugin with objects`.
- `os create`'s `plugin` template describes itself as a **kernel code** plugin.

The "Which scaffolder?" guidance in `content/docs/deployment/cli.mdx` is now a
two-question decision — *metadata or kernel code?* then *a new project, or an
addition to a directory you already have?* — landing on exactly one of the four
entry points, each with the reason to pick it: `npm create objectstack@latest`
(equivalently `npx create-objectstack`), `os init`, `os init <name> -t plugin`,
and `os create plugin <name>`. `os create example` is deliberately absent: it was
retired in #16483.
52 changes: 32 additions & 20 deletions content/docs/deployment/cli.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ npm create objectstack@latest my-app
cd my-app
```

This scaffolds a working project with `objectstack.config.ts`, a sample object, and all dependencies installed — plus the AI skills bundle and an `AGENTS.md` for coding agents. (`os init` is the CLI's own scaffolder for metadata-plugin skeletons and bare configs — see [below](#os-init).)
This scaffolds a working project with `objectstack.config.ts`, a sample object, and all dependencies installed — plus the AI skills bundle and an `AGENTS.md` for coding agents. (`os init` is the CLI's own scaffolder for metadata-package skeletons and bare configs — see [below](#os-init).)

### Add more metadata

Expand Down Expand Up @@ -80,20 +80,32 @@ predicate/schema/binding mistakes that fail silently at runtime), and `os dev --

Scaffolds a new ObjectStack project with configuration, TypeScript setup, and initial metadata files.

> **Which scaffolder?** For a new app, prefer **`npm create objectstack@latest`** — it
> also derives your namespace, pins the framework packages to the current release, and
> installs the AI skills bundle + `AGENTS.md`. Reach for `os init` when you want a
> **bare config** in an existing directory — or a **metadata plugin**, which is only one
> of the two different artifacts the word `plugin` names in this CLI.

**`plugin` names two artifacts. Route by the artifact you want, not by the word.**

| You want | Command | What it emits | Built by | Publishable? | Read next |
|:---------|:--------|:--------------|:---------|:-------------|:----------|
| A **metadata plugin** — declarative objects another stack loads, no kernel code | `os init <name> -t plugin` | `objectstack.config.ts` whose manifest declares `type: 'plugin'`, plus `src/objects/*.object.ts` | `objectstack compile` (its `build` script) | **No** — the emitted `package.json` is `private: true` | [`os init`](#os-init) below, and [Object Metadata](/docs/data-modeling/objects) for the objects it holds |
| A **kernel code plugin** — TypeScript implementing the kernel `Plugin` contract | `os create plugin <name>` | `src/index.ts` exporting a `Plugin` with `init` / `destroy` | `tsc` (its `build` script) | **Yes** — a publishable `@objectstack/plugin-<name>` package | [`os create`](#os-create) below, then [Plugin Anatomy](/docs/plugins/anatomy) and [Plugin Development](/docs/plugins/development) |

Every page under [Plugins & Packages](/docs/plugins) teaches the **kernel code** plugin, so
> **Which scaffolder? Two questions.**
>
> **1. Metadata, or kernel code?** A **metadata package** is declarative — objects
> another stack loads, built by `objectstack compile`. A **kernel code plugin** is
> TypeScript implementing the kernel `Plugin` contract, built by `tsc`.
>
> **2. A whole new project, or an addition to a directory you already have?**

| What you are building | Where it goes | Entry point | Why this one |
|:----------------------|:--------------|:------------|:-------------|
| An **application** — metadata you run | a brand-new project | **`npm create objectstack@latest <name>`** — equivalently `npx create-objectstack <name>` | Derives your namespace, pins the framework packages to the current release, and installs the AI skills bundle + `AGENTS.md`. Prefer it for anything green-field |
| An **application** — metadata you run | a directory you **already have** | `os init` — or `os init -t empty` for a bare config | Writes config, TypeScript setup and starter metadata in place. Derives no namespace and installs no skills bundle |
| A **metadata package** — declarative objects another stack loads, no kernel code | its own project | `os init <name> -t plugin` | The only scaffolder that emits a manifest declaring `type: 'plugin'` |
| A **kernel code plugin** — TypeScript implementing the kernel `Plugin` contract | its own project, or `--in-repo` inside an ObjectStack monorepo checkout | `os create plugin <name>` | The only scaffolder that emits a `Plugin` for you to implement |

**The CLI spells two different artifacts `plugin`; this page does not.** The flag stays
`-t plugin` and the subcommand stays `os create plugin` — what differs is the noun. A
**metadata package** is what `os init -t plugin` writes; a **kernel code plugin** is what
`os create plugin` writes. Route by the artifact you want, not by the word.

| The word `plugin` in | Names | What it emits | Built by | Publishable? | Read next |
|:---------------------|:------|:--------------|:---------|:-------------|:----------|
| `os init <name> -t plugin` | A **metadata package** — declarative objects another stack loads, no kernel code | `objectstack.config.ts` whose manifest declares `type: 'plugin'`, plus `src/objects/*.object.ts` | `objectstack compile` (its `build` script) | **No** — the emitted `package.json` is `private: true` | [`os init`](#os-init) below, and [Object Metadata](/docs/data-modeling/objects) for the objects it holds |
| `os create plugin <name>` | A **kernel code plugin** — TypeScript implementing the kernel `Plugin` contract | `src/index.ts` exporting a `Plugin` with `init` / `destroy` | `tsc` (its `build` script) | **Yes** — a publishable `@objectstack/plugin-<name>` package | [`os create`](#os-create) below, then [Plugin Anatomy](/docs/plugins/anatomy) and [Plugin Development](/docs/plugins/development) |

Every page under [Plugins & Packages](/docs/plugins) teaches the **kernel code plugin**, so
`os create plugin` is the scaffolder those pages mean — `os init -t plugin` will not give
you a `Plugin` to implement, and `os create plugin` will not give you declarative objects
to compile.
Expand All @@ -102,7 +114,7 @@ to compile.
**Why the two scaffolders are deliberately separate.** Merging the `os init` and
`os create` command families was measured and ruled against in
[#15531](https://github.com/objectstack-ai/objectstack/issues/15531): the two commands
emit two different artifacts, so collapsing a metadata plugin and a kernel code plugin
emit two different artifacts, so collapsing a metadata package and a kernel code plugin
under one command word would make this collision **structural** instead of merely
documented — teaching the wrong artifact to everyone, human or agent, who generates a
plugin from the CLI. The collision, and the misdirection this table replaces, are recorded
Expand All @@ -111,13 +123,13 @@ in [#15817](https://github.com/objectstack-ai/objectstack/issues/15817).

```bash
os init my-app # Create with default "app" template
os init my-plugin -t plugin # Create a metadata plugin project
os init my-package -t plugin # Create a metadata package project
os init blank -t empty # Minimal config only
os init my-app --no-install # Skip dependency installation
```

**Options:**
- `-t, --template <template>` — Template: `app` (default), `plugin`, `empty`
- `-t, --template <template>` — Template: `app` (default), `plugin` (a **metadata package**), `empty`
- `--no-install` — Skip automatic dependency installation
- `-p, --package-manager <npm|pnpm|yarn|bun>` — Package manager to use (auto-detected from the environment)

Expand All @@ -126,7 +138,7 @@ os init my-app --no-install # Skip dependency installation
| Template | What it creates |
|----------|-----------------|
| `app` | Full application with objects, barrel imports |
| `plugin` | **Metadata** plugin: declarative objects, built by `objectstack compile`, `private` — *not* the kernel code plugin `os create plugin` emits |
| `plugin` | **Metadata package**: declarative objects, built by `objectstack compile`, `private` — *not* the kernel code plugin `os create plugin` emits |
| `empty` | Minimal project with just `objectstack.config.ts` |

#### `os dev`
Expand Down Expand Up @@ -1315,7 +1327,7 @@ third-party extension primitive, authored as `src/skills/<name>.skill.ts` with
#### `os create`

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

```bash
Expand Down
5 changes: 4 additions & 1 deletion content/docs/getting-started/your-first-project.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ so explicitly.

<Callout type="tip">
`os init` (from `@objectstack/cli`) is an alternative scaffolder with `app` /
`plugin` / `empty` templates — see the [CLI reference](/docs/deployment/cli#os-init).
`plugin` / `empty` templates. Its `plugin` template writes a **metadata package** —
declarative objects another stack loads — and **not** the kernel code plugin
`os create plugin` writes; [Which scaffolder?](/docs/deployment/cli#os-init) routes
between all four entry points by the artifact you want rather than by the word.
`npm create objectstack` is the recommended entry point for new standalone projects.
</Callout>

Expand Down
14 changes: 12 additions & 2 deletions content/docs/plugins/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,20 @@ Plugins follow a strict three-phase lifecycle (`init()` → `start()` → `destr

## Creating a Plugin

The fastest way to create a plugin is with the CLI scaffolding:
<Callout type="info">
**Which artifact this page means.** Every plugin taught here is a **kernel code
plugin** — TypeScript implementing the `Plugin` contract, built by `tsc`, published as
`@objectstack/plugin-<name>`. That is what `os create plugin` scaffolds. If what you
want instead is a **metadata package** — declarative objects another stack loads, with
no kernel code — the scaffolder is `os init <name> -t plugin`, and
[Which scaffolder?](/docs/deployment/cli#os-init) routes between the two by artifact
rather than by the word `plugin`, which the CLI spells the same for both.
</Callout>

The fastest way to create a kernel code plugin is with the CLI scaffolding:

```bash
# Create a new plugin project in the current directory
# Create a new kernel code plugin project in the current directory
os create plugin my-feature

# This creates:
Expand Down
6 changes: 6 additions & 0 deletions content/docs/protocol/kernel/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,12 @@ All system configuration lives in Git:
## Developer Experience

### Plugin Development

`os create plugin` scaffolds a **kernel code plugin** — TypeScript implementing the
`Plugin` contract this page describes. It is not the **metadata package** that
`os init -t plugin` writes, which holds declarative objects and no kernel code;
[Which scaffolder?](/docs/deployment/cli#os-init) routes between the two by artifact.

```bash
# Scaffold new plugin (created as ./plugin-<name>/ in the current directory)
os create plugin slack-integration
Expand Down
6 changes: 4 additions & 2 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ os compile
| Command | Description |
|---------|-------------|
| `os generate <type> <name>` | Generate metadata files (alias: `os g`) |
| `os create <type> [name]` | Create a new package/plugin/example from template |
| `os create <type> [name]` | Scaffold a standalone **kernel code** plugin project (the `Plugin` contract, built by `tsc`) from a built-in template |

Available generate types: `object`, `view`, `action`, `flow`, `dashboard`, `app`, `skill`

Expand Down Expand Up @@ -177,7 +177,9 @@ Common variables: `OS_DATABASE_URL`, `OS_DATABASE_DRIVER`,

### `os init`

- `-t, --template <template>` — Template: `app` (default), `plugin`, `empty`
- `-t, --template <template>` — Template: `app` (default), `plugin` (a **metadata package** —
declarative objects another stack loads, *not* the kernel code plugin `os create plugin`
emits), `empty`
- `--no-install` — Skip dependency installation

### `os compile`
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ const PLUGIN_IN_REPO_DIR = 'packages/plugins';

export const templates: Record<string, CreateTemplate> = {
plugin: defineTemplate({
description: 'Create a new ObjectStack plugin',
description: 'Create a new kernel code plugin (TypeScript implementing the kernel Plugin contract)',
inRepoDir: PLUGIN_IN_REPO_DIR,
dirName: (name: string) => `plugin-${name}`,
filesFor: (placement: ScaffoldPlacement) => {
Expand Down
15 changes: 13 additions & 2 deletions packages/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,7 @@ export default ${toCamelCase(namespace)}Item;
},

plugin: {
description: 'Reusable plugin with objects',
description: 'Metadata package: declarative objects another stack loads',
get dependencies() {
return {
'@objectstack/spec': pkgVersion(),
Expand Down Expand Up @@ -882,7 +882,18 @@ export default class Init extends Command {
};

static override flags = {
template: Flags.string({ char: 't', description: 'Template: app, plugin, empty', default: 'app' }),
template: Flags.string({
char: 't',
// The word `plugin` names two different artifacts in this CLI and only one
// of them is a template here: `-t plugin` writes a METADATA PACKAGE
// (declarative objects, compiled), while `os create plugin` writes a
// KERNEL CODE plugin (TypeScript implementing `Plugin`). The flag
// spellings are published surface and unchanged (#16484); the nouns are
// what tell the two apart. See the chooser in
// content/docs/deployment/cli.mdx under `os init`.
description: 'Template: app, plugin (a metadata package), empty',
default: 'app',
}),
install: Flags.boolean({ description: 'Install dependencies', default: true, allowNo: true }),
'package-manager': Flags.string({
char: 'p',
Expand Down
Loading